@solidxai/core 0.1.11-beta.1 → 0.1.11-beta.12

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 (221) 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 +46 -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/controllers/service.controller.d.ts +1 -0
  10. package/dist/controllers/service.controller.d.ts.map +1 -1
  11. package/dist/controllers/service.controller.js +2 -2
  12. package/dist/controllers/service.controller.js.map +1 -1
  13. package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
  14. package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
  15. package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
  16. package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
  17. package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
  18. package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
  19. package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
  20. package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
  21. package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
  22. package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
  23. package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
  24. package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
  25. package/dist/helpers/command.service.d.ts +2 -0
  26. package/dist/helpers/command.service.d.ts.map +1 -1
  27. package/dist/helpers/command.service.js +6 -2
  28. package/dist/helpers/command.service.js.map +1 -1
  29. package/dist/helpers/environment.helper.d.ts +1 -0
  30. package/dist/helpers/environment.helper.d.ts.map +1 -1
  31. package/dist/helpers/environment.helper.js +4 -0
  32. package/dist/helpers/environment.helper.js.map +1 -1
  33. package/dist/helpers/field-crud-managers/IntFieldCrudManager.d.ts.map +1 -1
  34. package/dist/helpers/field-crud-managers/IntFieldCrudManager.js +4 -1
  35. package/dist/helpers/field-crud-managers/IntFieldCrudManager.js.map +1 -1
  36. package/dist/helpers/model-metadata-helper.service.d.ts +2 -1
  37. package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
  38. package/dist/helpers/model-metadata-helper.service.js +9 -5
  39. package/dist/helpers/model-metadata-helper.service.js.map +1 -1
  40. package/dist/helpers/module-metadata-helper.service.js +1 -1
  41. package/dist/helpers/module-metadata-helper.service.js.map +1 -1
  42. package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
  43. package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
  44. package/dist/helpers/nodemon-heartbeat.js +67 -0
  45. package/dist/helpers/nodemon-heartbeat.js.map +1 -0
  46. package/dist/helpers/solid-registry.d.ts +2 -1
  47. package/dist/helpers/solid-registry.d.ts.map +1 -1
  48. package/dist/helpers/solid-registry.js.map +1 -1
  49. package/dist/helpers/typeorm-db-helper.d.ts +1 -0
  50. package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
  51. package/dist/helpers/typeorm-db-helper.js +12 -0
  52. package/dist/helpers/typeorm-db-helper.js.map +1 -1
  53. package/dist/index.d.ts +3 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3 -2
  56. package/dist/index.js.map +1 -1
  57. package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
  58. package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
  59. package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
  60. package/dist/interfaces.d.ts +1 -0
  61. package/dist/interfaces.d.ts.map +1 -1
  62. package/dist/interfaces.js.map +1 -1
  63. package/dist/repository/scheduled-job.repository.d.ts +2 -2
  64. package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
  65. package/dist/repository/scheduled-job.repository.js +6 -3
  66. package/dist/repository/scheduled-job.repository.js.map +1 -1
  67. package/dist/repository/security-rule.repository.d.ts +2 -2
  68. package/dist/repository/security-rule.repository.d.ts.map +1 -1
  69. package/dist/repository/security-rule.repository.js +6 -3
  70. package/dist/repository/security-rule.repository.js.map +1 -1
  71. package/dist/seeders/module-metadata-seeder.service.d.ts +11 -1
  72. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  73. package/dist/seeders/module-metadata-seeder.service.js +163 -3
  74. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  75. package/dist/seeders/seed-data/solid-core-metadata.json +128 -34
  76. package/dist/services/api-key.service.d.ts.map +1 -1
  77. package/dist/services/api-key.service.js.map +1 -1
  78. package/dist/services/authentication.service.d.ts +2 -0
  79. package/dist/services/authentication.service.d.ts.map +1 -1
  80. package/dist/services/authentication.service.js +32 -11
  81. package/dist/services/authentication.service.js.map +1 -1
  82. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
  83. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +7 -5
  84. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
  85. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
  86. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
  87. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
  88. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
  89. package/dist/services/crud-helper.service.d.ts.map +1 -1
  90. package/dist/services/crud-helper.service.js +25 -12
  91. package/dist/services/crud-helper.service.js.map +1 -1
  92. package/dist/services/crud.service.d.ts +1 -0
  93. package/dist/services/crud.service.d.ts.map +1 -1
  94. package/dist/services/crud.service.js +27 -0
  95. package/dist/services/crud.service.js.map +1 -1
  96. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
  97. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
  98. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
  99. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
  100. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
  101. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
  102. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
  103. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
  104. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
  105. package/dist/services/datasource-introspection.service.d.ts +63 -2
  106. package/dist/services/datasource-introspection.service.d.ts.map +1 -1
  107. package/dist/services/datasource-introspection.service.js +665 -188
  108. package/dist/services/datasource-introspection.service.js.map +1 -1
  109. package/dist/services/import-transaction.service.d.ts.map +1 -1
  110. package/dist/services/import-transaction.service.js +3 -0
  111. package/dist/services/import-transaction.service.js.map +1 -1
  112. package/dist/services/locale.service.d.ts +14 -0
  113. package/dist/services/locale.service.d.ts.map +1 -1
  114. package/dist/services/locale.service.js +83 -0
  115. package/dist/services/locale.service.js.map +1 -1
  116. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
  117. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  118. package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
  119. package/dist/services/menu-item-metadata.service.js +1 -0
  120. package/dist/services/menu-item-metadata.service.js.map +1 -1
  121. package/dist/services/model-metadata.service.d.ts +3 -0
  122. package/dist/services/model-metadata.service.d.ts.map +1 -1
  123. package/dist/services/model-metadata.service.js +78 -0
  124. package/dist/services/model-metadata.service.js.map +1 -1
  125. package/dist/services/module-metadata.service.d.ts.map +1 -1
  126. package/dist/services/module-metadata.service.js +28 -0
  127. package/dist/services/module-metadata.service.js.map +1 -1
  128. package/dist/services/removed-field-migration.service.d.ts +30 -0
  129. package/dist/services/removed-field-migration.service.d.ts.map +1 -0
  130. package/dist/services/removed-field-migration.service.js +319 -0
  131. package/dist/services/removed-field-migration.service.js.map +1 -0
  132. package/dist/services/role-metadata.service.d.ts +1 -0
  133. package/dist/services/role-metadata.service.d.ts.map +1 -1
  134. package/dist/services/role-metadata.service.js +23 -2
  135. package/dist/services/role-metadata.service.js.map +1 -1
  136. package/dist/services/settings/default-settings-provider.service.d.ts +244 -0
  137. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  138. package/dist/services/settings/default-settings-provider.service.js +120 -0
  139. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  140. package/dist/services/solid-ts-morph.service.d.ts +6 -0
  141. package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
  142. package/dist/services/solid-ts-morph.service.js +43 -0
  143. package/dist/services/solid-ts-morph.service.js.map +1 -1
  144. package/dist/services/view-metadata.service.d.ts.map +1 -1
  145. package/dist/services/view-metadata.service.js +4 -7
  146. package/dist/services/view-metadata.service.js.map +1 -1
  147. package/dist/solid-core.module.d.ts.map +1 -1
  148. package/dist/solid-core.module.js +4 -0
  149. package/dist/solid-core.module.js.map +1 -1
  150. package/dist/subscribers/audit.subscriber.d.ts +5 -1
  151. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  152. package/dist/subscribers/audit.subscriber.js +76 -12
  153. package/dist/subscribers/audit.subscriber.js.map +1 -1
  154. package/dist/subscribers/computed-entity-field.subscriber.d.ts +9 -1
  155. package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
  156. package/dist/subscribers/computed-entity-field.subscriber.js +45 -11
  157. package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
  158. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
  159. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
  160. package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
  161. package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
  162. package/dist/subscribers/scheduled-job.subscriber.d.ts.map +1 -1
  163. package/dist/subscribers/scheduled-job.subscriber.js +3 -2
  164. package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
  165. package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
  166. package/dist/subscribers/security-rule.subscriber.js +5 -3
  167. package/dist/subscribers/security-rule.subscriber.js.map +1 -1
  168. package/package.json +1 -1
  169. package/src/commands/migrate-removed-fields.command.ts +81 -0
  170. package/src/controllers/datasource-introspection.controller.ts +27 -1
  171. package/src/controllers/service.controller.ts +3 -2
  172. package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
  173. package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
  174. package/src/helpers/command.service.ts +10 -2
  175. package/src/helpers/environment.helper.ts +11 -0
  176. package/src/helpers/field-crud-managers/IntFieldCrudManager.ts +4 -1
  177. package/src/helpers/model-metadata-helper.service.ts +8 -3
  178. package/src/helpers/module-metadata-helper.service.ts +1 -1
  179. package/src/helpers/nodemon-heartbeat.ts +59 -0
  180. package/src/helpers/solid-registry.ts +7 -1
  181. package/src/helpers/typeorm-db-helper.ts +15 -0
  182. package/src/index.ts +3 -2
  183. package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
  184. package/src/interfaces.ts +1 -0
  185. package/src/repository/scheduled-job.repository.ts +10 -4
  186. package/src/repository/security-rule.repository.ts +10 -4
  187. package/src/seeders/module-metadata-seeder.service.ts +192 -2
  188. package/src/seeders/seed-data/solid-core-metadata.json +128 -34
  189. package/src/services/api-key.service.ts +36 -36
  190. package/src/services/authentication.service.ts +46 -12
  191. package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +11 -5
  192. package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
  193. package/src/services/crud-helper.service.ts +25 -12
  194. package/src/services/crud.service.ts +36 -11
  195. package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
  196. package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
  197. package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
  198. package/src/services/datasource-introspection.service.ts +827 -164
  199. package/src/services/import-transaction.service.ts +5 -0
  200. package/src/services/locale.service.ts +111 -3
  201. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
  202. package/src/services/menu-item-metadata.service.ts +2 -1
  203. package/src/services/model-metadata.service.ts +109 -1
  204. package/src/services/module-metadata.service.ts +36 -0
  205. package/src/services/removed-field-migration.service.ts +334 -0
  206. package/src/services/role-metadata.service.ts +26 -2
  207. package/src/services/settings/default-settings-provider.service.ts +120 -0
  208. package/src/services/solid-ts-morph.service.ts +61 -1
  209. package/src/services/view-metadata.service.ts +4 -8
  210. package/src/solid-core.module.ts +4 -0
  211. package/src/subscribers/audit.subscriber.ts +101 -18
  212. package/src/subscribers/computed-entity-field.subscriber.ts +81 -28
  213. package/src/subscribers/created-by-updated-by.subscriber.ts +3 -0
  214. package/src/subscribers/field-metadata.subscriber.ts +3 -0
  215. package/src/subscribers/scheduled-job.subscriber.ts +3 -2
  216. package/src/subscribers/security-rule.subscriber.ts +8 -3
  217. package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
  218. package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
  219. package/dist/entities/legacy-common.entity.d.ts.map +0 -1
  220. package/dist/entities/legacy-common.entity.js.map +0 -1
  221. /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,95 @@ 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);
