@solidxai/core 0.1.10 → 0.1.11-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/dist/controllers/datasource-introspection.controller.d.ts +234 -0
  2. package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -0
  3. package/dist/controllers/datasource-introspection.controller.js +98 -0
  4. package/dist/controllers/datasource-introspection.controller.js.map +1 -0
  5. package/dist/controllers/datasource-management.controller.d.ts +18 -0
  6. package/dist/controllers/datasource-management.controller.d.ts.map +1 -0
  7. package/dist/controllers/datasource-management.controller.js +55 -0
  8. package/dist/controllers/datasource-management.controller.js.map +1 -0
  9. package/dist/dtos/create-datasource-management.dto.d.ts +25 -0
  10. package/dist/dtos/create-datasource-management.dto.d.ts.map +1 -0
  11. package/dist/dtos/create-datasource-management.dto.js +169 -0
  12. package/dist/dtos/create-datasource-management.dto.js.map +1 -0
  13. package/dist/dtos/datasource-introspection-mapping.dto.d.ts +45 -0
  14. package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -0
  15. package/dist/dtos/datasource-introspection-mapping.dto.js +233 -0
  16. package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -0
  17. package/dist/index.d.ts +5 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/interfaces/datasource-introspection-provider.interface.d.ts +26 -0
  22. package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -0
  23. package/dist/interfaces/datasource-introspection-provider.interface.js +3 -0
  24. package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -0
  25. package/dist/repository/scheduled-job.repository.d.ts +3 -0
  26. package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
  27. package/dist/repository/scheduled-job.repository.js +60 -0
  28. package/dist/repository/scheduled-job.repository.js.map +1 -1
  29. package/dist/seeders/module-metadata-seeder.service.d.ts +32 -1
  30. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  31. package/dist/seeders/module-metadata-seeder.service.js +1011 -230
  32. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  33. package/dist/seeders/seed-data/solid-core-metadata.json +21 -0
  34. package/dist/services/action-metadata.service.d.ts +7 -0
  35. package/dist/services/action-metadata.service.d.ts.map +1 -1
  36. package/dist/services/action-metadata.service.js +76 -3
  37. package/dist/services/action-metadata.service.js.map +1 -1
  38. package/dist/services/authentication.service.js +1 -1
  39. package/dist/services/authentication.service.js.map +1 -1
  40. package/dist/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.d.ts.map +1 -1
  41. package/dist/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.js +2 -2
  42. package/dist/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.js.map +1 -1
  43. package/dist/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.d.ts.map +1 -1
  44. package/dist/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.js +2 -2
  45. package/dist/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.js.map +1 -1
  46. package/dist/services/dashboard-providers/mq-dashboard-provider-utils.d.ts +3 -0
  47. package/dist/services/dashboard-providers/mq-dashboard-provider-utils.d.ts.map +1 -1
  48. package/dist/services/dashboard-providers/mq-dashboard-provider-utils.js +71 -0
  49. package/dist/services/dashboard-providers/mq-dashboard-provider-utils.js.map +1 -1
  50. package/dist/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.d.ts.map +1 -1
  51. package/dist/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.js +3 -3
  52. package/dist/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.js.map +1 -1
  53. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts +11 -0
  54. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -0
  55. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +181 -0
  56. package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -0
  57. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts +11 -0
  58. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -0
  59. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +90 -0
  60. package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -0
  61. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts +11 -0
  62. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -0
  63. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +156 -0
  64. package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -0
  65. package/dist/services/datasource-introspection.service.d.ts +288 -0
  66. package/dist/services/datasource-introspection.service.d.ts.map +1 -0
  67. package/dist/services/datasource-introspection.service.js +1278 -0
  68. package/dist/services/datasource-introspection.service.js.map +1 -0
  69. package/dist/services/datasource-management.service.d.ts +60 -0
  70. package/dist/services/datasource-management.service.d.ts.map +1 -0
  71. package/dist/services/datasource-management.service.js +461 -0
  72. package/dist/services/datasource-management.service.js.map +1 -0
  73. package/dist/services/field-metadata.service.d.ts.map +1 -1
  74. package/dist/services/field-metadata.service.js +19 -0
  75. package/dist/services/field-metadata.service.js.map +1 -1
  76. package/dist/services/model-metadata.service.d.ts +1 -3
  77. package/dist/services/model-metadata.service.d.ts.map +1 -1
  78. package/dist/services/model-metadata.service.js +47 -15
  79. package/dist/services/model-metadata.service.js.map +1 -1
  80. package/dist/services/role-metadata.service.d.ts +5 -0
  81. package/dist/services/role-metadata.service.d.ts.map +1 -1
  82. package/dist/services/role-metadata.service.js +85 -31
  83. package/dist/services/role-metadata.service.js.map +1 -1
  84. package/dist/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.d.ts.map +1 -1
  85. package/dist/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.js +2 -3
  86. package/dist/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.js.map +1 -1
  87. package/dist/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.d.ts.map +1 -1
  88. package/dist/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.js +2 -3
  89. package/dist/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.js.map +1 -1
  90. package/dist/services/setting.service.d.ts +1 -0
  91. package/dist/services/setting.service.d.ts.map +1 -1
  92. package/dist/services/setting.service.js +32 -8
  93. package/dist/services/setting.service.js.map +1 -1
  94. package/dist/services/solid-ts-morph.service.d.ts +1 -1
  95. package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
  96. package/dist/services/solid-ts-morph.service.js.map +1 -1
  97. package/dist/services/view-metadata.service.d.ts +6 -0
  98. package/dist/services/view-metadata.service.d.ts.map +1 -1
  99. package/dist/services/view-metadata.service.js +66 -1
  100. package/dist/services/view-metadata.service.js.map +1 -1
  101. package/dist/solid-core.module.d.ts.map +1 -1
  102. package/dist/solid-core.module.js +16 -0
  103. package/dist/solid-core.module.js.map +1 -1
  104. package/dist-tests/api/authenticate.spec.js +119 -0
  105. package/dist-tests/api/authenticate.spec.js.map +1 -0
  106. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
  107. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
  108. package/dist-tests/api/ping.spec.js +21 -0
  109. package/dist-tests/api/ping.spec.js.map +1 -0
  110. package/dist-tests/helpers/auth.js +41 -0
  111. package/dist-tests/helpers/auth.js.map +1 -0
  112. package/dist-tests/helpers/env.js +11 -0
  113. package/dist-tests/helpers/env.js.map +1 -0
  114. package/docs/agent-hub-grooming.md +301 -0
  115. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
  116. package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
  117. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
  118. package/docs/datasource-introspection-ddl-analysis.md +326 -0
  119. package/docs/datasource-introspection-implementation-plan.md +306 -0
  120. package/docs/grouping-enhancements.md +89 -0
  121. package/docs/java-spring/README.md +3 -0
  122. package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
  123. package/docs/module-package-import-handoff.md +691 -0
  124. package/docs/seed-changes.md +65 -0
  125. package/docs/test-data-workflow.md +200 -0
  126. package/docs/type-declaration-import-issue.md +24 -0
  127. package/package.json +1 -1
  128. package/src/controllers/datasource-introspection.controller.ts +58 -0
  129. package/src/controllers/datasource-management.controller.ts +24 -0
  130. package/src/dtos/create-datasource-management.dto.ts +139 -0
  131. package/src/dtos/datasource-introspection-mapping.dto.ts +182 -0
  132. package/src/index.ts +5 -0
  133. package/src/interfaces/datasource-introspection-provider.interface.ts +28 -0
  134. package/src/repository/scheduled-job.repository.ts +73 -0
  135. package/src/seeders/module-metadata-seeder.service.ts +1223 -262
  136. package/src/seeders/seed-data/solid-core-metadata.json +22 -1
  137. package/src/services/action-metadata.service.ts +87 -2
  138. package/src/services/authentication.service.ts +1 -1
  139. package/src/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.ts +9 -4
  140. package/src/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.ts +9 -4
  141. package/src/services/dashboard-providers/mq-dashboard-provider-utils.ts +82 -0
  142. package/src/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.ts +10 -4
  143. package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +185 -0
  144. package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +95 -0
  145. package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +160 -0
  146. package/src/services/datasource-introspection.service.ts +1536 -0
  147. package/src/services/datasource-management.service.ts +566 -0
  148. package/src/services/field-metadata.service.ts +21 -1
  149. package/src/services/model-metadata.service.ts +50 -13
  150. package/src/services/role-metadata.service.ts +100 -47
  151. package/src/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.ts +2 -4
  152. package/src/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.ts +2 -4
  153. package/src/services/setting.service.ts +43 -12
  154. package/src/services/solid-ts-morph.service.ts +2 -2
  155. package/src/services/view-metadata.service.ts +79 -1
  156. package/src/solid-core.module.ts +16 -0
  157. package/.claude/settings.local.json +0 -15
  158. package/src/services/1.js +0 -6
@@ -118,6 +118,14 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
118
118
  this.dataSource = dataSource;
119
119
  this.modelSequenceRepo = modelSequenceRepo;
120
120
  this.logger = new common_1.Logger(ModuleMetadataSeederService_1.name);
121
+ this.seedTimingTag = 'SEED_TIMING';
122
+ this.permissionSeedBatchSize = 500;
123
+ this.viewComparableFields = ['name', 'displayName', 'type', 'context', 'layout', 'module', 'model'];
124
+ this.actionComparableFields = ['name', 'displayName', 'type', 'domain', 'context', 'customComponent', 'customIsModal', 'serverEndpoint', 'module', 'model', 'view'];
125
+ this.moduleLookupCache = new Map();
126
+ this.modelLookupCache = new Map();
127
+ this.viewLookupCache = new Map();
128
+ this.mediaStorageProviderLookupCache = new Map();
121
129
  this.enablePruning = false;
122
130
  }
