@solidstarters/solid-core 1.2.187 → 1.2.189

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 (89) hide show
  1. package/dist/config/iam.config.d.ts +0 -13
  2. package/dist/config/iam.config.d.ts.map +1 -1
  3. package/dist/config/iam.config.js +2 -12
  4. package/dist/config/iam.config.js.map +1 -1
  5. package/dist/config/jwt.config.d.ts +14 -0
  6. package/dist/config/jwt.config.d.ts.map +1 -0
  7. package/dist/config/jwt.config.js +15 -0
  8. package/dist/config/jwt.config.js.map +1 -0
  9. package/dist/decorators/active-user.decorator.d.ts +1 -1
  10. package/dist/dtos/create-list-of-values.dto.d.ts.map +1 -1
  11. package/dist/dtos/create-list-of-values.dto.js +1 -0
  12. package/dist/dtos/create-list-of-values.dto.js.map +1 -1
  13. package/dist/dtos/update-list-of-values.dto.d.ts.map +1 -1
  14. package/dist/dtos/update-list-of-values.dto.js +1 -0
  15. package/dist/dtos/update-list-of-values.dto.js.map +1 -1
  16. package/dist/entities/list-of-values.entity.js +1 -1
  17. package/dist/entities/list-of-values.entity.js.map +1 -1
  18. package/dist/guards/access-token.guard.d.ts +1 -1
  19. package/dist/guards/access-token.guard.d.ts.map +1 -1
  20. package/dist/guards/access-token.guard.js +2 -2
  21. package/dist/guards/access-token.guard.js.map +1 -1
  22. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.d.ts.map +1 -1
  23. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js +12 -2
  24. package/dist/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.js.map +1 -1
  25. package/dist/interfaces.d.ts +16 -1
  26. package/dist/interfaces.d.ts.map +1 -1
  27. package/dist/interfaces.js.map +1 -1
  28. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
  29. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +7 -0
  30. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
  31. package/dist/seeders/seed-data/solid-core-metadata.json +236 -119
  32. package/dist/services/authentication.service.d.ts +8 -7
  33. package/dist/services/authentication.service.d.ts.map +1 -1
  34. package/dist/services/authentication.service.js +12 -11
  35. package/dist/services/authentication.service.js.map +1 -1
  36. package/dist/services/crud.service.d.ts +1 -0
  37. package/dist/services/crud.service.d.ts.map +1 -1
  38. package/dist/services/crud.service.js +14 -12
  39. package/dist/services/crud.service.js.map +1 -1
  40. package/dist/services/dashboard-question.service.d.ts.map +1 -1
  41. package/dist/services/dashboard-question.service.js +23 -2
  42. package/dist/services/dashboard-question.service.js.map +1 -1
  43. package/dist/services/genai/mcp-handlers/solid-add-button-to-form-view-mcp-handler.service.d.ts +16 -0
  44. package/dist/services/genai/mcp-handlers/solid-add-button-to-form-view-mcp-handler.service.d.ts.map +1 -0
  45. package/dist/services/genai/mcp-handlers/solid-add-button-to-form-view-mcp-handler.service.js +151 -0
  46. package/dist/services/genai/mcp-handlers/solid-add-button-to-form-view-mcp-handler.service.js.map +1 -0
  47. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.d.ts +1 -0
  48. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.d.ts.map +1 -1
  49. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.js +8 -1
  50. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.js.map +1 -1
  51. package/dist/services/genai/mcp-handlers/solid-create-custom-form-view-widget-mcp-handler.service.d.ts +14 -0
  52. package/dist/services/genai/mcp-handlers/solid-create-custom-form-view-widget-mcp-handler.service.d.ts.map +1 -0
  53. package/dist/services/genai/mcp-handlers/solid-create-custom-form-view-widget-mcp-handler.service.js +73 -0
  54. package/dist/services/genai/mcp-handlers/solid-create-custom-form-view-widget-mcp-handler.service.js.map +1 -0
  55. package/dist/services/list-of-values-metadata.service.d.ts.map +1 -1
  56. package/dist/services/list-of-values-metadata.service.js.map +1 -1
  57. package/dist/services/model-metadata.service.js +2 -2
  58. package/dist/services/model-metadata.service.js.map +1 -1
  59. package/dist/services/selection-providers/list-of-models-selection-provider.service.js +2 -2
  60. package/dist/services/selection-providers/list-of-models-selection-provider.service.js.map +1 -1
  61. package/dist/services/setting.service.d.ts.map +1 -1
  62. package/dist/services/setting.service.js +3 -2
  63. package/dist/services/setting.service.js.map +1 -1
  64. package/dist/solid-core.module.d.ts.map +1 -1
  65. package/dist/solid-core.module.js +7 -2
  66. package/dist/solid-core.module.js.map +1 -1
  67. package/dist/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +1 -1
  69. package/src/config/iam.config.ts +1 -11
  70. package/src/config/jwt.config.ts +13 -0
  71. package/src/dtos/create-list-of-values.dto.ts +1 -0
  72. package/src/dtos/update-list-of-values.dto.ts +1 -0
  73. package/src/entities/list-of-values.entity.ts +1 -1
  74. package/src/guards/access-token.guard.ts +1 -1
  75. package/src/helpers/field-crud-managers/SelectionDynamicFieldCrudManager.ts +17 -6
  76. package/src/interfaces.ts +17 -1
  77. package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +18 -0
  78. package/src/seeders/seed-data/solid-core-metadata.json +238 -119
  79. package/src/services/authentication.service.ts +17 -17
  80. package/src/services/crud.service.ts +17 -31
  81. package/src/services/dashboard-question.service.ts +29 -2
  82. package/src/services/genai/mcp-handlers/solid-add-button-to-form-view-mcp-handler.service.ts +137 -0
  83. package/src/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.ts +21 -7
  84. package/src/services/genai/mcp-handlers/solid-create-custom-form-view-widget-mcp-handler.service.ts +72 -0
  85. package/src/services/list-of-values-metadata.service.ts +0 -3
  86. package/src/services/model-metadata.service.ts +2 -2
  87. package/src/services/selection-providers/list-of-models-selection-provider.service.ts +2 -2
  88. package/src/services/setting.service.ts +3 -2
  89. package/src/solid-core.module.ts +7 -1