192
- if (plan.migration.willGenerate && plan.migration.content && plan.migration.filePath) {
193
- await fs.mkdir(path.dirname(plan.migration.filePath), { recursive: true });
194
- await fs.writeFile(plan.migration.filePath, plan.migration.content, "utf8");
195
- }
196
276
  const persistedModelId = mappingDto.modelId ?? persistedModel?.id ?? null;
197
277
  return {
198
278
  data: {
199
279
  modelId: persistedModelId,
200
280
  operation: plan.operation,
201
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
+ }
293
+ const migrationWritten = await this.writeMigrationFile(plan.migration);
294
+ const datasourceFile = await this.syncTypeormDatasourceFile(module.name, datasource, plan.modelPayload.singularName);
295
+ return {
296
+ data: {
297
+ modelId: mappingDto.modelId,
298
+ operation: plan.operation,
202
299
  migration: {
203
300
  ...plan.migration,
204
- written: Boolean(plan.migration.willGenerate && plan.migration.filePath),
301
+ written: migrationWritten,
205
302
  },
303
+ datasourceFile,
206
304
  summary: plan.summary,
207
305
  },
208
306
  };
209
307
  }
308
+ async generateCode(moduleId) {
309
+ await this.loadModule(moduleId);
310
+ const output = await this.moduleMetadataService.generateCodeViaCtl(moduleId);
311
+ return {
312
+ data: {
313
+ moduleId,
314
+ output,
315
+ message: "Module code generation completed.",
316
+ },
317
+ };
318
+ }
319
+ async runMigration(moduleId, datasourceName) {
320
+ const module = await this.loadModule(moduleId);
321
+ const datasource = this.requireDatasource(datasourceName);
322
+ const cwd = path.join(process.cwd(), "..");
323
+ const outputs = [];
324
+ const migrationOutput = await this.commandService.executeCommandWithArgs({
325
+ command: "npx",
326
+ args: ["-y", "@solidxai/solidctl@latest", "migration", "-d", datasource.name, "run"],
327
+ cwd,
328
+ });
329
+ outputs.push([
330
+ `npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
331
+ migrationOutput,
332
+ ].filter(Boolean).join("\n"));
333
+ const buildOutput = await this.commandService.executeCommandWithArgs({
334
+ command: "npx",
335
+ args: ["-y", "@solidxai/solidctl@latest", "build"],
336
+ cwd,
337
+ });
338
+ outputs.push([
339
+ "npx @solidxai/solidctl@latest build",
340
+ buildOutput,
341
+ ].filter(Boolean).join("\n"));
342
+ const seedOutput = await this.commandService.executeCommandWithArgs({
343
+ command: "npx",
344
+ args: ["-y", "@solidxai/solidctl@latest", "seed", "--modules-to-seed", module.name],
345
+ cwd,
346
+ });
347
+ outputs.push([
348
+ `npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
349
+ seedOutput,
350
+ ].filter(Boolean).join("\n"));
351
+ return {
352
+ data: {
353
+ moduleId,
354
+ datasource: datasource.name,
355
+ output: outputs.join("\n\n"),
356
+ command: [
357
+ `npx @solidxai/solidctl@latest migration -d ${datasource.name} run`,
358
+ "npx @solidxai/solidctl@latest build",
359
+ `npx @solidxai/solidctl@latest seed --modules-to-seed ${module.name}`,
360
+ ].join("\n"),
361
+ message: `Migration, build, and seed completed successfully for datasource "${datasource.name}".`,
362
+ },
363
+ };
364
+ }
210
365
  async buildMappingPreview(moduleId, mappingDto) {
211
366
  const module = await this.loadModule(moduleId);
212
367
  const datasource = this.requireDatasource(mappingDto.dataSource);
@@ -219,6 +374,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
219
374
  if (!tableRecord) {
220
375
  throw new common_1.NotFoundException(`Table "${mappingDto.tableName}" was not found on datasource "${datasource.name}".`);
221
376
  }
377
+ const tableColumns = inventory.columnsByKey.get(tableKey) ?? [];
378
+ this.validateTablePrimaryKeyForMapping(tableRecord, tableColumns, mappingDto);
222
379
  const existingModel = mappingDto.mapped && mappingDto.modelId
223
380
  ? await this.modelMetadataRepository.findOne({
224
381
  where: { id: mappingDto.modelId },
@@ -233,14 +390,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
233
390
  if (mappingDto.mapped && !existingModel) {
234
391
  throw new common_1.NotFoundException(`Mapped model "${mappingDto.modelId}" was not found.`);
235
392
  }
236
- const reviewedModel = this.normalizeReviewedMetadataModel(mappingDto.reviewedModel, mappingDto);
393
+ const reviewedModel = this.normalizeReviewedMetadataModel(mappingDto.reviewedModel, mappingDto, tableColumns);
237
394
  const modelPayload = reviewedModel
238
395
  ? this.buildModelPayloadFromReviewedModel(moduleId, mappingDto, existingModel, reviewedModel)
239
396
  : this.buildModelPayload(moduleId, mappingDto, existingModel);
240
397
  const metadataJson = reviewedModel
241
398
  ? await this.buildMetadataJsonPreviewFromReviewedModel(module.name, reviewedModel)
242
399
  : await this.buildMetadataJsonPreview(module.name, mappingDto, existingModel);
243
- const migration = await this.buildMigrationPreview(module.name, datasource, mappingDto, inventory.columnsByKey.get(tableKey) ?? []);
400
+ const migration = await this.buildMigrationPreview(module.name, datasource, mappingDto, tableColumns);
244
401
  const includedColumnNames = mappingDto.columns
245
402
  .filter((column) => !column.handledBySuperclass && column.include)
246
403
  .map((column) => column.columnName);
@@ -252,6 +409,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
252
409
  mappedModelId: mappingDto.modelId ?? null,
253
410
  metadataJson,
254
411
  migration,
412
+ datasourceFile: {
413
+ filePath: path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`),
414
+ created: false,
415
+ updated: false,
416
+ entityRegistered: false,
417
+ migrationGlobRegistered: false,
418
+ },
255
419
  summary: {
256
420
  includedColumnNames,
257
421
  removedColumnNames,
@@ -297,6 +461,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
297
461
  };
298
462
  }
299
463
  buildModelPayloadFromReviewedModel(moduleId, mappingDto, existingModel, reviewedModel) {
464
+ const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(reviewedModel.fields, reviewedModel.userKeyFieldUserKey, mappingDto.userKeyField);
300
465
  const systemFieldPayloads = existingModel
301
466
  ? existingModel.fields
302
467
  .filter((field) => field.isSystem)
@@ -307,7 +472,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
307
472
  return {
308
473
  ...field,
309
474
  ...(matchedExistingField?.id ? { id: matchedExistingField.id } : {}),
310
- isUserKey: field.name === reviewedModel.userKeyFieldUserKey,
475
+ isUserKey: field.name === resolvedUserKeyFieldName,
311
476
  isSystem: field.isSystem ?? false,
312
477
  };
313
478
  });
@@ -335,6 +500,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
335
500
  async buildMetadataJsonPreview(moduleName, mappingDto, existingModel) {
336
501
  const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
337
502
  const previewFields = await this.buildMetadataPreviewFields(mappingDto, existingModel);
503
+ const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(previewFields, mappingDto.userKeyField, mappingDto.userKeyField) ?? mappingDto.userKeyField;
338
504
  return {
339
505
  filePath,
340
506
  model: {
@@ -345,7 +511,7 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
345
511
  dataSource: mappingDto.dataSource,
346
512
  dataSourceType: mappingDto.dataSourceType,
347
513
  tableName: mappingDto.tableName,
348
- userKeyFieldUserKey: mappingDto.userKeyField,
514
+ userKeyFieldUserKey: resolvedUserKeyFieldName,
349
515
  isChild: false,
350
516
  legacyTableType: mappingDto.legacyTableType,
351
517
  parentModelUserKey: "",
@@ -394,10 +560,6 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
394
560
  }
395
561
  const serialized = Array.from(finalFields.values())
396
562
  .filter((field) => !field.isSystem)
397
- .map((field) => ({
398
- ...field,
399
- isUserKey: field.name === mappingDto.userKeyField,
400
- }))
401
563
  .sort((left, right) => {
402
564
  const leftOrder = typeof left.id === "number" ? left.id : Number.MAX_SAFE_INTEGER;
403
565
  const rightOrder = typeof right.id === "number" ? right.id : Number.MAX_SAFE_INTEGER;
@@ -410,6 +572,10 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
410
572
  for (const field of serialized) {
411
573
  previewFields.push(await this.fieldMetadataService.createFieldConfig(field));
412
574
  }
575
+ const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(previewFields, mappingDto.userKeyField, mappingDto.userKeyField);
576
+ for (const field of previewFields) {
577
+ field.isUserKey = field.name === resolvedUserKeyFieldName;
578
+ }
413
579
  return previewFields;
414
580
  }
415
581
  async buildMigrationPreview(moduleName, datasource, mappingDto, tableColumns) {
@@ -455,7 +621,13 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
455
621
  }
456
622
  return rest;
457
623
  }
458
- normalizeReviewedMetadataModel(reviewedModel, mappingDto) {
624
+ sanitizeConfiguredFieldPayload(fieldConfig) {
625
+ if (!fieldConfig || typeof fieldConfig !== "object" || Array.isArray(fieldConfig)) {
626
+ return {};
627
+ }
628
+ return this.sanitizeExistingFieldPayload(fieldConfig);
629
+ }
630
+ normalizeReviewedMetadataModel(reviewedModel, mappingDto, tableColumns) {
459
631
  if (!reviewedModel) {
460
632
  return null;
461
633
  }
@@ -504,10 +676,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
504
676
  }
505
677
  fieldNames.add(loweredName);
506
678
  }
507
- const userKeyFieldUserKey = readString("userKeyFieldUserKey");
508
- if (!normalizedFields.some((field) => field.name === userKeyFieldUserKey && !field.isMarkedForRemoval)) {
679
+ const requestedUserKeyFieldUserKey = readString("userKeyFieldUserKey");
680
+ const resolvedUserKeyFieldName = this.resolveReviewedUserKeyFieldName(normalizedFields, requestedUserKeyFieldUserKey, mappingDto.userKeyField);
681
+ const inheritedGeneratedIdUserKey = this.isGeneratedIdInheritedUserKey(mappingDto.legacyTableType, requestedUserKeyFieldUserKey);
682
+ if (!resolvedUserKeyFieldName && !inheritedGeneratedIdUserKey) {
509
683
  throw new common_1.BadRequestException("Reviewed metadata JSON must keep the selected userKeyFieldUserKey in the fields array.");
510
684
  }
685
+ this.validateReviewedPrimaryKeyFields(normalizedFields, tableColumns);
686
+ const userKeyFieldUserKey = resolvedUserKeyFieldName ?? requestedUserKeyFieldUserKey;
511
687
  return {
512
688
  singularName: readString("singularName"),
513
689
  pluralName: readString("pluralName"),
@@ -526,10 +702,39 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
526
702
  internationalisation: readBoolean("internationalisation"),
527
703
  fields: normalizedFields.map((field) => ({
528
704
  ...field,
529
- isUserKey: field.name === userKeyFieldUserKey,
705
+ isUserKey: field.name === resolvedUserKeyFieldName,
530
706
  })),
531
707
  };
532
708
  }
709
+ resolveReviewedUserKeyFieldName(reviewedFields, requestedUserKeyFieldName, fallbackUserKeyFieldName) {
710
+ const activeFields = reviewedFields.filter((field) => !field.isMarkedForRemoval);
711
+ const normalizedRequested = `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase();
712
+ const normalizedFallback = `${fallbackUserKeyFieldName ?? ""}`.trim().toLowerCase();
713
+ const byExactRequested = activeFields.find((field) => field.name?.toLowerCase?.() === normalizedRequested);
714
+ if (byExactRequested) {
715
+ return byExactRequested.name;
716
+ }
717
+ const byExistingUserKeyFlag = activeFields.find((field) => field.isUserKey);
718
+ if (byExistingUserKeyFlag) {
719
+ return byExistingUserKeyFlag.name;
720
+ }
721
+ const byPrimaryKeyFallback = activeFields.find((field) => {
722
+ if (!field.isPrimaryKey) {
723
+ return false;
724
+ }
725
+ const normalizedName = `${field.name ?? ""}`.trim().toLowerCase();
726
+ return normalizedName === normalizedFallback || normalizedName === "id" || normalizedName === "legacyid";
727
+ });
728
+ if (byPrimaryKeyFallback) {
729
+ return byPrimaryKeyFallback.name;
730
+ }
731
+ const byFallbackFieldName = activeFields.find((field) => field.name?.toLowerCase?.() === normalizedFallback);
732
+ return byFallbackFieldName?.name ?? null;
733
+ }
734
+ isGeneratedIdInheritedUserKey(legacyTableType, requestedUserKeyFieldName) {
735
+ return legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID
736
+ && `${requestedUserKeyFieldName ?? ""}`.trim().toLowerCase() === "id";
737
+ }
533
738
  normalizeReviewedField(field, index) {
534
739
  if (!field || typeof field !== "object" || Array.isArray(field)) {
535
740
  throw new common_1.BadRequestException(`Reviewed metadata JSON field at index ${index} is not a valid object.`);
@@ -577,32 +782,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
577
782
  }
578
783
  buildFieldPayload(column, mappingDto, existingField) {
579
784
  const preserved = existingField ? this.sanitizeExistingFieldPayload(existingField) : {};
785
+ const configured = this.sanitizeConfiguredFieldPayload(column.fieldConfig);
786
+ const merged = {
787
+ ...preserved,
788
+ ...configured,
789
+ };
580
790
  const maxLength = column.characterMaximumLength && ["shortText", "longText"].includes(column.solidFieldType)
581
791
  ? column.characterMaximumLength
582
- : preserved.max;
792
+ : merged.max;
583
793
  return {
584
- ...preserved,
794
+ ...merged,
585
795
  ...(column.fieldId ? { id: column.fieldId } : {}),
586
796
  name: column.fieldName,
587
- displayName: preserved.displayName ?? (0, lodash_1.startCase)(column.fieldName),
588
- description: preserved.description ?? "",
797
+ displayName: column.displayName?.trim() || merged.displayName || (0, lodash_1.startCase)(column.fieldName),
798
+ description: merged.description ?? "",
589
799
  type: column.solidFieldType,
590
800
  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,
801
+ length: merged.length ?? column.characterMaximumLength ?? undefined,
802
+ defaultValue: merged.defaultValue ?? column.defaultValue ?? undefined,
803
+ required: merged.required ?? (!column.nullable || column.isPrimaryKey),
804
+ unique: merged.unique ?? false,
805
+ encrypt: merged.encrypt ?? false,
806
+ index: merged.index ?? false,
597
807
  max: maxLength ?? undefined,
598
- min: preserved.min ?? undefined,
599
- private: preserved.private ?? false,
600
- isSystem: preserved.isSystem ?? false,
808
+ min: merged.min ?? undefined,
809
+ private: merged.private ?? false,
810
+ isSystem: merged.isSystem ?? false,
601
811
  isMarkedForRemoval: false,
602
812
  columnName: column.columnName,
603
813
  isUserKey: mappingDto.userKeyField === column.fieldName,
604
- enableAuditTracking: preserved.enableAuditTracking ?? mappingDto.enableAuditTracking,
605
- isMultiSelect: preserved.isMultiSelect ?? false,
814
+ enableAuditTracking: merged.enableAuditTracking ?? mappingDto.enableAuditTracking,
815
+ isMultiSelect: merged.isMultiSelect ?? false,
606
816
  isPrimaryKey: column.isPrimaryKey,
607
817
  };
608
818
  }
@@ -696,23 +906,19 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
696
906
  const generatedIdUniqueName = this.buildMigrationObjectName("UQ", tableNameLiteral, "ss_id");
697
907
  const upColumnStatements = missingSystemColumns.map((definition) => {
698
908
  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");
909
+ return ` if (!(await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)})))\n`
910
+ + ` await queryRunner.addColumn(tableName, new TableColumn(${columnLiteral}));`;
704
911
  }).join("\n\n");
705
912
  const postUpStatements = [];
706
913
  if (missingSystemColumns.some((definition) => definition.columnName === "ss_deleted_at")) {
707
914
  postUpStatements.push([
708
915
  " {",
709
916
  " 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
- " }",
917
+ ` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(deletedAtIndexName)}))`,
918
+ ` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
919
+ name: deletedAtIndexName,
920
+ columnNames: ["ss_deleted_at"],
921
+ })}));`,
716
922
  " }",
717
923
  ].join("\n"));
718
924
  }
@@ -720,13 +926,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
720
926
  postUpStatements.push([
721
927
  " {",
722
928
  " 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
- " }",
929
+ ` if (table && !table.indices.some((index) => index.name === ${JSON.stringify(generatedIdUniqueName)}))`,
930
+ ` await queryRunner.createIndex(tableName, new TableIndex(${this.renderTableIndexLiteral({
931
+ name: generatedIdUniqueName,
932
+ columnNames: ["ss_id"],
933
+ isUnique: true,
934
+ })}));`,
730
935
  " }",
731
936
  ].join("\n"));
732
937
  }
@@ -736,9 +941,8 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
736
941
  " {",
737
942
  " const table = await queryRunner.getTable(tableName);",
738
943
  ` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(generatedIdUniqueName)});`,
739
- " if (index) {",
944
+ " if (index)",
740
945
  " await queryRunner.dropIndex(tableName, index);",
741
- " }",
742
946
  " }",
743
947
  ].join("\n"));
744
948
  }