123
131
  async seed(conf) {
@@ -127,105 +135,110 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
127
135
  const shouldSeedGlobalMetadata = conf?.seedGlobalMetadata !== false;
128
136
  try {
129
137
  this.enablePruning = Boolean(conf?.pruneMetadata);
138
+ this.resetLookupCaches();
130
139
  console.log(this.enablePruning ? '▶ Pruning enabled: metadata not present in JSON will be removed.' : '▶ Pruning disabled: existing metadata will be kept.');
131
- if (shouldSeedGlobalMetadata) {
132
- currentStep = 'seedGlobalMetadata';
133
- await this.seedGlobalMetadata();
134
- }
135
- else {
136
- this.logger.log(`Skipping global metadata seeding.`);
137
- }
138
- const seedDataFiles = this.seedDataFiles;
139
- this.logger.debug(`Found seed data for modules: ${seedDataFiles.map(s => s.moduleMetadata?.name)}`);
140
- currentStep = 'resolveModulesToSeed';
141
- if (conf && Array.isArray(conf.modulesToSeed)) {
142
- modulesToSeed = conf.modulesToSeed;
143
- console.log(`▶ Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
144
- this.logger.log(`Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
145
- }
146
- else {
147
- console.log(`▶ No modulesToSeed provided. Seeding ALL modules.`);
148
- this.logger.log(`No modulesToSeed provided. Seeding ALL modules.`);
149
- }
150
- const filteredSeedDataFiles = modulesToSeed ? seedDataFiles.filter((file) => modulesToSeed.includes(file.moduleMetadata?.name)) : seedDataFiles;
151
- if (filteredSeedDataFiles.length === 0) {
152
- this.logger.warn(`No modules matched the provided modulesToSeed list.`);
153
- return;
154
- }
155
- for (let i = 0; i < filteredSeedDataFiles.length; i++) {
156
- const overallMetadata = filteredSeedDataFiles[i];
157
- const moduleMetadata = overallMetadata.moduleMetadata;
158
- currentModule = moduleMetadata?.name ?? 'unknown';
159
- if (!moduleMetadata?.name) {
160
- this.logger.warn(`Skipping seed metadata file because moduleMetadata.name is missing.`);
161
- continue;
140
+ await this.timeOperation('seed-run', async () => {
141
+ if (shouldSeedGlobalMetadata) {
142
+ currentStep = 'seedGlobalMetadata';
143
+ await this.timeOperation('global-seed', () => this.seedGlobalMetadata(), { moduleName: 'global', component: 'global-metadata' });
162
144
  }
163
- console.log(`▶ Seeding Metadata for Module: ${moduleMetadata.name}`);
164
- this.logger.log(`Seeding Metadata for Module: ${moduleMetadata.name}`);
165
- currentStep = 'seedMediaStorageProviders';
166
- this.logger.log(`Seeding Media Storage Providers`);
167
- const mediaStorageCounts = await this.seedMediaStorageProviders(overallMetadata.mediaStorageProviders);
168
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Media Storage Providers', mediaStorageCounts)}`);
169
- currentStep = 'seedModuleModelFields';
170
- this.logger.log(`Seeding Module / Model / Fields`);
171
- const moduleModelFieldCounts = await this.seedModuleModelFields(moduleMetadata);
172
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Module/Model/Fields', moduleModelFieldCounts)}`);
173
- currentStep = 'seedPermissions';
174
- this.logger.log(`Seeding Permissions`);
175
- const permissionCounts = await this.seedPermissions(overallMetadata);
176
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Permissions', permissionCounts)}`);
177
- currentStep = 'seedRoles';
178
- this.logger.log(`Seeding Roles`);
179
- const roleCounts = await this.seedRoles(overallMetadata);
180
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Roles', roleCounts)}`);
181
- currentStep = 'seedUsers';
182
- this.logger.log(`Seeding Users`);
183
- const userCounts = await this.seedUsers(overallMetadata);
184
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Users', userCounts)}`);
185
- currentStep = 'seedViews';
186
- this.logger.log(`Seeding Views`);
187
- const viewCounts = await this.seedViews(overallMetadata);
188
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Views', viewCounts)}`);
189
- currentStep = 'seedActions';
190
- this.logger.log(`Seeding Actions`);
191
- const actionCounts = await this.seedActions(overallMetadata);
192
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Actions', actionCounts)}`);
193
- currentStep = 'seedMenus';
194
- this.logger.log(`Seeding Menus`);
195
- const menuCounts = await this.seedMenus(overallMetadata);
196
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Menus', menuCounts)}`);
197
- currentStep = 'seedEmailTemplates';
198
- this.logger.log(`Seeding Email Templates`);
199
- const emailTemplateCounts = await this.seedEmailTemplates(overallMetadata, moduleMetadata.name);
200
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Email Templates', emailTemplateCounts)}`);
201
- currentStep = 'seedSmsTemplates';
202
- this.logger.log(`Seeding Sms Templates`);
203
- const smsTemplateCounts = await this.seedSmsTemplates(overallMetadata, moduleMetadata.name);
204
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Sms Templates', smsTemplateCounts)}`);
205
- currentStep = 'seedSecurityRules';
206
- this.logger.log(`Seeding Security Rules`);
207
- const securityRuleCounts = await this.seedSecurityRules(overallMetadata);
208
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Security Rules', securityRuleCounts)}`);
209
- currentStep = 'seedListOfValues';
210
- this.logger.log(`Seeding List Of Values`);
211
- const lovCounts = await this.seedListOfValues(moduleMetadata, overallMetadata);
212
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'List Of Values', lovCounts)}`);
213
- currentStep = 'seedScheduledJobs';
214
- this.logger.log(`Seeding Scheduled Jobs`);
215
- const scheduledJobCounts = await this.seedScheduledJobs(moduleMetadata, overallMetadata);
216
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Scheduled Jobs', scheduledJobCounts)}`);
217
- currentStep = 'seedSavedFilters';
218
- this.logger.log(`Seeding Saved Filters`);
219
- const savedFilterCounts = await this.seedSavedFilters(moduleMetadata, overallMetadata);
220
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Saved Filters', savedFilterCounts)}`);
221
- currentStep = 'seedModelSequences';
222
- this.logger.log(`Seeding Model Sequences`);
223
- const modelSequenceCounts = await this.seedModelSequences(overallMetadata);
224
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Model Sequences', modelSequenceCounts)}`);
225
- }
226
- currentModule = 'global';
227
- currentStep = 'setupDefaultRolesWithPermissions';
228
- await this.setupDefaultRolesWithPermissions();
145
+ else {
146
+ this.logger.log(`Skipping global metadata seeding.`);
147
+ }
148
+ const seedDataFiles = this.seedDataFiles;
149
+ this.logger.debug(`Found seed data for modules: ${seedDataFiles.map(s => s.moduleMetadata?.name)}`);
150
+ currentStep = 'resolveModulesToSeed';
151
+ if (conf && Array.isArray(conf.modulesToSeed)) {
152
+ modulesToSeed = conf.modulesToSeed;
153
+ console.log(`▶ Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
154
+ this.logger.log(`Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
155
+ }
156
+ else {
157
+ console.log(`▶ No modulesToSeed provided. Seeding ALL modules.`);
158
+ this.logger.log(`No modulesToSeed provided. Seeding ALL modules.`);
159
+ }
160
+ const filteredSeedDataFiles = modulesToSeed ? seedDataFiles.filter((file) => modulesToSeed.includes(file.moduleMetadata?.name)) : seedDataFiles;
161
+ if (filteredSeedDataFiles.length === 0) {
162
+ this.logger.warn(`No modules matched the provided modulesToSeed list.`);
163
+ return;
164
+ }
165
+ for (let i = 0; i < filteredSeedDataFiles.length; i++) {
166
+ const overallMetadata = filteredSeedDataFiles[i];
167
+ const moduleMetadata = overallMetadata.moduleMetadata;
168
+ currentModule = moduleMetadata?.name ?? 'unknown';
169
+ if (!moduleMetadata?.name) {
170
+ this.logger.warn(`Skipping seed metadata file because moduleMetadata.name is missing.`);
171
+ continue;
172
+ }
173
+ console.log(`▶ Seeding Metadata for Module: ${moduleMetadata.name}`);
174
+ this.logger.log(`Seeding Metadata for Module: ${moduleMetadata.name}`);
175
+ await this.timeOperation('module-total', async () => {
176
+ currentStep = 'seedMediaStorageProviders';
177
+ this.logger.log(`Seeding Media Storage Providers`);
178
+ const mediaStorageCounts = await this.timeOperation('seed-media-storage-providers', () => this.seedMediaStorageProviders(overallMetadata.mediaStorageProviders), { moduleName: moduleMetadata.name, component: 'media-storage-providers' });
179
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Media Storage Providers', mediaStorageCounts)}`);
180
+ currentStep = 'seedModuleModelFields';
181
+ this.logger.log(`Seeding Module / Model / Fields`);
182
+ const moduleModelFieldCounts = await this.timeOperation('seed-module-model-fields', () => this.seedModuleModelFields(moduleMetadata), { moduleName: moduleMetadata.name, component: 'module-model-fields' });
183
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Module/Model/Fields', moduleModelFieldCounts)}`);
184
+ currentStep = 'seedPermissions';
185
+ this.logger.log(`Seeding Permissions`);
186
+ const permissionCounts = await this.timeOperation('seed-permissions', () => this.seedPermissions(overallMetadata), { moduleName: moduleMetadata.name, component: 'permissions' });
187
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Permissions', permissionCounts)}`);
188
+ currentStep = 'seedRoles';
189
+ this.logger.log(`Seeding Roles`);
190
+ const roleCounts = await this.timeOperation('seed-roles', () => this.seedRoles(overallMetadata), { moduleName: moduleMetadata.name, component: 'roles' });
191
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Roles', roleCounts)}`);
192
+ currentStep = 'seedUsers';
193
+ this.logger.log(`Seeding Users`);
194
+ const userCounts = await this.timeOperation('seed-users', () => this.seedUsers(overallMetadata), { moduleName: moduleMetadata.name, component: 'users' });
195
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Users', userCounts)}`);
196
+ currentStep = 'seedViews';
197
+ this.logger.log(`Seeding Views`);
198
+ const viewCounts = await this.timeOperation('seed-views', () => this.seedViews(overallMetadata), { moduleName: moduleMetadata.name, component: 'views' });
199
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Views', viewCounts)}`);
200
+ currentStep = 'seedActions';
201
+ this.logger.log(`Seeding Actions`);
202
+ const actionCounts = await this.timeOperation('seed-actions', () => this.seedActions(overallMetadata), { moduleName: moduleMetadata.name, component: 'actions' });
203
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Actions', actionCounts)}`);
204
+ currentStep = 'seedMenus';
205
+ this.logger.log(`Seeding Menus`);
206
+ const menuCounts = await this.timeOperation('seed-menus', () => this.seedMenus(overallMetadata), { moduleName: moduleMetadata.name, component: 'menus' });
207
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Menus', menuCounts)}`);
208
+ currentStep = 'seedEmailTemplates';
209
+ this.logger.log(`Seeding Email Templates`);
210
+ const emailTemplateCounts = await this.timeOperation('seed-email-templates', () => this.seedEmailTemplates(overallMetadata, moduleMetadata.name), { moduleName: moduleMetadata.name, component: 'email-templates' });
211
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Email Templates', emailTemplateCounts)}`);
212
+ currentStep = 'seedSmsTemplates';
213
+ this.logger.log(`Seeding Sms Templates`);
214
+ const smsTemplateCounts = await this.timeOperation('seed-sms-templates', () => this.seedSmsTemplates(overallMetadata, moduleMetadata.name), { moduleName: moduleMetadata.name, component: 'sms-templates' });
215
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Sms Templates', smsTemplateCounts)}`);
216
+ currentStep = 'seedSecurityRules';
217
+ this.logger.log(`Seeding Security Rules`);
218
+ const securityRuleCounts = await this.timeOperation('seed-security-rules', () => this.seedSecurityRules(overallMetadata), { moduleName: moduleMetadata.name, component: 'security-rules' });
219
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Security Rules', securityRuleCounts)}`);
220
+ currentStep = 'seedListOfValues';
221
+ this.logger.log(`Seeding List Of Values`);
222
+ const lovCounts = await this.timeOperation('seed-list-of-values', () => this.seedListOfValues(moduleMetadata, overallMetadata), { moduleName: moduleMetadata.name, component: 'list-of-values' });
223
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'List Of Values', lovCounts)}`);
224
+ currentStep = 'seedScheduledJobs';
225
+ this.logger.log(`Seeding Scheduled Jobs`);
226
+ const scheduledJobCounts = await this.timeOperation('seed-scheduled-jobs', () => this.seedScheduledJobs(moduleMetadata, overallMetadata), { moduleName: moduleMetadata.name, component: 'scheduled-jobs' });
227
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Scheduled Jobs', scheduledJobCounts)}`);
228
+ currentStep = 'seedSavedFilters';
229
+ this.logger.log(`Seeding Saved Filters`);
230
+ const savedFilterCounts = await this.timeOperation('seed-saved-filters', () => this.seedSavedFilters(moduleMetadata, overallMetadata), { moduleName: moduleMetadata.name, component: 'saved-filters' });
231
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Saved Filters', savedFilterCounts)}`);
232
+ currentStep = 'seedModelSequences';
233
+ this.logger.log(`Seeding Model Sequences`);
234
+ const modelSequenceCounts = await this.timeOperation('seed-model-sequences', () => this.seedModelSequences(overallMetadata), { moduleName: moduleMetadata.name, component: 'model-sequences' });
235
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Model Sequences', modelSequenceCounts)}`);
236
+ }, { moduleName: moduleMetadata.name, component: 'module-seed' });
237
+ }
238
+ currentModule = 'global';
239
+ currentStep = 'setupDefaultRolesWithPermissions';
240
+ await this.timeOperation('setup-default-roles-with-permissions', () => this.setupDefaultRolesWithPermissions(), { moduleName: 'global', component: 'default-roles' });
241
+ }, { moduleName: 'global', component: 'seed-run' });
229
242
  console.log(`✔ Seeding completed.`);
230
243
  }
231
244
  catch (error) {
@@ -238,117 +251,286 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
238
251
  throw error;
239
252
  }
240
253
  }