@@ -15,10 +15,21 @@ import { JwtService } from '@nestjs/jwt';
15
15
  import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
16
16
  import { isEmpty, isNotEmpty } from 'class-validator';
17
17
  import { randomInt, randomUUID } from 'crypto';
18
- import { SMTPEMailService } from 'src/services/mail/smtp-email.service';
18
+ import commonConfig from 'src/config/common.config';
19
+ import { jwtConfig } from 'src/config/jwt.config';
20
+ import { ERROR_MESSAGES } from 'src/constants/error-messages';
21
+ import { SUCCESS_MESSAGES } from 'src/constants/success-messages';
22
+ import { CreateUserDto } from 'src/dtos/create-user.dto';
23
+ import { MailFactory } from 'src/factories/mail.factory';
19
24
  import { Msg91OTPService } from 'src/services/sms/Msg91OTPService';
20
25
  import { DataSource, Repository } from 'typeorm';
21
- import { iamConfig, jwtConfig } from '../config/iam.config';
26
+ import { v4 as uuidv4 } from 'uuid';
27
+ import { iamConfig } from '../config/iam.config';
28
+ import {
29
+ ForgotPasswordSendVerificationTokenOn,
30
+ RegistrationValidationSource,
31
+ TransactionalRegistrationValidationSource
32
+ } from "../constants";
22
33
  import { ChangePasswordDto } from "../dtos/change-password.dto";
23
34
  import { ConfirmForgotPasswordDto } from '../dtos/confirm-forgot-password.dto';