@@ -747,20 +951,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
747
951
  " {",
748
952
  " const table = await queryRunner.getTable(tableName);",
749
953
  ` const index = table?.indices.find((entry) => entry.name === ${JSON.stringify(deletedAtIndexName)});`,
750
- " if (index) {",
954
+ " if (index)",
751
955
  " await queryRunner.dropIndex(tableName, index);",
752
- " }",
753
956
  " }",
754
957
  ].join("\n"));
755
958
  }
756
959
  downStatements.push(...missingSystemColumns
757
960
  .slice()
758
961
  .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")));
962
+ .map((definition) => ` if (await queryRunner.hasColumn(tableName, ${JSON.stringify(definition.columnName)}))\n`
963
+ + ` await queryRunner.dropColumn(tableName, ${JSON.stringify(definition.columnName)});`));
764
964
  return [
765
965
  "import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from \"typeorm\";",
766
966
  "",
@@ -783,27 +983,36 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
783
983
  }
784
984
  renderTableColumnLiteral(definition) {
785
985
  const properties = [
786
- `{\n name: ${JSON.stringify(definition.columnName)},`,
787
- ` type: ${JSON.stringify(definition.type)},`,
986
+ `name: ${JSON.stringify(definition.columnName)}`,
987
+ `type: ${JSON.stringify(definition.type)}`,
788
988
  ];
789
989
  if (definition.length) {
790
- properties.push(` length: ${JSON.stringify(definition.length)},`);
990
+ properties.push(`length: ${JSON.stringify(definition.length)}`);
791
991
  }
792
992
  if (typeof definition.isNullable === "boolean") {
793
- properties.push(` isNullable: ${definition.isNullable},`);
993
+ properties.push(`isNullable: ${definition.isNullable}`);
794
994
  }
795
995
  if (definition.defaultExpression) {
796
- properties.push(` default: ${JSON.stringify(definition.defaultExpression)},`);
996
+ properties.push(`default: ${JSON.stringify(definition.defaultExpression)}`);
797
997
  }
798
998
  if (definition.isGenerated) {
799
- properties.push(" isGenerated: true,");
800
- properties.push(" generationStrategy: \"increment\",");
999
+ properties.push("isGenerated: true");
1000
+ properties.push("generationStrategy: \"increment\"");
801
1001
  }
802
1002
  if (definition.isUnique) {
803
- properties.push(" isUnique: true,");
1003
+ properties.push("isUnique: true");
1004
+ }
1005
+ return `{ ${properties.join(", ")}, }`;
1006
+ }
1007
+ renderTableIndexLiteral(definition) {
1008
+ const properties = [
1009
+ `name: ${JSON.stringify(definition.name)}`,
1010
+ `columnNames: ${JSON.stringify(definition.columnNames)}`,
1011
+ ];
1012
+ if (definition.isUnique) {
1013
+ properties.push("isUnique: true");
804
1014
  }
805
- properties.push(" }");
806
- return properties.join("\n");
1015
+ return `{ ${properties.join(", ")}, }`;
807
1016
  }