254
+ buildTimingPrefix(itemTag, options) {
255
+ const parts = [`[${this.seedTimingTag}]`, `[ITEM:${itemTag}]`];
256
+ if (options?.moduleName) {
257
+ parts.push(`[MODULE:${options.moduleName}]`);
258
+ }
259
+ if (options?.component) {
260
+ parts.push(`[COMPONENT:${options.component}]`);
261
+ }
262
+ if (options?.serviceCall) {
263
+ parts.push(`[CALL:${options.serviceCall}]`);
264
+ }
265
+ return parts.join(' ');
266
+ }
267
+ formatDuration(durationMs) {
268
+ return durationMs >= 1000
269
+ ? `${(durationMs / 1000).toFixed(2)} s (${durationMs.toFixed(2)} ms)`
270
+ : `${durationMs.toFixed(2)} ms`;
271
+ }
272
+ resetLookupCaches() {
273
+ this.moduleLookupCache.clear();
274
+ this.modelLookupCache.clear();
275
+ this.viewLookupCache.clear();
276
+ this.mediaStorageProviderLookupCache.clear();
277
+ }
278
+ async getModuleByUserKeyCached(moduleUserKey, options) {
279
+ if (!moduleUserKey) {
280
+ return null;
281
+ }
282
+ if (this.moduleLookupCache.has(moduleUserKey)) {
283
+ return this.moduleLookupCache.get(moduleUserKey) ?? null;
284
+ }
285
+ const module = await this.timeOperation('module-find-by-user-key-cache-miss', () => this.moduleMetadataService.findOneByUserKey(moduleUserKey), {
286
+ moduleName: options?.moduleName ?? moduleUserKey,
287
+ component: options?.component,
288
+ serviceCall: 'moduleMetadataService.findOneByUserKey',
289
+ details: options?.details ?? `module=${moduleUserKey}`,
290
+ });
291
+ this.moduleLookupCache.set(moduleUserKey, module ?? null);
292
+ return module ?? null;
293
+ }
294
+ async getModelByUserKeyCached(modelUserKey, options) {
295
+ if (!modelUserKey) {
296
+ return null;
297
+ }
298
+ if (this.modelLookupCache.has(modelUserKey)) {
299
+ return this.modelLookupCache.get(modelUserKey) ?? null;
300
+ }
301
+ const model = await this.timeOperation('model-find-by-user-key-cache-miss', () => this.modelMetadataService.findOneByUserKey(modelUserKey), {
302
+ moduleName: options?.moduleName,
303
+ component: options?.component,
304
+ serviceCall: 'modelMetadataService.findOneByUserKey',
305
+ details: options?.details ?? `model=${modelUserKey}`,
306
+ });
307
+ this.modelLookupCache.set(modelUserKey, model ?? null);
308
+ return model ?? null;
309
+ }
310
+ async getViewByUserKeyCached(viewUserKey, options) {
311
+ if (!viewUserKey) {
312
+ return null;
313
+ }
314
+ if (this.viewLookupCache.has(viewUserKey)) {
315
+ return this.viewLookupCache.get(viewUserKey) ?? null;
316
+ }
317
+ const view = await this.timeOperation('view-find-by-user-key-cache-miss', () => this.solidViewService.findOneByUserKey(viewUserKey), {
318
+ moduleName: options?.moduleName,
319
+ component: options?.component,
320
+ serviceCall: 'solidViewService.findOneByUserKey',
321
+ details: options?.details ?? `view=${viewUserKey}`,
322
+ });
323
+ this.viewLookupCache.set(viewUserKey, view ?? null);
324
+ return view ?? null;
325
+ }
326
+ async getMediaStorageProviderByUserKeyCached(mediaStorageProviderUserKey, options) {
327
+ if (!mediaStorageProviderUserKey) {
328
+ return null;
329
+ }
330
+ if (this.mediaStorageProviderLookupCache.has(mediaStorageProviderUserKey)) {
331
+ return this.mediaStorageProviderLookupCache.get(mediaStorageProviderUserKey) ?? null;
332
+ }
333
+ const mediaStorageProvider = await this.timeOperation('media-storage-provider-find-by-user-key-cache-miss', () => this.mediaStorageProviderMetadataService.findOneByUserKey(mediaStorageProviderUserKey), {
334
+ moduleName: options?.moduleName,
335
+ component: options?.component,
336
+ serviceCall: 'mediaStorageProviderMetadataService.findOneByUserKey',
337
+ details: options?.details ?? `provider=${mediaStorageProviderUserKey}`,
338
+ });
339
+ this.mediaStorageProviderLookupCache.set(mediaStorageProviderUserKey, mediaStorageProvider ?? null);
340
+ return mediaStorageProvider ?? null;
341
+ }
342
+ async timeOperation(itemTag, operation, options) {
343
+ const prefix = this.buildTimingPrefix(itemTag, options);
344
+ const detailSuffix = options?.details ? ` ${options.details}` : '';
345
+ const start = process.hrtime.bigint();
346
+ try {
347
+ const result = await operation();
348
+ const durationMs = Number(process.hrtime.bigint() - start) / 1_000_000;
349
+ this.logger.debug(`${prefix} done in ${this.formatDuration(durationMs)}${detailSuffix}`);
350
+ return result;
351
+ }
352
+ catch (error) {
353
+ const durationMs = Number(process.hrtime.bigint() - start) / 1_000_000;
354
+ this.logger.debug(`${prefix} failed after ${this.formatDuration(durationMs)}${detailSuffix}`);
355
+ throw error;
356
+ }
357
+ }
241
358
  async seedScheduledJobs(moduleMetadata, overallMetadata) {
242
- this.logger.debug(`[Start] Processing scheduled jobs for ${moduleMetadata.name}`);
243
359
  const scheduledJobs = this.getSeedArray(overallMetadata?.scheduledJobs);
244
- const pruned = this.enablePruning ? await this.pruneScheduledJobs(scheduledJobs, moduleMetadata.name) : 0;
360
+ const pruned = this.enablePruning ? await this.timeOperation('prune-scheduled-jobs', () => this.pruneScheduledJobs(scheduledJobs, moduleMetadata.name), {
361
+ moduleName: moduleMetadata.name,
362
+ component: 'scheduled-jobs',
363
+ serviceCall: 'pruneScheduledJobs',
364
+ }) : 0;
245
365
  if (scheduledJobs.length > 0) {
246
- await this.handleSeedScheduledJobs(scheduledJobs);
366
+ await this.timeOperation('handle-scheduled-jobs', () => this.handleSeedScheduledJobs(scheduledJobs), {
367
+ moduleName: moduleMetadata.name,
368
+ component: 'scheduled-jobs',
369
+ serviceCall: 'handleSeedScheduledJobs',
370
+ });
247
371
  }
248
- this.logger.debug(`[End] Processing scheduled jobs for ${moduleMetadata.name}`);
249
372
  return { pruned, upserted: scheduledJobs.length };
250
373
  }
251
374
  async seedSavedFilters(moduleMetadata, overallMetadata) {
252
- this.logger.debug(`[Start] Processing saved filters for ${moduleMetadata.name}`);
253
375
  const savedFilters = this.getSeedArray(overallMetadata?.savedFilters);
254
- const pruned = this.enablePruning ? await this.pruneSavedFilters(savedFilters, moduleMetadata.name) : 0;
376
+ const pruned = this.enablePruning ? await this.timeOperation('prune-saved-filters', () => this.pruneSavedFilters(savedFilters, moduleMetadata.name), {
377
+ moduleName: moduleMetadata.name,
378
+ component: 'saved-filters',
379
+ serviceCall: 'pruneSavedFilters',
380
+ }) : 0;
255
381
  if (savedFilters.length > 0) {
256
- await this.handleSeedSavedFilters(savedFilters);
382
+ await this.timeOperation('handle-saved-filters', () => this.handleSeedSavedFilters(savedFilters), {
383
+ moduleName: moduleMetadata.name,
384
+ component: 'saved-filters',
385
+ serviceCall: 'handleSeedSavedFilters',
386
+ });
257
387
  }
258
- this.logger.debug(`[End] Processing saved filters for ${moduleMetadata.name}`);
259
388
  return { pruned, upserted: savedFilters.length };
260
389
  }
261
390
  async seedListOfValues(moduleMetadata, overallMetadata) {
262
- this.logger.debug(`[Start] Processing List Of Values for ${moduleMetadata.name}`);
263
391
  const listOfValues = this.getSeedArray(overallMetadata?.listOfValues);
264
- const pruned = this.enablePruning ? await this.pruneListOfValues(listOfValues, moduleMetadata.name) : 0;
265
- await this.handleSeedListOfValues(listOfValues);
266
- this.logger.debug(`[End] Processing List Of Values for ${moduleMetadata.name}`);
392
+ const pruned = this.enablePruning ? await this.timeOperation('prune-list-of-values', () => this.pruneListOfValues(listOfValues, moduleMetadata.name), {
393
+ moduleName: moduleMetadata.name,
394
+ component: 'list-of-values',
395
+ serviceCall: 'pruneListOfValues',
396
+ }) : 0;
397
+ await this.timeOperation('handle-list-of-values', () => this.handleSeedListOfValues(listOfValues), {
398
+ moduleName: moduleMetadata.name,
399
+ component: 'list-of-values',
400
+ serviceCall: 'handleSeedListOfValues',
401
+ });
267
402
  return { pruned, upserted: listOfValues.length };
268
403
  }
269
404
  async setupDefaultRolesWithPermissions() {
270
405
  this.logger.debug(`About to add all permissions to the Admin role`);
271
- await this.roleService.addAllPermissionsToRole(create_role_metadata_dto_1.ADMIN_ROLE_NAME);
406
+ await this.timeOperation('role-add-all-permissions', () => this.roleService.addAllPermissionsToRole(create_role_metadata_dto_1.ADMIN_ROLE_NAME), {
407
+ moduleName: 'global',
408
+ component: 'default-roles',
409
+ serviceCall: 'roleService.addAllPermissionsToRole',
410
+ details: `role=${create_role_metadata_dto_1.ADMIN_ROLE_NAME}`,
411
+ });
272
412
  }
273
413
  async seedSecurityRules(overallMetadata) {
274
- this.logger.debug(`[Start] Processing security rules`);
275
414
  const securityRules = this.getSeedArray(overallMetadata?.securityRules);
276
- const pruned = this.enablePruning ? await this.pruneSecurityRules(securityRules, overallMetadata?.moduleMetadata?.name) : 0;
277
- await this.handleSeedSecurityRules(securityRules);
278
- this.logger.debug(`[End] Processing security rules`);
415
+ const pruned = this.enablePruning ? await this.timeOperation('prune-security-rules', () => this.pruneSecurityRules(securityRules, overallMetadata?.moduleMetadata?.name), {
416
+ moduleName: overallMetadata?.moduleMetadata?.name,
417
+ component: 'security-rules',
418
+ serviceCall: 'pruneSecurityRules',
419
+ }) : 0;
420
+ await this.timeOperation('handle-security-rules', () => this.handleSeedSecurityRules(securityRules), {
421
+ moduleName: overallMetadata?.moduleMetadata?.name,
422
+ component: 'security-rules',
423
+ serviceCall: 'handleSeedSecurityRules',
424
+ });
279
425
  return { pruned, upserted: securityRules.length };
280
426
  }
281
427
  async seedDefaultSettings() {
282
- this.logger.debug(`[Start] Processing settings`);
283
- await this.settingService.seedSystemAdminEditableAndAboveSettings();
284
- this.logger.debug(`[End] Processing settings`);
428
+ await this.timeOperation('seed-default-settings-call', () => this.settingService.seedSystemAdminEditableAndAboveSettings(), {
429
+ moduleName: 'global',
430
+ component: 'settings',
431
+ serviceCall: 'settingService.seedSystemAdminEditableAndAboveSettings',
432
+ });
285
433
  }
286
434
  async seedSmsTemplates(overallMetadata, moduleName) {
287
- this.logger.debug(`[Start] Processing sms templates`);
288
435
  const smsTemplates = this.getSeedArray(overallMetadata?.smsTemplates);
289
- await this.handleSeedSmsTemplates(smsTemplates, moduleName);
290
- this.logger.debug(`[End] Processing sms templates`);
436
+ await this.timeOperation('handle-sms-templates', () => this.handleSeedSmsTemplates(smsTemplates, moduleName), {
437
+ moduleName,
438
+ component: 'sms-templates',
439
+ serviceCall: 'handleSeedSmsTemplates',
440
+ });
291
441
  return { pruned: 0, upserted: smsTemplates.length };
292
442
  }