24
35
  import { InitiateForgotPasswordDto } from '../dtos/initiate-forgot-password.dto';
@@ -29,26 +40,15 @@ import { RefreshTokenDto } from '../dtos/refresh-token.dto';
29
40
  import { SignInDto } from '../dtos/sign-in.dto';
30
41
  import { SignUpDto } from '../dtos/sign-up.dto';
31
42
  import { User } from '../entities/user.entity';
43
+ import { EventDetails, EventType } from "../interfaces";
32
44
  import { ActiveUserData } from '../interfaces/active-user-data.interface';
33
45
  import { HashingService } from './hashing.service';
34
46
  import { InvalidatedRefreshTokenError, RefreshTokenIdsStorageService } from './refresh-token-ids-storage.service';
35
- import { UserService } from './user.service';
36
- import { EventDetails, EventType, IMail } from "../interfaces";
37
- import {
38
- ForgotPasswordSendVerificationTokenOn,
39
- RegistrationValidationSource,
40
- TransactionalRegistrationValidationSource
41
- } from "../constants";
42
- import { SettingService } from './setting.service';
43
- import { CreateUserDto } from 'src/dtos/create-user.dto';
47
+ import { RequestContextService } from './request-context.service';
44
48
  import { RoleMetadataService } from './role-metadata.service';
45
- import commonConfig from 'src/config/common.config';
49
+ import { SettingService } from './setting.service';
46
50
  import { UserActivityHistoryService } from './user-activity-history.service';
47
- import { RequestContextService } from './request-context.service';
48
- import { ERROR_MESSAGES } from 'src/constants/error-messages';
49
- import { SUCCESS_MESSAGES } from 'src/constants/success-messages';
50
- import { MailFactory } from 'src/factories/mail.factory';
51
- import { v4 as uuidv4 } from 'uuid';
51
+ import { UserService } from './user.service';
52
52
 
53
53
  enum LoginProvider {
54
54
  LOCAL = 'local',
@@ -655,14 +655,9 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
655
655
  return entity;
656
656
  }
657
657
 
658
- async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [], solidRequestContext: any = {}): Promise<T[]> {
659
-
660
-
661
- // if (createDtos.length !== filesArray.length) {
662
- // throw new BadRequestException('Mismatch between data objects and file arrays.');
663
- // }
664
-
658
+ async createMany(createDtos: any[], solidRequestContext: any = {}): Promise<T[]> {
665
659
  const loadedmodel = await this.loadModel();
660
+
666
661
  // Check wheather user has create permission for model
667
662
  if (solidRequestContext.activeUser) {
668
663
  const hasPermission = this.crudHelperService.hasCreatePermissionOnModel(solidRequestContext.activeUser, loadedmodel.singularName);
@@ -680,37 +675,28 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
680
675
  module: true,
681
676
  });
682
677
 
683
- // Process each createDto in parallel
684
- const createAndSavePromises = createDtos.map(async (createDto, index) => {
685
- const files = []; // TODO, This is set, because we are not supporting files for insertMany currently
686
- let hasMediaFields = false;
687
-
688
- // Process each field
678
+ const entitiesForSave: T[] = [];
679
+ for (const createDto of createDtos) {
680
+ // Validate and transform each createDto sequentially
681
+ let transformedDto = createDto;
689
682
  for (const field of model.fields) {
690
683
  const fieldManager: FieldCrudManager = await this.fieldCrudManager(field, this.entityManager);
691
- const validationErrors = await fieldManager.validate(createDto, files);
684
+ const validationErrors = await fieldManager.validate(createDto, []); // TODO, This is set, because we are not supporting files for insertMany currently
692
685
  if (validationErrors.length > 0) {
693
686
  throw new BadRequestException(`Validation errors in ${field.name} are invalid: ${validationErrors.map(e => e.error).join(', ')}`);
694
687
  }
695
- createDto = await fieldManager.transformForCreate(createDto);
696
- hasMediaFields = hasMediaFields || field.type === 'mediaSingle' || field.type === 'mediaMultiple';
688
+ transformedDto = await fieldManager.transformForCreate(createDto);
697
689
  }
690
+ const entity = this.repo.create(transformedDto);
691
+ entitiesForSave.push(entity as T);
692
+ }
693
+ // Save all entities in a single batch
694
+ const savedEntities = await this.repo.save(entitiesForSave) as T[];
695
+ return savedEntities;
696
+ }
698
697
 
699
- // Save the entity
700
- const entity = this.repo.create(createDto);
701
- const savedEntity = await this.repo.save(entity) as T;
702
-
703
- //Commented since currently Files are not supported for insertmany
704
- // if (hasMediaFields) {
705
- // await this.saveMedia(model, files, savedEntity);
706
- // }
707
-
708
- return savedEntity;
709
- });
710
-
711
- // Await all promises in parallel
712
- const savedEntities = await Promise.all(createAndSavePromises);
713
-
698
+ async insertMany(createDtos: any[], filesArray: Express.Multer.File[][] = [], solidRequestContext: any = {}): Promise<T[]> {
699
+ const savedEntities = await this.createMany(createDtos, solidRequestContext);
714
700
  return savedEntities;
715
701
  }