808
1017
  buildMigrationObjectName(prefix, tableName, columnName) {
809
1018
  const hash = (0, crypto_1.createHash)("md5").update(`${tableName}:${columnName}:${prefix}`).digest("hex").slice(0, 24);
@@ -832,12 +1041,14 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
832
1041
  async buildTableInventory(datasource) {
833
1042
  const provider = this.getProvider(datasource.type);
834
1043
  const runtimeDataSource = this.resolveDataSourceByName(datasource.name);
835
- const [tables, columns, models] = await Promise.all([
1044
+ const [rawTables, rawColumns, models] = await Promise.all([
836
1045
  provider.listTables(runtimeDataSource),
837
1046
  provider.listColumns(runtimeDataSource),
838
1047
  this.modelMetadataRepository.find({ relations: { module: true } }),
839
1048
  ]);
840
- const modelMap = this.buildModelMap(models);
1049
+ const tables = rawTables.filter((table) => !this.isSolidxInternalTableName(table.tableName));
1050
+ const columns = rawColumns.filter((column) => !this.isSolidxInternalTableName(column.tableName));
1051
+ const modelLookup = this.buildModelLookup(models);
841
1052
  const columnsByKey = this.groupColumnsByTable(columns);
842
1053
  const tableKeys = new Set();
843
1054
  for (const table of tables) {
@@ -857,8 +1068,10 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
857
1068
  }
858
1069
  const suggestedLegacyTableType = this.detectLegacyTableType(datasource.type, tableColumns);
859
1070
  const suggestedIdColumn = this.getSuggestedIdColumn(datasource.type, tableColumns, suggestedLegacyTableType);
860
- const mappedModel = modelMap.get(this.toModelMapKey(datasource.name, tableName)) ?? null;
1071
+ const mappedModel = this.resolveMappedModel(modelLookup, datasource.name, tableName);
861
1072
  const physicalColumns = tableColumns.filter((column) => !this.isHandledBySuperclass(column.columnName, suggestedLegacyTableType));
1073
+ const primaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
1074
+ const hasPrimaryKey = primaryKeyColumns.length > 0;
862
1075
  return {
863
1076
  schema,
864
1077
  tableName,
@@ -875,6 +1088,12 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
875
1088
  suggestedUserKeyField: this.buildUserKeyCandidates(tableColumns)[0]?.name ?? "id",
876
1089
  columnCount: tableColumns.length,
877
1090
  physicalColumnCount: physicalColumns.length,
1091
+ hasPrimaryKey,
1092
+ primaryKeyColumnCount: primaryKeyColumns.length,
1093
+ primaryKeyColumnNames: primaryKeyColumns.map((column) => column.columnName),
1094
+ mappingBlockedReason: hasPrimaryKey
1095
+ ? null
1096
+ : this.buildMissingPrimaryKeyMessage(tableName),
878
1097
  };
879
1098
  })
880
1099
  .filter((record) => Boolean(record))
@@ -892,16 +1111,37 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
892
1111
  columnsByKey,
893
1112
  };
894
1113
  }