293
443
  async seedEmailTemplates(overallMetadata, moduleName) {
294
- this.logger.debug(`[Start] Processing email templates`);
295
444
  const emailTemplates = this.getSeedArray(overallMetadata?.emailTemplates);
296
- await this.handleSeedEmailTemplates(emailTemplates, moduleName);
297
- this.logger.debug(`[End] Processing email templates`);
445
+ await this.timeOperation('handle-email-templates', () => this.handleSeedEmailTemplates(emailTemplates, moduleName), {
446
+ moduleName,
447
+ component: 'email-templates',
448
+ serviceCall: 'handleSeedEmailTemplates',
449
+ });
298
450
  return { pruned: 0, upserted: emailTemplates.length };
299
451
  }
300
452
  async seedMenus(overallMetadata) {
301
- this.logger.debug(`[Start] Processing menus`);
302
453
  const menus = this.getSeedArray(overallMetadata?.menus);
303
- const pruned = this.enablePruning ? await this.pruneMenus(menus, overallMetadata?.moduleMetadata?.name) : 0;
304
- await this.handleSeedMenus(menus);
305
- this.logger.debug(`[End] Processing menus`);
454
+ const pruned = this.enablePruning ? await this.timeOperation('prune-menus', () => this.pruneMenus(menus, overallMetadata?.moduleMetadata?.name), {
455
+ moduleName: overallMetadata?.moduleMetadata?.name,
456
+ component: 'menus',
457
+ serviceCall: 'pruneMenus',
458
+ }) : 0;
459
+ await this.timeOperation('handle-menus', () => this.handleSeedMenus(menus), {
460
+ moduleName: overallMetadata?.moduleMetadata?.name,
461
+ component: 'menus',
462
+ serviceCall: 'handleSeedMenus',
463
+ });
306
464
  return { pruned, upserted: menus.length };
307
465
  }
308
466
  async seedActions(overallMetadata) {
309
- this.logger.debug(`[Start] Processing actions`);
310
467
  const actions = this.getSeedArray(overallMetadata?.actions);
311
- const pruned = this.enablePruning ? await this.pruneActions(actions, overallMetadata?.moduleMetadata?.name) : 0;
312
- await this.handleSeedActions(actions);
313
- this.logger.debug(`[End] Processing actions`);
468
+ const pruned = this.enablePruning ? await this.timeOperation('prune-actions', () => this.pruneActions(actions, overallMetadata?.moduleMetadata?.name), {
469
+ moduleName: overallMetadata?.moduleMetadata?.name,
470
+ component: 'actions',
471
+ serviceCall: 'pruneActions',
472
+ }) : 0;
473
+ await this.timeOperation('handle-actions', () => this.handleSeedActions(actions), {
474
+ moduleName: overallMetadata?.moduleMetadata?.name,
475
+ component: 'actions',
476
+ serviceCall: 'handleSeedActions',
477
+ });
314
478
  return { pruned, upserted: actions.length };
315
479
  }
316
480
  async seedViews(overallMetadata) {
317
- this.logger.debug(`[Start] Processing views`);
318
481
  const views = this.getSeedArray(overallMetadata?.views);
319
- const pruned = this.enablePruning ? await this.pruneViews(views, overallMetadata?.moduleMetadata?.name) : 0;
320
- await this.handleSeedViews(views);
321
- this.logger.debug(`[End] Processing views`);
482
+ const pruned = this.enablePruning ? await this.timeOperation('prune-views', () => this.pruneViews(views, overallMetadata?.moduleMetadata?.name), {
483
+ moduleName: overallMetadata?.moduleMetadata?.name,
484
+ component: 'views',
485
+ serviceCall: 'pruneViews',
486
+ }) : 0;
487
+ await this.timeOperation('handle-views', () => this.handleSeedViews(views), {
488
+ moduleName: overallMetadata?.moduleMetadata?.name,
489
+ component: 'views',
490
+ serviceCall: 'handleSeedViews',
491
+ });
322
492
  return { pruned, upserted: views.length };
323
493
  }
324
494
  async seedUsers(overallMetadata) {
325
- this.logger.debug(`[Start] Processing users`);
326
495
  const users = this.getSeedArray(overallMetadata?.users);
327
- await this.handleSeedUsers(users);
328
- this.logger.debug(`[End] Processing users`);
496
+ await this.timeOperation('handle-users', () => this.handleSeedUsers(users), {
497
+ moduleName: overallMetadata?.moduleMetadata?.name,
498
+ component: 'users',
499
+ serviceCall: 'handleSeedUsers',
500
+ });
329
501
  return { pruned: 0, upserted: users.length };
330
502
  }
331
503
  async seedRoles(overallMetadata) {
332
- this.logger.debug(`[Start] Processing roles`);
333
504
  const roles = this.getSeedArray(overallMetadata?.roles);
334
- await this.roleService.createRolesIfNotExists(roles
505
+ await this.timeOperation('roles-create-if-not-exists', () => this.roleService.createRolesIfNotExists(roles
335
506
  .filter((role) => role?.name)
336
- .map((role) => ({ name: role.name })));
507
+ .map((role) => ({ name: role.name }))), {
508
+ moduleName: overallMetadata?.moduleMetadata?.name,
509
+ component: 'roles',
510
+ serviceCall: 'roleService.createRolesIfNotExists',
511
+ details: `roleCount=${roles.length}`,
512
+ });
337
513
  for (const role of roles) {
338
514
  if (role.permissions) {
339
- await this.roleService.addPermissionsToRole(role.name, role.permissions
515
+ await this.timeOperation('role-add-permissions', () => this.roleService.addPermissionsToRole(role.name, role.permissions
340
516
  .map((permission) => typeof permission === 'string' ? permission : permission?.name)
341
- .filter(Boolean));
517
+ .filter(Boolean)), {
518
+ moduleName: overallMetadata?.moduleMetadata?.name,
519
+ component: 'roles',
520
+ serviceCall: 'roleService.addPermissionsToRole',
521
+ details: `role=${role.name} permissionCount=${role.permissions.length}`,
522
+ });
342
523
  }
343
524
  }
344
- this.logger.debug(`[End] Processing roles`);
345
525
  return { pruned: 0, upserted: roles.length };
346
526
  }
347
527
  async seedPermissions(overallMetadata) {
348
- this.logger.debug(`[Start] Processing permissions`);
349
528
  const permissions = overallMetadata.permissions ?? [];
350
- await this.handleSeedPermissions(permissions);
351
- this.logger.debug(`[End] Processing permissions`);
529
+ await this.timeOperation('handle-permissions', () => this.handleSeedPermissions(permissions), {
530
+ moduleName: overallMetadata?.moduleMetadata?.name,
531
+ component: 'permissions',
532
+ serviceCall: 'handleSeedPermissions',
533
+ });
352
534
  return { pruned: 0, upserted: permissions?.length ?? 0 };
353
535
  }
354
536
  get seedDataFiles() {
@@ -367,15 +549,28 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
367
549
  }
368
550
  async seedGlobalMetadata() {
369
551
  this.logger.log(`Seeding Permissions`);
370
- await this.seedControllerPermissions();
552
+ await this.timeOperation('seed-controller-permissions', () => this.seedControllerPermissions(), {
553
+ moduleName: 'global',
554
+ component: 'controller-permissions',
555
+ });
371
556
  this.logger.log(`Seeding System Fields Metadata`);
372
- await this.seedDefaultSystemFields();
557
+ await this.timeOperation('seed-default-system-fields', () => this.seedDefaultSystemFields(), {
558
+ moduleName: 'global',
559
+ component: 'system-fields',
560
+ });
373
561
  this.logger.log(`Seeding Default Settings`);
374
- await this.seedDefaultSettings();
562
+ await this.timeOperation('seed-default-settings', () => this.seedDefaultSettings(), {
563
+ moduleName: 'global',
564
+ component: 'settings',
565
+ });
375
566
  this.logger.debug(`Global metadata seeding completed`);
376
567
  }
377
568
  async seedDefaultSystemFields() {
378
- await this.systemFieldsSeederService.seed();
569
+ await this.timeOperation('system-fields-seed', () => this.systemFieldsSeederService.seed(), {
570
+ moduleName: 'global',
571
+ component: 'system-fields',
572
+ serviceCall: 'systemFieldsSeederService.seed',
573
+ });
379
574
  }
380
575
  async seedControllerPermissions() {
381
576
  const controllers = this.solidRegistry.getControllers();
@@ -388,55 +583,112 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
388
583
  const methodName = methods[mId];
389
584
  const permissionName = `${controller.name}.${methodName}`;
390
585
  permissionNames.push(permissionName);
391
- await this.createPermissionIfNotExists(permissionName);
392
586
  }
393
587
  }
394
588
  catch (error) {
395
589
  this.logger.error(error);
396
590
  }
397
591
  }
592
+ await this.timeOperation('seed-controller-permissions-bulk', () => this.seedPermissionsInBatches(permissionNames, {
593
+ moduleName: 'global',
594
+ component: 'controller-permissions',
595
+ }), {
596
+ moduleName: 'global',
597
+ component: 'controller-permissions',
598
+ serviceCall: 'seedPermissionsInBatches',
599
+ details: `permissionCount=${permissionNames.length} batchSize=${this.permissionSeedBatchSize}`,
600
+ });
398
601
  if (this.enablePruning) {
399
- await this.prunePermissions(permissionNames);
602
+ await this.timeOperation('prune-controller-permissions', () => this.prunePermissions(permissionNames), {
603
+ moduleName: 'global',
604
+ component: 'controller-permissions',
605
+ serviceCall: 'prunePermissions',
606
+ });
400
607
  }
401
608
  }