716
702
 
@@ -82,6 +82,7 @@ export class DashboardQuestionService extends CRUDService<DashboardQuestion> {
82
82
 
83
83
  private getExpressions(isPreview: boolean, dashboardVariables: DashboardVariable[], inputExpressions: SqlExpression[]) {
84
84
  const expressions: SqlExpression[] = [];
85
+
85
86
  if (isPreview) {
86
87
  // Convert the dashboard variables into objects of interface type SqlExpression using the default value, default operator and the variable name
87
88
  const expr: SqlExpression[] = dashboardVariables.map(variable => {
@@ -94,9 +95,35 @@ export class DashboardQuestionService extends CRUDService<DashboardQuestion> {
94
95
  expressions.push(...expr);
95
96
  }
96
97
  else {
97
- expressions.push(...inputExpressions);
98
+ // Loop through the dashboard variables and see if there is a matching input expression
99
+ // If there is, use that expression instead of the default value
100
+ for (const variable of dashboardVariables) {
101
+ const matchingInputExpression = inputExpressions.find(expr => expr.variableName === variable.variableName);
102
+ if (matchingInputExpression) {
103
+ expressions.push(matchingInputExpression);
104
+ }
105
+ else {
106
+ expressions.push({
107
+ variableName: variable.variableName,
108
+ operator: variable.defaultOperator as SqlExpressionOperator,
109
+ value: JSON.parse(variable.defaultValue || '[]'),
110
+ });
111
+ }
112
+ }
113
+ expressions.push(...expressions);
114
+ }
115
+
116
+ // Remove duplicate expressions based on variableName in the expressions array
117
+ const deduplicatedExpressions: SqlExpression[] = [];
118
+ const variableNames = new Set<string>();
119
+ for (const expr of expressions) {
120
+ if (!variableNames.has(expr.variableName)) {
121
+ deduplicatedExpressions.push(expr);
122
+ variableNames.add(expr.variableName);
123
+ }
98
124
  }
99
- return expressions;
125
+
126
+ return deduplicatedExpressions;
100
127
  }
101
128
 
102
129
  private async loadQuestion(id: number) {
@@ -0,0 +1,137 @@
1
+ import { Injectable, Logger } from "@nestjs/common";
2
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
3
+ import { IMcpToolResponseHandler, McpComputedProviderResponse, PlanStep } from "../../../interfaces";
4
+ import { SolidTsMorphService } from "src/services/solid-ts-morph.service";
5
+ import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
6
+ import * as fs from "fs/promises";
7
+
8
+ const RESTART_TOUCH_FILE = process.env.MCP_RESTART_TOUCH_FILE || "tmp/restart.touch";
9
+
10
+ @Injectable()
11
+ export class SolidAddButtonToFormViewMcpHandler implements IMcpToolResponseHandler {
12
+ private readonly logger = new Logger(SolidAddButtonToFormViewMcpHandler.name);
13
+
14
+ constructor(
15
+ private readonly tsMorph: SolidTsMorphService,
16
+ private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
17
+
18
+ ) { }
19
+
20
+ async apply(aiInteraction: AiInteraction) {
21
+ const raw = this.safeParse(aiInteraction.message);
22
+ const payload: McpComputedProviderResponse | undefined = (raw?.data?.plan ? raw.data : raw) as McpComputedProviderResponse;
23
+
24
+ if (!payload || !Array.isArray(payload.plan)) {
25
+ throw new Error("SolidAddButtonToFormViewMcpHandler: invalid MCP response; missing plan[]");
26
+ }
27
+
28
+ // Batch all plan steps in a single txn so nodemon restarts only once.
29
+ this.tsMorph.begin();
30
+ try {
31
+ for (const step of payload.plan as PlanStep[]) {
32
+ switch (step.type) {
33
+ case "createNewFile": {
34
+ const overwrite = step.overwrite ?? false;
35
+ this.tsMorph.createNewFile(step.path, step.content, overwrite);
36
+ break;
37
+ }
38
+ case "registerSolidXExtensionComponent": {
39
+ this.tsMorph.registerSolidUiExtension(
40
+ step.path,
41
+ step.content,
42
+ );
43
+ this.tsMorph.addImport(
44
+ step.path,
45
+ step.importExtensionComponent,
46
+ );
47
+ break;
48
+ }
49
+ case "addFormViewButton": {
50
+
51
+ const filePath =
52
+ await this.moduleMetadataHelperService.getModuleMetadataFilePath(
53
+ step.moduleName
54
+ );
55
+ try {
56
+ await fs.access(filePath);
57
+ } catch {
58
+ this.logger.error(`Metadata file not found: ${filePath}`);
59
+ return;
60
+ }
61
+ const metaData =
62
+ await this.moduleMetadataHelperService.getModuleMetadataConfiguration(
63
+ filePath
64
+ );
65
+ // Remove, update or insert logic
66
+
67
+
68
+ // Find the existing view of type "list" for the given module & model
69
+ const existingViewIndex = metaData.views.findIndex(
70
+ (view: any) =>
71
+ view.type === "form" &&
72
+ view.moduleUserKey === step.moduleName &&
73
+ view.modelUserKey === step.modelName
74
+ );
75
+
76
+ if (existingViewIndex !== -1) {
77
+ const view = metaData.views[existingViewIndex];
78
+
79
+ // Ensure layout & attrs exist
80
+ view.layout = view.layout || {};
81
+ view.layout.attrs = view.layout.attrs || {};
82
+
83
+ // Initialize rowButtons or headerButtons if not present
84
+ if (!Array.isArray(view.layout.attrs[step.buttonType])) {
85
+ view.layout.attrs[step.buttonType] = [];
86
+ }
87
+ let buttonContent = step.content;
88
+ // Parse only if it’s a string
89
+ if (typeof buttonContent === "string") {
90
+ try {
91
+ buttonContent = JSON.parse(buttonContent);
92
+ } catch (err) {
93
+ this.logger.error("❌ Failed to parse step.content JSON:", err);
94
+ return;
95
+ }
96
+ }
97
+ // Push the new button content
98
+ view.layout.attrs[step.buttonType].push(buttonContent);
99
+
100
+ console.log(`Added ${step.buttonType} to view: ${view.name}`);
101
+ } else {
102
+ console.warn(`No matching list view found for module=${step.moduleName} and model=${step.modelName}`);
103
+ }
104
+
105
+ const updatedContent = JSON.stringify(metaData, null, 2);
106
+ await fs.writeFile(filePath, updatedContent);
107
+ this.logger.log(`Updated list view in ${filePath}`);
108
+
109
+ break;
110
+ }
111
+ default:
112
+ throw new Error(`Unsupported plan step type: ${(step as any).type}`);
113
+ }
114
+ }
115
+
116
+ const result = await this.tsMorph.commit();
117
+
118
+ return {
119
+ seedingRequired: true,
120
+ serverRebooting: false,
121
+ };
122
+ } catch (err) {
123
+ this.logger.error(`Apply failed; rolling back. ${String(err)}`);
124
+ this.tsMorph.rollback();
125
+ throw err;
126
+ }
127
+ }
128
+
129
+ private safeParse(str: string): any {
130
+ try {
131
+ return JSON.parse(str);
132
+ } catch {
133
+ const unescaped = str.replace(/\\'/g, "'");
134
+ return JSON.parse(unescaped);
135
+ }
136
+ }
137
+ }
@@ -1,6 +1,6 @@
1
1
  import { Injectable, Logger } from "@nestjs/common";
2
2
  import { AiInteraction } from "src/entities/ai-interaction.entity";
3
- import { IMcpToolResponseHandler, McpComputedProviderResponse, PlanStep } from "../../../interfaces";
3
+ import { AddMethodToExistingClassStep, IMcpToolResponseHandler, McpComputedProviderResponse, PlanStep } from "../../../interfaces";
4
4
  import { SolidTsMorphService } from "src/services/solid-ts-morph.service";
5
5
 
6
6
  @Injectable()
@@ -28,12 +28,7 @@ export class SolidAddControllerHandlerMcpHandler implements IMcpToolResponseHand
28
28
  break;
29
29
  }
30
30
  case "addMethodToExistingClass": {
31
- this.tsMorph.addMethodToExistingClass(
32
- step.path,
33
- step.className,
34
- step.methodName,
35
- step.content,
36
- );
31
+ this.handleAddMethodToExistingClass(step);
37
32
  break;
38
33
  }
39
34
  default:
@@ -64,4 +59,23 @@ export class SolidAddControllerHandlerMcpHandler implements IMcpToolResponseHand
64
59
  return JSON.parse(unescaped);
65
60
  }
66
61
  }
62
+
63
+ private handleAddMethodToExistingClass(step: PlanStep) {
64
+ // Cast step to the appropriate type if necessary
65
+ const addMethodStep = step as AddMethodToExistingClassStep
66
+ // Add the import statement to the specified file
67
+ if (addMethodStep.importStatements && addMethodStep.importStatements.length > 0) {
68
+ this.tsMorph.addImport(
69
+ addMethodStep.path,
70
+ (addMethodStep.importStatements || []).join('\n'),
71
+ );
72
+ }
73
+ // Add the method content to the specified class in the file
74
+ this.tsMorph.addMethodToExistingClass(
75
+ addMethodStep.path,
76
+ addMethodStep.className,
77
+ addMethodStep.methodName,
78
+ addMethodStep.content,
79
+ );
80
+ }
67
81
  }
@@ -0,0 +1,72 @@
1
+ import { Injectable, Logger } from "@nestjs/common";
2
+ import { AiInteraction } from "src/entities/ai-interaction.entity";
3
+ import { IMcpToolResponseHandler, McpComputedProviderResponse, PlanStep } from "../../../interfaces";
4
+ import { SolidTsMorphService } from "src/services/solid-ts-morph.service";
5
+
6
+ const RESTART_TOUCH_FILE = process.env.MCP_RESTART_TOUCH_FILE || "tmp/restart.touch";
7
+
8
+ @Injectable()
9
+ export class SolidCreateCustomFormViewWidgetMcpHandler implements IMcpToolResponseHandler {
10
+ private readonly logger = new Logger(SolidCreateCustomFormViewWidgetMcpHandler.name);
11
+
12
+ constructor(
13
+ private readonly tsMorph: SolidTsMorphService,
14
+
15
+ ) { }
16
+
17
+ async apply(aiInteraction: AiInteraction) {
18
+ const raw = this.safeParse(aiInteraction.message);
19
+ const payload: McpComputedProviderResponse | undefined = (raw?.data?.plan ? raw.data : raw) as McpComputedProviderResponse;
20
+
21
+ if (!payload || !Array.isArray(payload.plan)) {
22
+ throw new Error("SolidCreateCustomFormViewWidgetMcpHandler: invalid MCP response; missing plan[]");
23
+ }
24
+
25
+ // Batch all plan steps in a single txn so nodemon restarts only once.
26
+ this.tsMorph.begin();
27
+ try {
28
+ for (const step of payload.plan as PlanStep[]) {
29
+ switch (step.type) {
30
+ case "createNewFile": {
31
+ const overwrite = step.overwrite ?? false;
32
+ this.tsMorph.createNewFile(step.path, step.content, overwrite);
33
+ break;
34
+ }
35
+ case "registerSolidXExtensionComponent": {
36
+ this.tsMorph.registerSolidUiExtension(
37
+ step.path,
38
+ step.content,
39
+ );
40
+ this.tsMorph.addImport(
41
+ step.path,
42
+ step.importExtensionComponent,
43
+ );
44
+ break;
45
+ }
46
+ default:
47
+ throw new Error(`Unsupported plan step type: ${(step as any).type}`);
48
+ }
49
+ }
50
+
51
+ const result = await this.tsMorph.commit();
52
+
53
+ return {
54
+ seedingRequired: true,
55
+ serverRebooting: false,
56
+ };
57
+ } catch (err) {
58
+ this.logger.error(`Apply failed; rolling back. ${String(err)}`);
59
+ this.tsMorph.rollback();
60
+ throw err;
61
+ }
62
+ }
63
+
64
+ private safeParse(str: string): any {
65
+ try {
66
+ return JSON.parse(str);
67
+ } catch {
68
+ const unescaped = str.replace(/\\'/g, "'");
69
+ return JSON.parse(unescaped);
70
+ }
71
+ }
72
+ }
@@ -111,8 +111,6 @@ export class ListOfValuesMetadataService extends CRUDService<ListOfValues> {
111
111
  await this.deleteFromConfig(metaData, listofvalue, filePath);
112
112
  }
113
113
 
114
-
115
-
116
114
  private async getConfigFileDetails(moduleName: string): Promise<{ filePath: string; metaData: any }> {
117
115
  const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
118
116
  try {
@@ -185,7 +183,6 @@ export class ListOfValuesMetadataService extends CRUDService<ListOfValues> {
185
183
  await fs.writeFile(filePath, updatedContent);
186
184
  }
187
185
 
188
-
189
186
  private async deleteFromConfig(metaData: any, listofvalues: ListOfValues, filePath: string) {
190
187
  const dto = await this.listOfValuesMapper.toDto(listofvalues);
191
188
 
@@ -1045,8 +1045,8 @@ export class ModelMetadataService {
1045
1045
  let view = await viewRepo.findOne({ where: { name: `${model.singularName}-list-view` } });
1046
1046
 
1047
1047
  const actionData = {
1048
- displayName: `${model.displayName} List View`,
1049
- name: `${model.singularName}-list-view`,
1048
+ displayName: `${model.displayName} List Action`,
1049
+ name: `${model.singularName}-list-action`,
1050
1050
  type: "solid",
1051
1051
  domain: "" as any,
1052
1052
  context: "" as any,
@@ -33,7 +33,7 @@ export class ListOfModelsSelectionProvider implements ISelectionProvider<ISelect
33
33
  const models = (await this.modelMetadataService.findMany(queryData)).records;
34
34
 
35
35
  const model = models.filter(i => i.singularName === optionValue)[0];
36
- return { label: model.singularName, value: model.id }
36
+ return { label: model.displayName, value: model.singularName }
37
37
  }
38
38
 
39
39
  async values(query: string, ctxt: ISelectionProviderContext): Promise<readonly ISelectionProviderValues[]> {
@@ -63,7 +63,7 @@ export class ListOfModelsSelectionProvider implements ISelectionProvider<ISelect
63
63
  // });
64
64
 
65
65
  return filteredModels.map(i => {
66
- return { label: i.singularName, value: i.singularName };
66
+ return { label: i.displayName, value: i.singularName };
67
67
  });
68
68
 
69
69
  }
@@ -77,7 +77,8 @@ export class SettingService extends CRUDService<Setting> {
77
77
  defaultProvider: "",
78
78
  availableProviders: []
79
79
  }),
80
- showNameFieldsForRegistration:this.iamConfiguration.showNameFieldsForRegistration
80
+ showNameFieldsForRegistration:this.iamConfiguration.showNameFieldsForRegistration,
81
+ forceChangePasswordOnFirstLogin: this.iamConfiguration.forceChangePasswordOnFirstLogin
81
82
  };
82
83
 
83
84
  const existingSettings = await this.repo.find();
@@ -175,7 +176,7 @@ export class SettingService extends CRUDService<Setting> {
175
176
  shouldQueueSms: this.commonConfiguration.shouldQueueSms,
176
177
  enableDarkMode: true,
177
178
  copyright: null,
178
- forceChangePasswordOnFirstLogin: true,
179
+ forceChangePasswordOnFirstLogin: this.iamConfiguration.forceChangePasswordOnFirstLogin,
179
180
  enableUsername: true,
180
181
  enabledNotification: true,
181
182
  contactSupportEmail : null,
@@ -57,7 +57,8 @@ import { HttpModule } from '@nestjs/axios';
57
57
  import { JwtModule } from '@nestjs/jwt';
58
58
  import { SeedCommand } from './commands/seed.command';
59
59
  import commonConfig from './config/common.config';
60
- import { iamConfig, jwtConfig } from './config/iam.config';
60
+ import { iamConfig } from './config/iam.config';
61
+ import { jwtConfig } from './config/jwt.config';
61
62
  import { AuthenticationController } from './controllers/authentication.controller';
62
63
  import { EmailTemplateController } from './controllers/email-template.controller';
63
64
  import { GoogleAuthenticationController } from './controllers/google-authentication.controller';
@@ -300,6 +301,8 @@ import { SolidAddQuestionToDashboardMcpHandler } from './services/genai/mcp-hand
300
301
  import { SolidAddCustomServiceMethodMcpHandler } from './services/genai/mcp-handlers/solid-add-custom-service-method-mcp-handler.service';
301
302
  import { SolidAddHeaderButtonOrRowButtonToListViewMcpHandler } from './services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service';
302
303
  import { SolidAddControllerHandlerMcpHandler } from './services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service';
304
+ import { SolidAddButtonToFormViewMcpHandler } from './services/genai/mcp-handlers/solid-add-button-to-form-view-mcp-handler.service';
305
+ import { SolidCreateCustomFormViewWidgetMcpHandler } from './services/genai/mcp-handlers/solid-create-custom-form-view-widget-mcp-handler.service';
303
306
 
304
307
 
305
308
  @Global()
@@ -649,6 +652,9 @@ import { SolidAddControllerHandlerMcpHandler } from './services/genai/mcp-handle
649
652
  SolidAddCustomServiceMethodMcpHandler,
650
653
  SolidAddHeaderButtonOrRowButtonToListViewMcpHandler,
651
654
  SolidAddControllerHandlerMcpHandler,
655
+ SolidAddButtonToFormViewMcpHandler,
656
+ SolidCreateCustomFormViewWidgetMcpHandler,
657
+
652
658
  SolidTsMorphService,
653
659
 
654
660
  ViewMetadataRepository,