895
- buildModelMap(models) {
896
- const map = new Map();
1114
+ buildModelLookup(models) {
1115
+ const byDatasourceAndTable = new Map();
1116
+ const byTableName = new Map();
897
1117
  for (const model of models) {
898
1118
  if (!model.tableName) {
899
1119
  continue;
900
1120
  }
901
- map.set(this.toModelMapKey(model.dataSource, model.tableName), model);
902
- map.set(this.toModelMapKey("*", model.tableName), model);
1121
+ byDatasourceAndTable.set(this.toModelMapKey(model.dataSource, model.tableName), model);
1122
+ const normalizedTableName = model.tableName.trim().toLowerCase();
1123
+ const bucket = byTableName.get(normalizedTableName);
1124
+ if (bucket) {
1125
+ bucket.push(model);
1126
+ continue;
1127
+ }
1128
+ byTableName.set(normalizedTableName, [model]);
903
1129
  }
904
- return map;
1130
+ return {
1131
+ byDatasourceAndTable,
1132
+ byTableName,
1133
+ };
1134
+ }
1135
+ resolveMappedModel(lookup, datasourceName, tableName) {
1136
+ const exactMatch = lookup.byDatasourceAndTable.get(this.toModelMapKey(datasourceName, tableName));
1137
+ if (exactMatch) {
1138
+ return exactMatch;
1139
+ }
1140
+ const fallbackMatches = lookup.byTableName.get(tableName.trim().toLowerCase()) ?? [];
1141
+ if (fallbackMatches.length === 1) {
1142
+ return fallbackMatches[0];
1143
+ }
1144
+ return fallbackMatches[0] ?? null;
905
1145
  }
906
1146
  groupColumnsByTable(columns) {
907
1147
  const grouped = new Map();
@@ -916,6 +1156,9 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
916
1156
  }
917
1157
  return grouped;
918
1158
  }