402
- async createPermissionIfNotExists(permissionName) {
403
- const existingPermission = await this.permissionRepo.findOne({
404
- where: {
405
- name: permissionName
406
- }
609
+ async handleSeedPermissions(permissions) {
610
+ const permissionNames = permissions
611
+ .map((permission) => typeof permission === 'string' ? permission : permission?.name)
612
+ .filter(Boolean);
613
+ await this.seedPermissionsInBatches(permissionNames, {
614
+ component: 'permissions',
407
615
  });
408
- if (!existingPermission) {
409
- this.logger.log(`Permission ${permissionName} does not exist, creating new.`);
410
- const newPermission = this.permissionRepo.create({
411
- name: permissionName
616
+ }
617
+ async seedPermissionsInBatches(permissionNames, options) {
618
+ const uniquePermissionNames = this.getUniquePermissionNames(permissionNames);
619
+ if (uniquePermissionNames.length === 0) {
620
+ return;
621
+ }
622
+ for (let index = 0; index < uniquePermissionNames.length; index += this.permissionSeedBatchSize) {
623
+ const batch = uniquePermissionNames.slice(index, index + this.permissionSeedBatchSize);
624
+ const batchNumber = Math.floor(index / this.permissionSeedBatchSize) + 1;
625
+ const existingPermissions = await this.timeOperation('permission-batch-find', () => this.permissionRepo.find({
626
+ where: {
627
+ name: (0, typeorm_1.In)(batch),
628
+ }
629
+ }), {
630
+ moduleName: options?.moduleName,
631
+ component: options?.component ?? 'permissions',
632
+ serviceCall: 'permissionRepo.find',
633
+ details: `batch=${batchNumber} batchSize=${batch.length}`,
634
+ });
635
+ const existingPermissionNames = new Set(existingPermissions.map((permission) => this.normalizePermissionName(permission.name)));
636
+ const missingPermissionNames = batch.filter((name) => !existingPermissionNames.has(this.normalizePermissionName(name)));
637
+ if (missingPermissionNames.length === 0) {
638
+ continue;
639
+ }
640
+ this.logger.log(`Creating ${missingPermissionNames.length} missing permissions in batch ${batchNumber}.`);
641
+ await this.timeOperation('permission-batch-save', () => this.permissionRepo.save(missingPermissionNames.map((name) => this.permissionRepo.create({ name }))), {
642
+ moduleName: options?.moduleName,
643
+ component: options?.component ?? 'permissions',
644
+ serviceCall: 'permissionRepo.save',
645
+ details: `batch=${batchNumber} createCount=${missingPermissionNames.length}`,
412
646
  });
413
- await this.permissionRepo.save(newPermission);
414
647
  }
415
648
  }
416
- async handleSeedPermissions(permissions) {
417
- for (const permission of permissions) {
418
- const permissionName = typeof permission === 'string' ? permission : permission?.name;
419
- if (permissionName) {
420
- await this.createPermissionIfNotExists(permissionName);
649
+ getUniquePermissionNames(permissionNames) {
650
+ const normalizedPermissionNames = new Set();
651
+ const uniquePermissionNames = [];
652
+ for (const permissionName of permissionNames) {
653
+ if (!permissionName) {
654
+ continue;
655
+ }
656
+ const normalizedPermissionName = this.normalizePermissionName(permissionName);
657
+ if (normalizedPermissionNames.has(normalizedPermissionName)) {
658
+ continue;
421
659
  }
660
+ normalizedPermissionNames.add(normalizedPermissionName);
661
+ uniquePermissionNames.push(permissionName.trim());
422
662
  }
663
+ return uniquePermissionNames;
664
+ }
665
+ normalizePermissionName(permissionName) {
666
+ return permissionName.trim().toLowerCase();
423
667
  }
424
668
  async seedMediaStorageProviders(mediaStorageProviders) {
425
- this.logger.debug(`[Start] Processing Media Storage Provider`);
426
669
  const providers = this.getSeedArray(mediaStorageProviders);
427
670
  for (let i = 0; i < providers.length; i++) {
428
671
  const mediaStorageProvider = providers[i];
429
- await this.mediaStorageProviderMetadataService.upsert(mediaStorageProvider);
672
+ await this.timeOperation('media-storage-provider-upsert', () => this.mediaStorageProviderMetadataService.upsert(mediaStorageProvider), {
673
+ component: 'media-storage-providers',
674
+ serviceCall: 'mediaStorageProviderMetadataService.upsert',
675
+ details: `provider=${mediaStorageProvider?.name ?? mediaStorageProvider?.userKey ?? i}`,
676
+ });
430
677
  }
431
- this.logger.debug(`[End] Processing Media Storage Provider`);
432
678
  return { pruned: 0, upserted: providers.length };
433
679
  }
434
680
  async seedModelSequences(overallMetadata) {
435
- this.logger.debug(`[Start] Processing model sequences`);
436
681
  const modelSequences = this.getSeedArray(overallMetadata?.modelSequences);
437
- const pruned = this.enablePruning ? await this.pruneModelSequences(modelSequences, overallMetadata?.moduleMetadata?.name) : 0;
438
- await this.handleSeedModelSequences(modelSequences);
439
- this.logger.debug(`[End] Processing model sequences`);
682
+ const pruned = this.enablePruning ? await this.timeOperation('prune-model-sequences', () => this.pruneModelSequences(modelSequences, overallMetadata?.moduleMetadata?.name), {
683
+ moduleName: overallMetadata?.moduleMetadata?.name,
684
+ component: 'model-sequences',
685
+ serviceCall: 'pruneModelSequences',
686
+ }) : 0;
687
+ await this.timeOperation('handle-model-sequences', () => this.handleSeedModelSequences(modelSequences), {
688
+ moduleName: overallMetadata?.moduleMetadata?.name,
689
+ component: 'model-sequences',
690
+ serviceCall: 'handleSeedModelSequences',
691
+ });
440
692
  return { pruned, upserted: modelSequences.length };
441
693
  }
442
694
  async handleSeedEmailTemplates(emailTemplates, moduleName) {
@@ -473,8 +725,18 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
473
725
  emailTemplate.body = fs.readFileSync(fullPath, 'utf-8').toString();
474
726
  }
475
727
  }
476
- await this.emailTemplateService.removeByName(emailTemplate.name);
477
- await this.emailTemplateService.create(emailTemplate);
728
+ await this.timeOperation('email-template-remove', () => this.emailTemplateService.removeByName(emailTemplate.name), {
729
+ moduleName,
730
+ component: 'email-templates',
731
+ serviceCall: 'emailTemplateService.removeByName',
732
+ details: `template=${emailTemplate.name}`,
733
+ });
734
+ await this.timeOperation('email-template-create', () => this.emailTemplateService.create(emailTemplate), {
735
+ moduleName,
736
+ component: 'email-templates',
737
+ serviceCall: 'emailTemplateService.create',
738
+ details: `template=${emailTemplate.name}`,
739
+ });
478
740
  }
479
741
  }
480
742
  async handleSeedSmsTemplates(smsTemplates, moduleName) {
@@ -511,32 +773,62 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
511
773
  smsTemplate.body = fs.readFileSync(fullPath, 'utf-8').toString();
512
774
  }
513
775
  }
514
- await this.smsTemplateService.removeByName(smsTemplate.name);
515
- await this.smsTemplateService.create(smsTemplate);
776
+ await this.timeOperation('sms-template-remove', () => this.smsTemplateService.removeByName(smsTemplate.name), {
777
+ moduleName,
778
+ component: 'sms-templates',
779
+ serviceCall: 'smsTemplateService.removeByName',
780
+ details: `template=${smsTemplate.name}`,
781
+ });
782
+ await this.timeOperation('sms-template-create', () => this.smsTemplateService.create(smsTemplate), {
783
+ moduleName,
784
+ component: 'sms-templates',
785
+ serviceCall: 'smsTemplateService.create',
786
+ details: `template=${smsTemplate.name}`,
787
+ });
516
788
  }
517
789
  }
518
790
  async handleSeedMenus(menus) {
519
791
  if (!menus) {
520
792
  return;
521
793
  }
522
- await this.dataSource.transaction(async (trx) => {
794
+ await this.timeOperation('menus-transaction', () => this.dataSource.transaction(async (trx) => {
523
795
  const menuRepo = trx.getRepository(menu_item_metadata_entity_1.MenuItemMetadata);
524
796
  const roleRepo = trx.getRepository(role_metadata_entity_1.RoleMetadata);
525
797
  const actionRepo = trx.getRepository(action_metadata_entity_1.ActionMetadata);
526
798
  const moduleRepo = trx.getRepository(module_metadata_entity_1.ModuleMetadata);
527
799
  for (const m of menus) {
528
800
  const action = m.actionUserKey
529
- ? await actionRepo.findOne({ where: { name: m.actionUserKey }, select: ["id"] })
801
+ ? await this.timeOperation('menu-action-find-one', () => actionRepo.findOne({ where: { name: m.actionUserKey }, select: ["id"] }), {
802
+ component: 'menus',
803
+ serviceCall: 'actionRepo.findOne',
804
+ details: `action=${m.actionUserKey}`,
805
+ })
530
806
  : null;
531
807
  const module = m.moduleUserKey
532
- ? await moduleRepo.findOne({ where: { name: m.moduleUserKey }, select: ["id"] })
808
+ ? await this.timeOperation('menu-module-find-one', () => moduleRepo.findOne({ where: { name: m.moduleUserKey }, select: ["id"] }), {
809
+ component: 'menus',
810
+ serviceCall: 'moduleRepo.findOne',
811
+ details: `module=${m.moduleUserKey}`,
812
+ })
533
813
  : null;
534
814
  const parentMenuItem = m.parentMenuItemUserKey
535
- ? await menuRepo.findOne({ where: { name: m.parentMenuItemUserKey }, select: ["id"] })
815
+ ? await this.timeOperation('menu-parent-find-one', () => menuRepo.findOne({ where: { name: m.parentMenuItemUserKey }, select: ["id"] }), {
816
+ component: 'menus',
817
+ serviceCall: 'menuRepo.findOne',
818
+ details: `parent=${m.parentMenuItemUserKey}`,
819
+ })
536
820
  : null;
537
- const existing = await menuRepo.findOne({
821
+ const existing = await this.timeOperation('menu-existing-find-one', () => menuRepo.findOne({
538
822
  where: { name: m.name },
539
- select: ["id"],
823
+ relations: {
824
+ action: true,
825
+ module: true,
826
+ parentMenuItem: true,
827
+ },
828
+ }), {
829
+ component: 'menus',
830
+ serviceCall: 'menuRepo.findOne',
831
+ details: `menu=${m.name}`,
540
832
  });
541
833
  const base = {
542
834
  name: m.name,
@@ -547,23 +839,51 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
547
839
  sequenceNumber: m.sequenceNumber,
548
840
  iconName: m.iconName,
549
841
  };
842
+ const hasChanges = existing
843
+ ? existing.displayName !== base.displayName
844
+ || existing.sequenceNumber !== base.sequenceNumber
845
+ || existing.iconName !== base.iconName
846
+ || existing.action?.id !== base.action?.id
847
+ || existing.module?.id !== base.module?.id
848
+ || existing.parentMenuItem?.id !== base.parentMenuItem?.id
849
+ : true;
850
+ if (existing && !hasChanges) {
851
+ this.logger.debug(`Skipping menu upsert for ${m.name}; no changes detected.`);
852
+ continue;
853
+ }
550
854
  const entity = menuRepo.create(existing ? { id: existing.id, ...base } : base);
551
- await menuRepo.save(entity);
855
+ await this.timeOperation('menu-save', () => menuRepo.save(entity), {
856
+ component: 'menus',
857
+ serviceCall: 'menuRepo.save',
858
+ details: `menu=${m.name}`,
859
+ });
552
860
  }
553
- const seeded = await menuRepo.find({
861
+ const seeded = await this.timeOperation('menu-seeded-find', () => menuRepo.find({
554
862
  where: { name: (0, typeorm_1.In)(menus.map((m) => m.name)) },
555
863
  select: ["id", "name"],
864
+ }), {
865
+ component: 'menus',
866
+ serviceCall: 'menuRepo.find',
867
+ details: `count=${menus.length}`,
556
868
  });
557
869
  const idByName = new Map(seeded.map(s => [s.name, s.id]));
558
- const admin = await roleRepo.findOne({ where: { name: create_role_metadata_dto_1.ADMIN_ROLE_NAME }, select: ["id", "name"] });
870
+ const admin = await this.timeOperation('menu-admin-role-find-one', () => roleRepo.findOne({ where: { name: create_role_metadata_dto_1.ADMIN_ROLE_NAME }, select: ["id", "name"] }), {
871
+ component: 'menus',
872
+ serviceCall: 'roleRepo.findOne',
873
+ details: `role=${create_role_metadata_dto_1.ADMIN_ROLE_NAME}`,
874
+ });
559
875
  const allRoleNames = new Set();
560
876
  for (const m of menus)
561
877
  (m.roles ?? []).forEach((r) => allRoleNames.add(r));
562
878
  if (admin)
563
879
  allRoleNames.add(admin.name);
564
- const roles = await roleRepo.find({
880
+ const roles = await this.timeOperation('menu-roles-find', () => roleRepo.find({
565
881
  where: { name: (0, typeorm_1.In)([...allRoleNames]) },
566
882
  select: ["id", "name"],
883
+ }), {
884
+ component: 'menus',
885
+ serviceCall: 'roleRepo.find',
886
+ details: `roleCount=${allRoleNames.size}`,
567
887
  });
568
888
  const roleByName = new Map(roles.map(r => [r.name, r]));
569
889
  const joinRows = [];
@@ -578,57 +898,153 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
578
898
  }
579
899
  const menuIds = [...new Set(joinRows.map(r => r.menuId))];
580
900
  if (menuIds.length) {
581
- await trx
901
+ await this.timeOperation('menu-role-joins-delete', () => trx
582
902
  .createQueryBuilder()
583
903
  .delete()
584
904
  .from(create_menu_item_metadata_dto_1.MENU_ROLE_JOIN_TABLE_NAME)
585
905
  .where(`${create_menu_item_metadata_dto_1.MENU_ROLE_JOIN_TABLE_NAME_MENU_COL} IN (:...ids)`, { ids: menuIds })
586
- .execute();
906
+ .execute(), {
907
+ component: 'menus',
908
+ serviceCall: 'trx.delete.menuRoleJoins',
909
+ details: `menuCount=${menuIds.length}`,
910
+ });
587
911
  }
588
912
  if (joinRows.length) {
589
913
  const values = joinRows.map(r => ({
590
914
  [create_menu_item_metadata_dto_1.MENU_ROLE_JOIN_TABLE_NAME_MENU_COL]: r.menuId,
591
915
  [create_menu_item_metadata_dto_1.MENU_ROLE_JOIN_TABLE_NAME_ROLE_COL]: r.roleId,
592
916
  }));
593
- await trx
917
+ await this.timeOperation('menu-role-joins-insert', () => trx
594
918
  .createQueryBuilder()
595
919
  .insert()
596
920
  .into(create_menu_item_metadata_dto_1.MENU_ROLE_JOIN_TABLE_NAME)
597
921
  .values(values)
598
- .execute();
922
+ .execute(), {
923
+ component: 'menus',
924
+ serviceCall: 'trx.insert.menuRoleJoins',
925
+ details: `rowCount=${values.length}`,
926
+ });
599
927
  }
600
- });
928
+ }), { component: 'menus', serviceCall: 'dataSource.transaction' });
601
929
  }
602
930
  async handleSeedActions(actions) {
603
931
  if (!actions) {
604
932
  return;
605
933
  }
934
+ const actionRepo = this.dataSource.getRepository(action_metadata_entity_1.ActionMetadata);
935
+ await this.timeOperation('action-prime-module-cache', () => this.primeModuleLookupCache(actions.map((action) => action?.moduleUserKey)), {
936
+ component: 'actions',
937
+ serviceCall: 'primeModuleLookupCache',
938
+ details: `count=${actions.length}`,
939
+ });
940
+ await this.timeOperation('action-prime-model-cache', () => this.primeModelLookupCache(actions.map((action) => action?.modelUserKey)), {
941
+ component: 'actions',
942
+ serviceCall: 'primeModelLookupCache',
943
+ details: `count=${actions.length}`,
944
+ });
945
+ await this.timeOperation('action-prime-view-cache', () => this.primeViewLookupCache(actions.map((action) => action?.viewUserKey)), {
946
+ component: 'actions',
947
+ serviceCall: 'primeViewLookupCache',
948
+ details: `count=${actions.length}`,
949
+ });
950
+ const existingActionsByName = await this.timeOperation('action-preload-existing', () => this.loadExistingActionsByName(actionRepo, actions.map((action) => action?.name).filter(Boolean)), {
951
+ component: 'actions',
952
+ serviceCall: 'loadExistingActionsByName',
953
+ details: `count=${actions.length}`,
954
+ });
955
+ let created = 0;
956
+ let updated = 0;
957
+ let skipped = 0;
606
958
  for (let j = 0; j < actions.length; j++) {
607
959
  const actionData = actions[j];
608
- actionData['module'] = await this.moduleMetadataService.findOneByUserKey(actionData.moduleUserKey);
960
+ actionData['module'] = await this.getModuleByUserKeyCached(actionData.moduleUserKey, {
961
+ moduleName: actionData.moduleUserKey,
962
+ component: 'actions',
963
+ details: `module=${actionData.moduleUserKey}`,
964
+ });
609
965
  if (actionData.type === 'solid') {
610
- actionData['model'] = await this.modelMetadataService.findOneByUserKey(actionData.modelUserKey);
611
- actionData['view'] = await this.solidViewService.findOneByUserKey(actionData.viewUserKey);
966
+ actionData['model'] = await this.getModelByUserKeyCached(actionData.modelUserKey, {
967
+ moduleName: actionData.moduleUserKey,
968
+ component: 'actions',
969
+ details: `model=${actionData.modelUserKey}`,
970
+ });
971
+ actionData['view'] = await this.getViewByUserKeyCached(actionData.viewUserKey, {
972
+ moduleName: actionData.moduleUserKey,
973
+ component: 'actions',
974
+ details: `view=${actionData.viewUserKey}`,
975
+ });
612
976
  }
613
977
  else {
614
978
  if (actionData.modelUserKey) {
615
- actionData['model'] = await this.modelMetadataService.findOneByUserKey(actionData.modelUserKey);
979
+ actionData['model'] = await this.getModelByUserKeyCached(actionData.modelUserKey, {
980
+ moduleName: actionData.moduleUserKey,
981
+ component: 'actions',
982
+ details: `model=${actionData.modelUserKey}`,
983
+ });
616
984
  }
617
985
  }
618
- await this.solidActionService.upsert(actionData);
986
+ const result = await this.upsertActionMetadataWithPreloadedExisting(actionRepo, existingActionsByName, actionData);
987
+ if (result.outcome === 'created') {
988
+ created += 1;
989
+ }
990
+ else if (result.outcome === 'updated') {
991
+ updated += 1;
992
+ }
993
+ else {
994
+ skipped += 1;
995
+ }
619
996
  }
997
+ this.logger.debug(`Action seed summary: created=${created}, updated=${updated}, skipped=${skipped}.`);
620
998
  }
621
999
  async handleSeedViews(views) {
622
1000
  if (!views) {
623
1001
  return;
624
1002
  }
1003
+ const viewRepo = this.dataSource.getRepository(view_metadata_entity_1.ViewMetadata);
1004
+ await this.timeOperation('view-prime-module-cache', () => this.primeModuleLookupCache(views.map((view) => view?.moduleUserKey)), {
1005
+ component: 'views',
1006
+ serviceCall: 'primeModuleLookupCache',
1007
+ details: `count=${views.length}`,
1008
+ });
1009
+ await this.timeOperation('view-prime-model-cache', () => this.primeModelLookupCache(views.map((view) => view?.modelUserKey)), {
1010
+ component: 'views',
1011
+ serviceCall: 'primeModelLookupCache',
1012
+ details: `count=${views.length}`,
1013
+ });
1014
+ const existingViewsByName = await this.timeOperation('view-preload-existing', () => this.loadExistingViewsByName(viewRepo, views.map((view) => view?.name).filter(Boolean)), {
1015
+ component: 'views',
1016
+ serviceCall: 'loadExistingViewsByName',
1017
+ details: `count=${views.length}`,
1018
+ });
1019
+ let created = 0;
1020
+ let updated = 0;
1021
+ let skipped = 0;
625
1022
  for (let j = 0; j < views.length; j++) {
626
1023
  const viewData = views[j];
627
1024
  viewData['layout'] = JSON.stringify(viewData['layout'], null, 2);
628
- viewData['module'] = await this.moduleMetadataService.findOneByUserKey(viewData.moduleUserKey);
629
- viewData['model'] = await this.modelMetadataService.findOneByUserKey(viewData.modelUserKey);
630
- await this.solidViewService.upsert(viewData);
1025
+ viewData['module'] = await this.getModuleByUserKeyCached(viewData.moduleUserKey, {
1026
+ moduleName: viewData.moduleUserKey,
1027
+ component: 'views',
1028
+ details: `module=${viewData.moduleUserKey}`,
1029
+ });
1030
+ viewData['model'] = await this.getModelByUserKeyCached(viewData.modelUserKey, {
1031
+ moduleName: viewData.moduleUserKey,
1032
+ component: 'views',
1033
+ details: `model=${viewData.modelUserKey}`,
1034
+ });
1035
+ const result = await this.upsertViewMetadataWithPreloadedExisting(viewRepo, existingViewsByName, viewData);
1036
+ this.viewLookupCache.set(viewData.name, result.entity ?? null);
1037
+ if (result.outcome === 'created') {
1038
+ created += 1;
1039
+ }
1040
+ else if (result.outcome === 'updated') {
1041
+ updated += 1;
1042
+ }
1043
+ else {
1044
+ skipped += 1;
1045
+ }
631
1046
  }
1047
+ this.logger.debug(`View seed summary: created=${created}, updated=${updated}, skipped=${skipped}.`);
632
1048
  }
633
1049
  async handleSeedUsers(users) {
634
1050
  if (!users) {
@@ -636,19 +1052,33 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
636
1052
  }
637
1053
  for (let l = 0; l < users.length; l++) {
638
1054
  const user = users[l];
639
- let exisitingUser = await this.userService.findOneByUsername(user.username);
1055
+ let exisitingUser = await this.timeOperation('user-find-by-username', () => this.userService.findOneByUsername(user.username), {
1056
+ component: 'users',
1057
+ serviceCall: 'userService.findOneByUsername',
1058
+ details: `username=${user.username}`,
1059
+ });
640
1060
  if (!exisitingUser) {
641
1061
  if (user.username === 'sa') {
642
1062
  user.password = create_user_dto_1.DEFAULT_SA_PASSWORD;
643
1063
  }
644
- exisitingUser = await this.authenticationService.signUp(user);
1064
+ exisitingUser = await this.timeOperation('user-sign-up', () => this.authenticationService.signUp(user), {
1065
+ component: 'users',
1066
+ serviceCall: 'authenticationService.signUp',
1067
+ details: `username=${user.username}`,
1068
+ });
645
1069
  this.logger.log(`Newly created user ${user.username}`);
646
1070
  }
647
1071
  }
648
1072
  }
649
1073
  async seedModuleModelFields(moduleMetadata) {
650
- this.logger.debug(`[Start] Processing module metadata`);
651
- const module = await this.moduleMetadataService.upsert(moduleMetadata);
1074
+ const fieldMetadataRepo = this.dataSource.getRepository(field_metadata_entity_1.FieldMetadata);
1075
+ const module = await this.timeOperation('module-upsert', () => this.moduleMetadataService.upsert(moduleMetadata), {
1076
+ moduleName: moduleMetadata.name,
1077
+ component: 'module-model-fields',
1078
+ serviceCall: 'moduleMetadataService.upsert',
1079
+ details: `module=${moduleMetadata.name}`,
1080
+ });
1081
+ this.moduleLookupCache.set(moduleMetadata.name, module ?? null);
652
1082
  let pruned = 0;
653
1083
  let upserted = 1;
654
1084
  const modelsMetadata = this.getSeedArray(moduleMetadata?.models);
@@ -658,24 +1088,59 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
658
1088
  modelMetadata['module'] = module;
659
1089
  const { fields: fieldsMetadata, ...modelMetaDataWithoutFields } = modelMetadata;
660
1090
  if (modelMetadata.isChild && modelMetadata.parentModelUserKey) {
661
- const parentModel = await this.modelMetadataService.findOneByUserKey(modelMetadata.parentModelUserKey);
1091
+ const parentModel = await this.getModelByUserKeyCached(modelMetadata.parentModelUserKey, {
1092
+ moduleName: moduleMetadata.name,
1093
+ component: 'module-model-fields',
1094
+ details: `parentModel=${modelMetadata.parentModelUserKey}`,
1095
+ });
662
1096
  modelMetaDataWithoutFields['parentModel'] = parentModel;
663
1097
  }
664
- await this.modelMetadataService.upsert(modelMetaDataWithoutFields);
665
- const model = await this.modelMetadataService.findOneBySingularName(modelMetadata.singularName);
1098
+ const upsertedModel = await this.timeOperation('model-upsert', () => this.modelMetadataService.upsert(modelMetaDataWithoutFields), {
1099
+ moduleName: moduleMetadata.name,
1100
+ component: 'module-model-fields',
1101
+ serviceCall: 'modelMetadataService.upsert',
1102
+ details: `model=${modelMetadata.singularName}`,
1103
+ });
1104
+ this.modelLookupCache.set(modelMetadata.singularName, upsertedModel ?? null);
1105
+ const model = await this.timeOperation('model-find-by-singular-name', () => this.modelMetadataService.findOneBySingularName(modelMetadata.singularName), {
1106
+ moduleName: moduleMetadata.name,
1107
+ component: 'module-model-fields',
1108
+ serviceCall: 'modelMetadataService.findOneBySingularName',
1109
+ details: `model=${modelMetadata.singularName}`,
1110
+ });
666
1111
  if (this.enablePruning) {
667
- pruned += await this.pruneFieldsForModel(model, fieldsMetadata);
1112
+ pruned += await this.timeOperation('prune-fields-for-model', () => this.pruneFieldsForModel(model, fieldsMetadata), {
1113
+ moduleName: moduleMetadata.name,
1114
+ component: 'module-model-fields',
1115
+ serviceCall: 'pruneFieldsForModel',
1116
+ details: `model=${modelMetadata.singularName}`,
1117
+ });
668
1118
  }
669
1119
  let userKeyField = null;
670
1120
  const userKeyFieldName = modelMetadata.userKeyFieldUserKey;
1121
+ const existingFieldsByName = await this.timeOperation('field-preload-for-model', () => this.loadExistingFieldsByName(fieldMetadataRepo, model.id), {
1122
+ moduleName: moduleMetadata.name,
1123
+ component: 'module-model-fields',
1124
+ serviceCall: 'loadExistingFieldsByName',
1125
+ details: `model=${modelMetadata.singularName}`,
1126
+ });
671
1127
  upserted += fieldsMetadata?.length ?? 0;
672
1128
  for (let k = 0; k < fieldsMetadata.length; k++) {
673
1129
  const fieldMetadata = fieldsMetadata[k];
674
1130
  fieldMetadata['model'] = model;
675
1131
  if (fieldMetadata.mediaStorageProviderUserKey) {
676
- fieldMetadata['mediaStorageProvider'] = await this.mediaStorageProviderMetadataService.findOneByUserKey(fieldMetadata.mediaStorageProviderUserKey);
1132
+ fieldMetadata['mediaStorageProvider'] = await this.getMediaStorageProviderByUserKeyCached(fieldMetadata.mediaStorageProviderUserKey, {
1133
+ moduleName: moduleMetadata.name,
1134
+ component: 'module-model-fields',
1135
+ details: `provider=${fieldMetadata.mediaStorageProviderUserKey}`,
1136
+ });
677
1137
  }
678
- const affectedField = await this.fieldMetadataService.upsert(fieldMetadata);
1138
+ const affectedField = await this.timeOperation('field-upsert', () => this.upsertFieldMetadataWithPreloadedExisting(fieldMetadataRepo, existingFieldsByName, fieldMetadata), {
1139
+ moduleName: moduleMetadata.name,
1140
+ component: 'module-model-fields',
1141
+ serviceCall: 'upsertFieldMetadataWithPreloadedExisting',
1142
+ details: `field=${fieldMetadata.name} model=${modelMetadata.singularName}`,
1143
+ });
679
1144
  if (fieldMetadata.name === userKeyFieldName || fieldMetadata.isUserKey) {
680
1145
  const { model, ...fieldData } = affectedField;
681
1146
  userKeyField = fieldData;
@@ -683,25 +1148,285 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
683
1148
  }
684
1149
  if (userKeyField) {
685
1150
  modelMetaDataWithoutFields['userKeyField'] = userKeyField;
686
- await this.modelMetadataService.upsert(modelMetaDataWithoutFields);
1151
+ await this.timeOperation('model-user-key-field-upsert', () => this.modelMetadataService.upsert(modelMetaDataWithoutFields), {
1152
+ moduleName: moduleMetadata.name,
1153
+ component: 'module-model-fields',
1154
+ serviceCall: 'modelMetadataService.upsert',
1155
+ details: `model=${modelMetadata.singularName} userKeyField=${userKeyField?.name ?? 'unknown'}`,
1156
+ });
687
1157
  }
688
1158
  }
689
1159
  if (this.enablePruning) {
690
- pruned += await this.pruneModels(modelsMetadata, moduleMetadata.name);
1160
+ pruned += await this.timeOperation('prune-models', () => this.pruneModels(modelsMetadata, moduleMetadata.name), {
1161
+ moduleName: moduleMetadata.name,
1162
+ component: 'module-model-fields',
1163
+ serviceCall: 'pruneModels',
1164
+ });
691
1165
  }
692
- this.logger.debug(`[End] Processing module metadata`);
693
1166
  return { pruned, upserted };
694
1167
  }
695
1168
  getSeedArray(value) {
696
1169
  return Array.isArray(value) ? value : [];
697
1170
  }
1171
+ async loadExistingFieldsByName(fieldMetadataRepo, modelId) {
1172
+ const existingFields = await fieldMetadataRepo.find({
1173
+ where: {
1174
+ model: { id: modelId },
1175
+ },
1176
+ relations: {
1177
+ model: true,
1178
+ mediaStorageProvider: true,
1179
+ },
1180
+ });
1181
+ return new Map(existingFields.map((field) => [field.name, field]));
1182
+ }
1183
+ async primeModuleLookupCache(moduleUserKeys) {
1184
+ const missingModuleUserKeys = [...new Set(moduleUserKeys.filter((moduleUserKey) => Boolean(moduleUserKey) && !this.moduleLookupCache.has(moduleUserKey)))];
1185
+ if (missingModuleUserKeys.length === 0) {
1186
+ return;
1187
+ }
1188
+ const moduleRepo = this.dataSource.getRepository(module_metadata_entity_1.ModuleMetadata);
1189
+ const existingModules = await moduleRepo.find({
1190
+ where: {
1191
+ name: (0, typeorm_1.In)(missingModuleUserKeys),
1192
+ },
1193
+ });
1194
+ const moduleByName = new Map(existingModules.map((module) => [module.name, module]));
1195
+ for (const moduleUserKey of missingModuleUserKeys) {
1196
+ this.moduleLookupCache.set(moduleUserKey, moduleByName.get(moduleUserKey) ?? null);
1197
+ }
1198
+ }
1199
+ async primeModelLookupCache(modelUserKeys) {
1200
+ const missingModelUserKeys = [...new Set(modelUserKeys.filter((modelUserKey) => Boolean(modelUserKey) && !this.modelLookupCache.has(modelUserKey)))];
1201
+ if (missingModelUserKeys.length === 0) {
1202
+ return;
1203
+ }
1204
+ const modelRepo = this.dataSource.getRepository(model_metadata_entity_1.ModelMetadata);
1205
+ const existingModels = await modelRepo.find({
1206
+ where: {
1207
+ singularName: (0, typeorm_1.In)(missingModelUserKeys),
1208
+ },
1209
+ relations: {
1210
+ module: true,
1211
+ userKeyField: true,
1212
+ },
1213
+ });
1214
+ const modelBySingularName = new Map(existingModels.map((model) => [model.singularName, model]));
1215
+ for (const modelUserKey of missingModelUserKeys) {
1216
+ this.modelLookupCache.set(modelUserKey, modelBySingularName.get(modelUserKey) ?? null);
1217
+ }
1218
+ }
1219
+ async primeViewLookupCache(viewUserKeys) {
1220
+ const missingViewUserKeys = [...new Set(viewUserKeys.filter((viewUserKey) => Boolean(viewUserKey) && !this.viewLookupCache.has(viewUserKey)))];
1221
+ if (missingViewUserKeys.length === 0) {
1222
+ return;
1223
+ }
1224
+ const viewRepo = this.dataSource.getRepository(view_metadata_entity_1.ViewMetadata);
1225
+ const existingViews = await viewRepo.find({
1226
+ where: {
1227
+ name: (0, typeorm_1.In)(missingViewUserKeys),
1228
+ },
1229
+ relations: {
1230
+ module: true,
1231
+ model: true,
1232
+ },
1233
+ });
1234
+ const viewByName = new Map(existingViews.map((view) => [view.name, view]));
1235
+ for (const viewUserKey of missingViewUserKeys) {
1236
+ this.viewLookupCache.set(viewUserKey, viewByName.get(viewUserKey) ?? null);
1237
+ }
1238
+ }
1239
+ async loadExistingViewsByName(viewRepo, viewNames) {
1240
+ const uniqueViewNames = [...new Set(viewNames.filter(Boolean))];
1241
+ if (uniqueViewNames.length === 0) {
1242
+ return new Map();
1243
+ }
1244
+ const existingViews = await viewRepo.find({
1245
+ where: {
1246
+ name: (0, typeorm_1.In)(uniqueViewNames),
1247
+ },
1248
+ relations: {
1249
+ module: true,
1250
+ model: true,
1251
+ },
1252
+ });
1253
+ return new Map(existingViews.map((view) => [view.name, view]));
1254
+ }
1255
+ async loadExistingActionsByName(actionRepo, actionNames) {
1256
+ const uniqueActionNames = [...new Set(actionNames.filter(Boolean))];
1257
+ if (uniqueActionNames.length === 0) {
1258
+ return new Map();
1259
+ }
1260
+ const existingActions = await actionRepo.find({
1261
+ where: {
1262
+ name: (0, typeorm_1.In)(uniqueActionNames),
1263
+ },
1264
+ relations: {
1265
+ module: true,
1266
+ model: true,
1267
+ view: true,
1268
+ },
1269
+ });
1270
+ return new Map(existingActions.map((action) => [action.name, action]));
1271
+ }
1272
+ normalizeJsonFieldValue(value) {
1273
+ if (typeof value === 'string') {
1274
+ const trimmedValue = value.trim();
1275
+ if ((trimmedValue.startsWith('{') && trimmedValue.endsWith('}'))
1276
+ || (trimmedValue.startsWith('[') && trimmedValue.endsWith(']'))) {
1277
+ try {
1278
+ return this.normalizeJsonFieldValue(JSON.parse(trimmedValue));
1279
+ }
1280
+ catch {
1281
+ return value;
1282
+ }
1283
+ }
1284
+ return value;
1285
+ }
1286
+ if (Array.isArray(value)) {
1287
+ return value.map((item) => this.normalizeJsonFieldValue(item));
1288
+ }
1289
+ if (value && typeof value === 'object') {
1290
+ return Object.keys(value)
1291
+ .sort()
1292
+ .reduce((result, key) => {
1293
+ result[key] = this.normalizeJsonFieldValue(value[key]);
1294
+ return result;
1295
+ }, {});
1296
+ }
1297
+ return value;
1298
+ }
1299
+ getCanonicalJsonFieldString(value) {
1300
+ return JSON.stringify(this.normalizeJsonFieldValue(value) ?? null);
1301
+ }
1302
+ hasViewMetadataChanges(existingView, updateDto) {
1303
+ const relationFields = new Set(['module', 'model']);
1304
+ const jsonFields = new Set(['layout', 'context']);
1305
+ return this.viewComparableFields.some((key) => {
1306
+ const value = updateDto[key];
1307
+ if (typeof value === 'undefined') {
1308
+ return false;
1309
+ }
1310
+ if (relationFields.has(key)) {
1311
+ const relationValue = value;
1312
+ return existingView[key]?.id !== relationValue?.id;
1313
+ }
1314
+ if (jsonFields.has(key)) {
1315
+ return this.getCanonicalJsonFieldString(existingView[key]) !== this.getCanonicalJsonFieldString(value);
1316
+ }
1317
+ return existingView[key] !== value;
1318
+ });
1319
+ }
1320
+ hasActionMetadataChanges(existingAction, updateDto) {
1321
+ const relationFields = new Set(['module', 'model', 'view']);
1322
+ const jsonFields = new Set(['domain', 'context']);
1323
+ return this.actionComparableFields.some((key) => {
1324
+ const value = updateDto[key];
1325
+ if (typeof value === 'undefined') {
1326
+ return false;
1327
+ }
1328
+ if (relationFields.has(key)) {
1329
+ const relationValue = value;
1330
+ return existingAction[key]?.id !== relationValue?.id;
1331
+ }
1332
+ if (jsonFields.has(key)) {
1333
+ return this.getCanonicalJsonFieldString(existingAction[key]) !== this.getCanonicalJsonFieldString(value);
1334
+ }
1335
+ return existingAction[key] !== value;
1336
+ });
1337
+ }
1338
+ async upsertViewMetadataWithPreloadedExisting(viewRepo, existingViewsByName, updateDto) {
1339
+ const existingView = existingViewsByName.get(updateDto.name);
1340
+ if (existingView) {
1341
+ if (!this.hasViewMetadataChanges(existingView, updateDto)) {
1342
+ return { entity: existingView, outcome: 'skipped' };
1343
+ }
1344
+ const updatedView = { ...existingView, ...updateDto };
1345
+ const savedView = await this.timeOperation('view-save', () => viewRepo.save(updatedView), {
1346
+ component: 'views',
1347
+ serviceCall: 'viewRepo.save',
1348
+ details: `view=${updateDto.name}`,
1349
+ });
1350
+ existingViewsByName.set(updateDto.name, savedView);
1351
+ return { entity: savedView, outcome: 'updated' };
1352
+ }
1353
+ const view = viewRepo.create(updateDto);
1354
+ const savedView = await this.timeOperation('view-save', () => viewRepo.save(view), {
1355
+ component: 'views',
1356
+ serviceCall: 'viewRepo.save',
1357
+ details: `view=${updateDto.name}`,
1358
+ });
1359
+ existingViewsByName.set(updateDto.name, savedView);
1360
+ return { entity: savedView, outcome: 'created' };
1361
+ }
1362
+ async upsertActionMetadataWithPreloadedExisting(actionRepo, existingActionsByName, updateDto) {
1363
+ const existingAction = existingActionsByName.get(updateDto.name);
1364
+ if (existingAction) {
1365
+ if (!this.hasActionMetadataChanges(existingAction, updateDto)) {
1366
+ return { entity: existingAction, outcome: 'skipped' };
1367
+ }
1368
+ const updatedAction = { ...existingAction, ...updateDto };
1369
+ const savedAction = await this.timeOperation('action-save', () => actionRepo.save(updatedAction), {
1370
+ component: 'actions',
1371
+ serviceCall: 'actionRepo.save',
1372
+ details: `action=${updateDto.name}`,
1373
+ });
1374
+ existingActionsByName.set(updateDto.name, savedAction);
1375
+ return { entity: savedAction, outcome: 'updated' };
1376
+ }
1377
+ const action = actionRepo.create(updateDto);
1378
+ const savedAction = await this.timeOperation('action-save', () => actionRepo.save(action), {
1379
+ component: 'actions',
1380
+ serviceCall: 'actionRepo.save',
1381
+ details: `action=${updateDto.name}`,
1382
+ });
1383
+ existingActionsByName.set(updateDto.name, savedAction);
1384
+ return { entity: savedAction, outcome: 'created' };
1385
+ }
1386
+ async upsertFieldMetadataWithPreloadedExisting(fieldMetadataRepo, existingFieldsByName, updateDto) {
1387
+ const existingFieldMetadata = existingFieldsByName.get(updateDto.name);
1388
+ if (existingFieldMetadata) {
1389
+ const hasChanges = Object.entries(updateDto).some(([key, value]) => {
1390
+ const relationValue = value;
1391
+ if (key === 'model') {
1392
+ return existingFieldMetadata.model?.id !== relationValue?.id;
1393
+ }
1394
+ if (key === 'mediaStorageProvider') {
1395
+ return existingFieldMetadata.mediaStorageProvider?.id !== relationValue?.id;
1396
+ }
1397
+ const currentValue = existingFieldMetadata[key];
1398
+ if (Array.isArray(currentValue) || Array.isArray(value)) {
1399
+ return JSON.stringify(currentValue ?? null) !== JSON.stringify(value ?? null);
1400
+ }
1401
+ if (value && typeof value === 'object') {
1402
+ return JSON.stringify(currentValue ?? null) !== JSON.stringify(value ?? null);
1403
+ }
1404
+ return currentValue !== value;
1405
+ });
1406
+ if (!hasChanges) {
1407
+ return existingFieldMetadata;
1408
+ }
1409
+ const updatedFieldMetadata = { ...existingFieldMetadata, ...updateDto };
1410
+ const savedFieldMetadata = await fieldMetadataRepo.save(updatedFieldMetadata);
1411
+ existingFieldsByName.set(updateDto.name, savedFieldMetadata);
1412
+ return savedFieldMetadata;
1413
+ }
1414
+ const fieldMetadata = fieldMetadataRepo.create(updateDto);
1415
+ const savedFieldMetadata = await fieldMetadataRepo.save(fieldMetadata);
1416
+ existingFieldsByName.set(updateDto.name, savedFieldMetadata);
1417
+ return savedFieldMetadata;
1418
+ }
698
1419
  async handleSeedSecurityRules(rulesDto) {
699
1420
  if (!rulesDto || rulesDto.length === 0) {
700
1421
  this.logger.debug(`No security rules found to seed`);
701
1422
  return;
702
1423
  }
703
1424
  for (const dto of rulesDto) {
704
- await this.securityRuleRepo.upsertWithDto({ ...dto, securityRuleConfig: JSON.stringify(dto.securityRuleConfig) });
1425
+ await this.timeOperation('security-rule-upsert', () => this.securityRuleRepo.upsertWithDto({ ...dto, securityRuleConfig: JSON.stringify(dto.securityRuleConfig) }), {
1426
+ component: 'security-rules',
1427
+ serviceCall: 'securityRuleRepo.upsertWithDto',
1428
+ details: `rule=${dto.name}`,
1429
+ });
705
1430
  }
706
1431
  }
707
1432
  async handleSeedListOfValues(listOfValuesDto) {
@@ -711,8 +1436,16 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
711
1436
  }
712
1437
  for (let j = 0; j < listOfValuesDto.length; j++) {
713
1438
  const listOfValueDto = listOfValuesDto[j];
714
- listOfValueDto['module'] = await this.moduleMetadataService.findOneByUserKey(listOfValueDto.moduleUserKey);
715
- await this.listOfValuesService.upsert(listOfValuesDto[j]);
1439
+ listOfValueDto['module'] = await this.getModuleByUserKeyCached(listOfValueDto.moduleUserKey, {
1440
+ moduleName: listOfValueDto.moduleUserKey,
1441
+ component: 'list-of-values',
1442
+ details: `module=${listOfValueDto.moduleUserKey}`,
1443
+ });
1444
+ await this.timeOperation('lov-upsert', () => this.listOfValuesService.upsert(listOfValuesDto[j]), {
1445
+ component: 'list-of-values',
1446
+ serviceCall: 'listOfValuesService.upsert',
1447
+ details: `type=${listOfValueDto.type} value=${listOfValueDto.value}`,
1448
+ });
716
1449
  }
717
1450
  }
718
1451
  async handleSeedScheduledJobs(createScheduledJobDto) {
@@ -721,7 +1454,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
721
1454
  return;
722
1455
  }
723
1456
  for (const dto of createScheduledJobDto) {
724
- await this.scheduledJobRepository.upsertWithDto(dto);
1457
+ await this.timeOperation('scheduled-job-upsert', () => this.scheduledJobRepository.upsertWithDto(dto), {
1458
+ component: 'scheduled-jobs',
1459
+ serviceCall: 'scheduledJobRepository.upsertWithDto',
1460
+ details: `schedule=${dto.scheduleName}`,
1461
+ });
725
1462
  }
726
1463
  }
727
1464
  async handleSeedSavedFilters(createSavedFilterDto) {
@@ -731,7 +1468,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
731
1468
  }
732
1469
  for (const dto of createSavedFilterDto) {
733
1470
  this.validateSavedFilterQueryJsonWrapper(dto);
734
- await this.savedFiltersRepo.upsertWithDto({ ...dto, filterQueryJson: JSON.stringify(dto.filterQueryJson), isSeeded: true });
1471
+ await this.timeOperation('saved-filter-upsert', () => this.savedFiltersRepo.upsertWithDto({ ...dto, filterQueryJson: JSON.stringify(dto.filterQueryJson), isSeeded: true }), {
1472
+ component: 'saved-filters',
1473
+ serviceCall: 'savedFiltersRepo.upsertWithDto',
1474
+ details: `filter=${dto.name}`,
1475
+ });
735
1476
  }
736
1477
  }
737
1478
  validateSavedFilterQueryJsonWrapper(dto) {
@@ -755,7 +1496,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
755
1496
  return;
756
1497
  }
757
1498
  for (const dto of modelSequencesDto) {
758
- await this.modelSequenceRepo.upsertWithDto(dto);
1499
+ await this.timeOperation('model-sequence-upsert', () => this.modelSequenceRepo.upsertWithDto(dto), {
1500
+ component: 'model-sequences',
1501
+ serviceCall: 'modelSequenceRepo.upsertWithDto',
1502
+ details: `sequence=${dto.sequenceName}`,
1503
+ });
759
1504
  }
760
1505
  }
761
1506
  async pruneModelSequences(modelSequencesDto, moduleName) {
@@ -764,7 +1509,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
764
1509
  return 0;
765
1510
  }
766
1511
  const sequences = modelSequencesDto ?? [];
767
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1512
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1513
+ moduleName,
1514
+ component: 'model-sequences',
1515
+ details: `module=${moduleName}`,
1516
+ });
768
1517
  if (!module) {
769
1518
  this.logger.warn(`Skipping model sequence prune: module not found for ${moduleName}.`);
770
1519
  return 0;
@@ -798,7 +1547,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
798
1547
  return 0;
799
1548
  }
800
1549
  const savedFilters = savedFiltersDto ?? [];
801
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1550
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1551
+ moduleName,
1552
+ component: 'saved-filters',
1553
+ details: `module=${moduleName}`,
1554
+ });
802
1555
  if (!module) {
803
1556
  this.logger.warn(`Skipping saved filters prune: module not found for ${moduleName}.`);
804
1557
  return 0;
@@ -833,7 +1586,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
833
1586
  return 0;
834
1587
  }
835
1588
  const scheduledJobs = scheduledJobsDto ?? [];
836
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1589
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1590
+ moduleName,
1591
+ component: 'scheduled-jobs',
1592
+ details: `module=${moduleName}`,
1593
+ });
837
1594
  if (!module) {
838
1595
  this.logger.warn(`Skipping scheduled jobs prune: module not found for ${moduleName}.`);
839
1596
  return 0;
@@ -867,7 +1624,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
867
1624
  return 0;
868
1625
  }
869
1626
  const securityRules = securityRulesDto ?? [];
870
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1627
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1628
+ moduleName,
1629
+ component: 'security-rules',
1630
+ details: `module=${moduleName}`,
1631
+ });
871
1632
  if (!module) {
872
1633
  this.logger.warn(`Skipping security rules prune: module not found for ${moduleName}.`);
873
1634
  return 0;
@@ -902,7 +1663,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
902
1663
  return 0;
903
1664
  }
904
1665
  const listOfValues = listOfValuesDto ?? [];
905
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1666
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1667
+ moduleName,
1668
+ component: 'list-of-values',
1669
+ details: `module=${moduleName}`,
1670
+ });
906
1671
  if (!module) {
907
1672
  this.logger.warn(`Skipping list of values prune: module not found for ${moduleName}.`);
908
1673
  return 0;
@@ -944,7 +1709,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
944
1709
  return 0;
945
1710
  }