1159
+ isSolidxInternalTableName(tableName) {
1160
+ return typeof tableName === "string" && tableName.toLowerCase().startsWith("ss_");
1161
+ }
919
1162
  detectLegacyTableType(datasourceType, columns) {
920
1163
  const normalizedDatasourceType = datasourceType.toLowerCase();
921
1164
  const hasExistingGeneratedId = columns.some((column) => column.isAutoGenerated);
@@ -983,12 +1226,16 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
983
1226
  }
984
1227
  toDetailColumnRecord(column, legacyTableType, datasourceType) {
985
1228
  const superclassFieldName = this.getSuperclassFieldName(column.columnName, legacyTableType);
1229
+ const normalizedColumnName = column.columnName.toLowerCase();
986
1230
  const fieldName = legacyTableType === legacy_table_type_enum_1.LegacyTableType.EXISTING_ID && column.isAutoGenerated
987
1231
  ? "id"
988
- : this.toFieldName(column.columnName);
1232
+ : legacyTableType === legacy_table_type_enum_1.LegacyTableType.GENERATED_ID && normalizedColumnName === "id"
1233
+ ? "legacyId"
1234
+ : this.toFieldName(column.columnName);
989
1235
  return {
990
1236
  columnName: column.columnName,
991
1237
  fieldName,
1238
+ displayName: (0, lodash_1.startCase)(fieldName),
992
1239
  dataType: column.dataType,
993
1240
  solidFieldType: this.toSolidFieldType(column),
994
1241
  ormType: this.toOrmFieldType(column, datasourceType),
@@ -999,10 +1246,49 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
999
1246
  numericScale: column.numericScale,
1000
1247
  ordinalPosition: column.ordinalPosition,
1001
1248
  isAutoGenerated: column.isAutoGenerated,
1249
+ isPrimaryKey: column.isPrimaryKey,
1002
1250
  handledBySuperclass: Boolean(superclassFieldName),
1003
1251
  superclassFieldName,
1004
1252
  };
1005
1253
  }
1254
+ validateTablePrimaryKeyForMapping(tableRecord, tableColumns, mappingDto) {
1255
+ const detectedPrimaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
1256
+ if (!detectedPrimaryKeyColumns.length) {
1257
+ throw new common_1.BadRequestException(tableRecord.mappingBlockedReason ?? this.buildMissingPrimaryKeyMessage(tableRecord.tableName));
1258
+ }
1259
+ const requestColumnsByName = new Map(mappingDto.columns.map((column) => [column.columnName.trim().toLowerCase(), column]));
1260
+ for (const primaryKeyColumn of detectedPrimaryKeyColumns) {
1261
+ const matchingRequestColumn = requestColumnsByName.get(primaryKeyColumn.columnName.trim().toLowerCase());
1262
+ if (!matchingRequestColumn) {
1263
+ throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" must remain present in the mapping request for table "${tableRecord.tableName}".`);
1264
+ }
1265
+ if (!matchingRequestColumn.include) {
1266
+ throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" cannot be excluded from the mapping for table "${tableRecord.tableName}".`);
1267
+ }
1268
+ if (!matchingRequestColumn.isPrimaryKey) {
1269
+ throw new common_1.BadRequestException(`Primary key column "${primaryKeyColumn.columnName}" must stay marked as isPrimaryKey for table "${tableRecord.tableName}".`);
1270
+ }
1271
+ }
1272
+ }
1273
+ validateReviewedPrimaryKeyFields(reviewedFields, tableColumns) {
1274
+ const detectedPrimaryKeyColumns = tableColumns.filter((column) => column.isPrimaryKey);
1275
+ if (!detectedPrimaryKeyColumns.length) {
1276
+ return;
1277
+ }
1278
+ const activeReviewedFields = reviewedFields.filter((field) => !field.isMarkedForRemoval);
1279
+ for (const primaryKeyColumn of detectedPrimaryKeyColumns) {
1280
+ const matchingReviewedField = activeReviewedFields.find((field) => `${field.columnName ?? ""}`.trim().toLowerCase() === primaryKeyColumn.columnName.trim().toLowerCase());
1281
+ if (!matchingReviewedField) {
1282
+ throw new common_1.BadRequestException(`Reviewed metadata JSON must keep datasource primary key column "${primaryKeyColumn.columnName}" in the fields array.`);
1283
+ }
1284
+ if (!matchingReviewedField.isPrimaryKey) {
1285
+ throw new common_1.BadRequestException(`Reviewed metadata JSON must keep datasource primary key column "${primaryKeyColumn.columnName}" marked with isPrimaryKey: true.`);
1286
+ }
1287
+ }
1288
+ }
1289
+ buildMissingPrimaryKeyMessage(tableName) {
1290
+ return `Table "${tableName}" cannot be mapped because the datasource table does not define any primary key. Add a real primary key to the table before continuing with SolidX mapping.`;
1291
+ }
1006
1292
  getSuperclassFieldName(columnName, legacyTableType) {
1007
1293
  return this.getLegacySuperclassFieldName(columnName, legacyTableType);
1008
1294
  }
@@ -1033,127 +1319,70 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
1033
1319
  }
1034
1320
  return "LegacyCommonEntityWithExistingId";
1035
1321
  }
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";
1322
+ getDatasourceTypeFamily(dataType) {
1323
+ const normalized = dataType.toLowerCase();
1324
+ for (const [family, aliases] of Object.entries(DATASOURCE_TYPE_FAMILY_ALIASES)) {
1325
+ if (aliases.includes(normalized)) {
1326
+ return family;
1327
+ }
1059
1328
  }
1060
- if (["json", "jsonb", "simple-json", "simplejson"].includes(normalized) || normalized.includes("json")) {
1329
+ if (normalized.includes("json")) {
1061
1330
  return "json";
1062
1331
  }
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";
1332
+ if (normalized.includes("date")) {
1333
+ return "datetime";
1074
1334
  }
1075
- return "shortText";
1335
+ return null;
1076
1336
  }
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))
1337
+ toSolidFieldType(column) {
1338
+ const family = this.getDatasourceTypeFamily(column.dataType);
1339
+ const charLength = column.characterMaximumLength ?? null;
1340
+ switch (family) {
1341
+ case "boolean":
1342
+ return "boolean";
1343
+ case "bigint":
1084
1344
  return "bigint";
1085
- if (["int", "integer", "smallint", "tinyint"].includes(normalized))
1345
+ case "int":
1086
1346
  return "int";
1087
- if (["numeric"].includes(normalized))
1088
- return "numeric";
1089
- if (["decimal", "float", "double", "real", "money", "smallmoney"].includes(normalized))
1347
+ case "numeric":
1348
+ case "decimal":
1349
+ case "float":
1090
1350
  return "decimal";
1091
- if (["date"].includes(normalized))
1351
+ case "date":
1092
1352
  return "date";
1093
- if (["time"].includes(normalized))
1353
+ case "time":
1094
1354
  return "time";
1095
- if (["datetime2"].includes(normalized))
1096
- return "datetime2";
1097
- if (["datetime", "smalldatetime", "timestamp"].includes(normalized) || normalized.includes("date"))
1355
+ case "datetime":
1356
+ case "datetimeWithTimezone":
1098
1357
  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"))
1358
+ case "json":
1359
+ case "jsonb":
1127
1360
  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";
1361
+ case "uuid":
1362
+ return "uuid";
1363
+ case "mediumText":
1364
+ case "longText":
1365
+ return "longText";
1366
+ case "shortText":
1367
+ case "unicodeShortText":
1368
+ if (charLength !== null && charLength > 255) {
1369
+ return "longText";
1370
+ }
1371
+ return "shortText";
1372
+ default:
1373
+ return "shortText";
1374
+ }
1375
+ }
1376
+ toOrmFieldType(column, datasourceType) {
1377
+ const family = this.getDatasourceTypeFamily(column.dataType);
1378
+ const normalizedDatasourceType = datasourceType.toLowerCase();
1379
+ const ormType = family
1380
+ ? ORM_TYPE_BY_DATASOURCE_FAMILY[normalizedDatasourceType]?.[family]
1381
+ : undefined;
1382
+ if (ormType) {
1383
+ return ormType;
1384
+ }
1385
+ return normalizedDatasourceType === "postgres" ? "varchar" : "varchar";
1157
1386
  }