946
1711
  const menus = menusDto ?? [];
947
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1712
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1713
+ moduleName,
1714
+ component: 'menus',
1715
+ details: `module=${moduleName}`,
1716
+ });
948
1717
  if (!module) {
949
1718
  this.logger.warn(`Skipping menus prune: module not found for ${moduleName}.`);
950
1719
  return 0;
@@ -986,7 +1755,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
986
1755
  return 0;
987
1756
  }
988
1757
  const views = viewsDto ?? [];
989
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1758
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1759
+ moduleName,
1760
+ component: 'views',
1761
+ details: `module=${moduleName}`,
1762
+ });
990
1763
  if (!module) {
991
1764
  this.logger.warn(`Skipping views prune: module not found for ${moduleName}.`);
992
1765
  return 0;
@@ -1039,7 +1812,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
1039
1812
  return 0;
1040
1813
  }
1041
1814
  const actions = actionsDto ?? [];
1042
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1815
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1816
+ moduleName,
1817
+ component: 'actions',
1818
+ details: `module=${moduleName}`,
1819
+ });
1043
1820
  if (!module) {
1044
1821
  this.logger.warn(`Skipping actions prune: module not found for ${moduleName}.`);
1045
1822
  return 0;
@@ -1125,7 +1902,11 @@ let ModuleMetadataSeederService = ModuleMetadataSeederService_1 = class ModuleMe
1125
1902
  return 0;
1126
1903
  }
1127
1904
  const models = modelsMetadata ?? [];
1128
- const module = await this.moduleMetadataService.findOneByUserKey(moduleName);
1905
+ const module = await this.getModuleByUserKeyCached(moduleName, {
1906
+ moduleName,
1907
+ component: 'module-model-fields',
1908
+ details: `module=${moduleName}`,
1909
+ });
1129
1910
  if (!module) {
1130
1911
  this.logger.warn(`Skipping models prune: module not found for ${moduleName}.`);
1131
1912
  return 0;