1158
1387
  toFieldName(columnName) {
1159
1388
  return (0, lodash_1.camelCase)(columnName.replace(/^ss_/, ""));
@@ -1190,6 +1419,252 @@ let DatasourceIntrospectionService = class DatasourceIntrospectionService {
1190
1419
  toModelMapKey(datasourceName, tableName) {
1191
1420
  return `${datasourceName.toLowerCase()}::${tableName.toLowerCase()}`;
1192
1421
  }
1422
+ async syncTypeormDatasourceFile(moduleName, datasource, singularName) {
1423
+ const datasourceFilePath = path.resolve(process.cwd(), "src", `typeorm-${datasource.name}-datasource.ts`);
1424
+ const entityClassName = (0, string_helper_1.classify)(singularName);
1425
+ const entityImportPath = `./${moduleName}/entities/${(0, lodash_1.kebabCase)(singularName)}.entity`;
1426
+ const migrationGlob = `join(__dirname, './${moduleName}/migrations/${datasource.name}/*.{ts,js}')`;
1427
+ const existingContent = await fs.readFile(datasourceFilePath, "utf8").catch(() => null);
1428
+ if (!existingContent) {
1429
+ const rendered = this.renderTypeormDatasourceTemplate({
1430
+ datasource,
1431
+ entityClassName,
1432
+ entityImportPath,
1433
+ migrationGlob,
1434
+ });
1435
+ await fs.writeFile(datasourceFilePath, rendered, "utf8");
1436
+ return {
1437
+ filePath: datasourceFilePath,
1438
+ created: true,
1439
+ updated: false,
1440
+ entityRegistered: true,
1441
+ migrationGlobRegistered: true,
1442
+ };
1443
+ }
1444
+ const project = new ts_morph_1.Project({
1445
+ skipAddingFilesFromTsConfig: true,
1446
+ manipulationSettings: {
1447
+ quoteKind: ts_morph_1.QuoteKind.Single,
1448
+ indentationText: ts_morph_1.IndentationText.FourSpaces,
1449
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
1450
+ },
1451
+ });
1452
+ const sourceFile = project.createSourceFile(datasourceFilePath, existingContent, { overwrite: true });
1453
+ const entityImportDeclaration = sourceFile.getImportDeclarations().find((declaration) => declaration.getModuleSpecifierValue() === entityImportPath);
1454
+ if (entityImportDeclaration) {
1455
+ const alreadyImported = entityImportDeclaration.getNamedImports().some((namedImport) => namedImport.getName() === entityClassName);
1456
+ if (!alreadyImported) {
1457
+ entityImportDeclaration.addNamedImport(entityClassName);
1458
+ }
1459
+ }
1460
+ else {
1461
+ sourceFile.addImportDeclaration({
1462
+ moduleSpecifier: entityImportPath,
1463
+ namedImports: [entityClassName],
1464
+ });
1465
+ }
1466
+ const entitiesArray = sourceFile
1467
+ .getVariableDeclarationOrThrow("entities")
1468
+ .getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
1469
+ const entityAlreadyRegistered = this.arrayLiteralContainsValue(entitiesArray, entityClassName);
1470
+ if (!entityAlreadyRegistered) {
1471
+ entitiesArray.addElement(entityClassName);
1472
+ }
1473
+ const dataSourceOptions = this.getTypeormDatasourceOptions(sourceFile);
1474
+ const migrationsArray = this.getObjectPropertyArrayLiteral(dataSourceOptions, "migrations");
1475
+ const migrationAlreadyRegistered = this.arrayLiteralContainsValue(migrationsArray, migrationGlob);
1476
+ if (!migrationAlreadyRegistered) {
1477
+ migrationsArray.addElement(migrationGlob);
1478
+ }
1479
+ sourceFile.organizeImports();
1480
+ sourceFile.formatText();
1481
+ const nextContent = sourceFile.getFullText();
1482
+ const updated = nextContent !== existingContent;
1483
+ if (updated) {
1484
+ await fs.writeFile(datasourceFilePath, nextContent, "utf8");
1485
+ }
1486
+ return {
1487
+ filePath: datasourceFilePath,
1488
+ created: false,
1489
+ updated,
1490
+ entityRegistered: true,
1491
+ migrationGlobRegistered: true,
1492
+ };
1493
+ }
1494
+ async writeMigrationFile(migration) {
1495
+ if (!migration.willGenerate || !migration.content || !migration.filePath) {
1496
+ return false;
1497
+ }
1498
+ await fs.mkdir(path.dirname(migration.filePath), { recursive: true });
1499
+ await fs.writeFile(migration.filePath, migration.content, "utf8");
1500
+ const writtenContent = await fs.readFile(migration.filePath, "utf8").catch(() => null);
1501
+ if (writtenContent !== migration.content) {
1502
+ throw new common_1.BadRequestException(`Migration file could not be verified after writing: ${migration.filePath}`);
1503
+ }
1504
+ return true;
1505
+ }
1506
+ renderTypeormDatasourceTemplate(input) {
1507
+ const { datasource, entityClassName, entityImportPath, migrationGlob } = input;
1508
+ const envPrefix = datasource.envPrefix?.toUpperCase?.() || "DEFAULT";
1509
+ const portFallback = datasource.type === "postgres" ? 5432 : datasource.type === "mysql" ? 3306 : 1433;
1510
+ const project = new ts_morph_1.Project({
1511
+ skipAddingFilesFromTsConfig: true,
1512
+ manipulationSettings: {
1513
+ quoteKind: ts_morph_1.QuoteKind.Single,
1514
+ indentationText: ts_morph_1.IndentationText.FourSpaces,
1515
+ insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
1516
+ },
1517
+ });
1518
+ const sourceFile = project.createSourceFile("typeorm-template.ts", "", { overwrite: true });
1519
+ sourceFile.addImportDeclaration({ moduleSpecifier: "reflect-metadata" });
1520
+ sourceFile.addImportDeclaration({ moduleSpecifier: "path", namedImports: ["join"] });
1521
+ sourceFile.addImportDeclaration({
1522
+ moduleSpecifier: "dotenv",
1523
+ namedImports: [{ name: "config", alias: "dotenvConfig" }],
1524
+ });
1525
+ sourceFile.addStatements("dotenvConfig({ path: join(__dirname, '../.env') });");
1526
+ sourceFile.addStatements("");
1527
+ sourceFile.addImportDeclaration({ moduleSpecifier: "typeorm", namedImports: ["DataSource"] });
1528
+ sourceFile.addImportDeclaration({
1529
+ moduleSpecifier: "@solidxai/core",
1530
+ namespaceImport: "SolidCoreModuleExports",
1531
+ });
1532
+ sourceFile.addImportDeclaration({
1533
+ moduleSpecifier: "@solidxai/core",
1534
+ namedImports: ["getDynamicModuleNames", "parseBooleanEnv"],
1535
+ });
1536
+ sourceFile.addImportDeclaration({
1537
+ moduleSpecifier: "typeorm",
1538
+ namedImports: ["getMetadataArgsStorage"],
1539
+ });
1540
+ sourceFile.addImportDeclaration({
1541
+ moduleSpecifier: "typeorm-naming-strategies",
1542
+ namedImports: ["SnakeNamingStrategy"],
1543
+ });
1544
+ sourceFile.addImportDeclaration({
1545
+ moduleSpecifier: entityImportPath,
1546
+ namedImports: [entityClassName],
1547
+ });
1548
+ sourceFile.addFunction({
1549
+ name: "getEntitiesFromExports",
1550
+ parameters: [{ name: "exports", type: "Record<string, any>" }],
1551
+ statements: (writer) => {
1552
+ writer.writeLine("const metadataStorage = getMetadataArgsStorage();");
1553
+ writer.writeLine("return Object.values(exports).filter((item) =>");
1554
+ writer.indent(() => {
1555
+ writer.writeLine("metadataStorage.tables.some((table) => table.target === item),");
1556
+ });
1557
+ writer.write(");");
1558
+ },
1559
+ });
1560
+ sourceFile.addVariableStatement({
1561
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1562
+ declarations: [
1563
+ {
1564
+ name: "coreEntities",
1565
+ initializer: "getEntitiesFromExports(SolidCoreModuleExports)",
1566
+ },
1567
+ ],
1568
+ });
1569
+ sourceFile.addVariableStatement({
1570
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1571
+ declarations: [
1572
+ {
1573
+ name: "dynamicModules",
1574
+ initializer: "getDynamicModuleNames()",
1575
+ },
1576
+ ],
1577
+ });
1578
+ sourceFile.addVariableStatement({
1579
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1580
+ declarations: [
1581
+ {
1582
+ name: "entities",
1583
+ initializer: `[\n ...coreEntities,\n ${entityClassName},\n]`,
1584
+ },
1585
+ ],
1586
+ });
1587
+ sourceFile.addVariableStatement({
1588
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1589
+ declarations: [
1590
+ {
1591
+ name: "logging",
1592
+ initializer: `parseBooleanEnv('${envPrefix}_DATABASE_LOGGING')`,
1593
+ },
1594
+ ],
1595
+ });
1596
+ sourceFile.addStatements("// IMPORTANT: synchronize must be false when using migrations");
1597
+ sourceFile.addVariableStatement({
1598
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
1599
+ isExported: true,
1600
+ declarations: [
1601
+ {
1602
+ name: "DefaultDataSource",
1603
+ initializer: (writer) => {
1604
+ writer.write("new DataSource(");
1605
+ writer.inlineBlock(() => {
1606
+ writer.writeLine(`type: '${datasource.type}',`);
1607
+ writer.blankLine();
1608
+ writer.writeLine(`host: process.env.${envPrefix}_DATABASE_HOST,`);
1609
+ writer.writeLine(`port: +(process.env.${envPrefix}_DATABASE_PORT || ${portFallback}),`);
1610
+ writer.writeLine(`username: process.env.${envPrefix}_DATABASE_USER,`);
1611
+ writer.writeLine(`password: process.env.${envPrefix}_DATABASE_PASSWORD,`);
1612
+ writer.writeLine(`database: process.env.${envPrefix}_DATABASE_NAME,`);
1613
+ writer.blankLine();
1614
+ writer.writeLine("entities,");
1615
+ writer.writeLine("migrations: [");
1616
+ writer.indent(() => {
1617
+ writer.writeLine(`${migrationGlob},`);
1618
+ });
1619
+ writer.writeLine("],");
1620
+ writer.writeLine("synchronize: false,");
1621
+ writer.writeLine("logging,");
1622
+ writer.writeLine("namingStrategy: new SnakeNamingStrategy(),");
1623
+ if (datasource.type === "mssql") {
1624
+ writer.writeLine("options: {");
1625
+ writer.indent(() => {
1626
+ writer.writeLine("encrypt: false,");
1627
+ writer.writeLine("trustServerCertificate: true,");
1628
+ });
1629
+ writer.writeLine("},");
1630
+ }
1631
+ });
1632
+ writer.write(")");
1633
+ },
1634
+ },
1635
+ ],
1636
+ });
1637
+ sourceFile.organizeImports();
1638
+ sourceFile.formatText();
1639
+ return sourceFile.getFullText();
1640
+ }
1641
+ arrayLiteralContainsValue(arrayLiteral, rawValue) {
1642
+ const normalizedTarget = rawValue.replace(/\s+/g, "");
1643
+ return arrayLiteral.getElements().some((element) => element.getText().replace(/\s+/g, "") === normalizedTarget);
1644
+ }
1645
+ getTypeormDatasourceOptions(sourceFile) {
1646
+ const defaultDataSourceDeclaration = sourceFile.getVariableDeclarationOrThrow("DefaultDataSource");
1647
+ const initializer = defaultDataSourceDeclaration.getInitializer();
1648
+ if (!initializer || !ts_morph_1.Node.isNewExpression(initializer)) {
1649
+ throw new common_1.BadRequestException("Unable to locate DefaultDataSource initializer in datasource file.");
1650
+ }
1651
+ const options = initializer.getArguments()[0];
1652
+ if (!options || !ts_morph_1.Node.isObjectLiteralExpression(options)) {
1653
+ throw new common_1.BadRequestException("Unable to locate DefaultDataSource options object in datasource file.");
1654
+ }
1655
+ return options;
1656
+ }
1657
+ getObjectPropertyArrayLiteral(objectLiteral, propertyName) {
1658
+ const property = objectLiteral.getProperty(propertyName);
1659
+ if (!property || !ts_morph_1.Node.isPropertyAssignment(property)) {
1660
+ throw new common_1.BadRequestException(`Unable to locate "${propertyName}" property in datasource file.`);
1661
+ }
1662
+ const initializer = property.getInitializer();
1663
+ if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer)) {
1664
+ throw new common_1.BadRequestException(`Unable to locate "${propertyName}" array in datasource file.`);
1665
+ }
1666
+ return initializer;
1667
+ }
1193
1668
  buildSynchronizeBlockedMessage(datasource) {
1194
1669
  return `Datasource "${datasource.displayName}" has synchronize enabled. Set ${datasource.envPrefix}_DATABASE_SYNCHRONIZE=false before starting legacy table introspection.`;
1195
1670
  }
@@ -1271,6 +1746,8 @@ exports.DatasourceIntrospectionService = DatasourceIntrospectionService = __deco
1271
1746
  model_metadata_helper_service_1.ModelMetadataHelperService,
1272
1747
  field_metadata_service_1.FieldMetadataService,
1273
1748
  model_metadata_service_1.ModelMetadataService,
1749
+ module_metadata_service_1.ModuleMetadataService,
1750
+ command_service_1.CommandService,
1274
1751
  mssql_datasource_introspection_provider_service_1.MssqlDatasourceIntrospectionProviderService,
1275
1752
  mysql_datasource_introspection_provider_service_1.MysqlDatasourceIntrospectionProviderService,
1276
1753
  postgres_datasource_introspection_provider_service_1.PostgresDatasourceIntrospectionProviderService])