@solidstarters/solid-core 1.2.185 → 1.2.187

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 (48) hide show
  1. package/dist/dtos/selection-dynamic-query.dto.d.ts +1 -0
  2. package/dist/dtos/selection-dynamic-query.dto.d.ts.map +1 -1
  3. package/dist/dtos/selection-dynamic-query.dto.js +6 -1
  4. package/dist/dtos/selection-dynamic-query.dto.js.map +1 -1
  5. package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -1
  6. package/dist/helpers/module-metadata-helper.service.js +8 -3
  7. package/dist/helpers/module-metadata-helper.service.js.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/interfaces.d.ts +17 -1
  13. package/dist/interfaces.d.ts.map +1 -1
  14. package/dist/interfaces.js.map +1 -1
  15. package/dist/services/field-metadata.service.d.ts.map +1 -1
  16. package/dist/services/field-metadata.service.js +10 -0
  17. package/dist/services/field-metadata.service.js.map +1 -1
  18. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.d.ts +16 -0
  19. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.d.ts.map +1 -0
  20. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.js +73 -0
  21. package/dist/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.js.map +1 -0
  22. package/dist/services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service.d.ts +16 -0
  23. package/dist/services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service.d.ts.map +1 -0
  24. package/dist/services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service.js +151 -0
  25. package/dist/services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service.js.map +1 -0
  26. package/dist/services/model-metadata.service.d.ts.map +1 -1
  27. package/dist/services/model-metadata.service.js +10 -8
  28. package/dist/services/model-metadata.service.js.map +1 -1
  29. package/dist/services/solid-ts-morph.service.d.ts +10 -0
  30. package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
  31. package/dist/services/solid-ts-morph.service.js +47 -0
  32. package/dist/services/solid-ts-morph.service.js.map +1 -1
  33. package/dist/solid-core.module.d.ts.map +1 -1
  34. package/dist/solid-core.module.js +4 -0
  35. package/dist/solid-core.module.js.map +1 -1
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +1 -1
  38. package/src/dtos/selection-dynamic-query.dto.ts +4 -0
  39. package/src/helpers/module-metadata-helper.service.ts +10 -3
  40. package/src/index.ts +1 -0
  41. package/src/interfaces.ts +17 -1
  42. package/src/services/field-metadata.service.ts +10 -0
  43. package/src/services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service.ts +67 -0
  44. package/src/services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service.ts +141 -0
  45. package/src/services/model-metadata.service.ts +21 -19
  46. package/src/services/solid-ts-morph.service.ts +75 -0
  47. package/src/solid-core.module.ts +4 -0
  48. package/src/controllers/user.controller.ts.bkp +0 -78
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.185",
3
+ "version": "1.2.187",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,4 +26,8 @@ export class SelectionDynamicQueryDto extends PaginationQueryDto {
26
26
  @IsString()
27
27
  @IsOptional()
28
28
  optionValue?: string = '';
29
+
30
+ @ApiProperty({ description: "Form Values", type: Object })
31
+ @IsOptional()
32
+ formValues: Record<string, any>;
29
33
  }
@@ -16,8 +16,15 @@ export class ModuleMetadataHelperService {
16
16
  // }
17
17
 
18
18
  async getModuleMetadataConfiguration(configFilePath: string): Promise<any> {
19
- const fileContent = await fs.readFile(configFilePath, 'utf8');
20
- return JSON.parse(fileContent);
19
+ try {
20
+ const fileContent = await fs.readFile(configFilePath, 'utf8');
21
+ return JSON.parse(fileContent);
22
+ }
23
+ catch (error) {
24
+ this.logger.error(`module metadata configuration non existent at: ${configFilePath}`);
25
+ }
26
+
27
+ return null;
21
28
  }
22
29
 
23
30
  async getModulePath(moduleName: string): Promise<string> {
@@ -30,7 +37,7 @@ export class ModuleMetadataHelperService {
30
37
  const filePath = path.join(folderPath, `${dashModuleName}-metadata.json`);
31
38
  // Check if filePath exists
32
39
  const fileExists = await this.fileService.fileExists(filePath);
33
- this.logger.debug(`File exists: ${fileExists} at ${filePath}`);
40
+ // this.logger.debug(`File exists: ${fileExists} at ${filePath}`);
34
41
  if (!fileExists) {
35
42
  // If the module is solid-core, try the fallback path, in case the current root directory is the solid core project
36
43
  if (dashModuleName === SOLID_CORE_MODULE_NAME) {
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export * from './decorators/solid-database-module.decorator'
24
24
  export * from './decorators/solid-service.decorator'
25
25
  export * from './decorators/mail-provider.decorator'
26
26
 
27
+ export * from './dtos/post-chatter-message.dto'
27
28
  export * from './dtos/basic-filters.dto'
28
29
  export * from './dtos/solid-request-context.dto'
29
30
  export * from './dtos/change-password.dto'
package/src/interfaces.ts CHANGED
@@ -83,6 +83,9 @@ export interface McpResponse {
83
83
  }
84
84
 
85
85
  export interface ISelectionProviderContext {
86
+ limit: number;
87
+ offset: number;
88
+ formValues: Record<string, any>;
86
89
  // query: string;
87
90
  }
88
91
 
@@ -278,7 +281,7 @@ export interface IErrorCodeProvider {
278
281
 
279
282
  // MCP Tool Related
280
283
 
281
- export type PlanStep = CreateNewFileStep | RegisterNestProviderStep | AddMethodToExistingClassStep;
284
+ export type PlanStep = CreateNewFileStep | RegisterNestProviderStep | AddMethodToExistingClassStep | RegisterSolidXExtensionComponentStep | AddListViewButtonStep;
282
285
 
283
286
  export interface CreateNewFileStep {
284
287
  type: "createNewFile";
@@ -307,7 +310,20 @@ export interface AddMethodToExistingClassStep {
307
310
  rationale?: string; // optional, ignored by executor
308
311
  }
309
312
 
313
+ export interface RegisterSolidXExtensionComponentStep {
314
+ type: "registerSolidXExtensionComponent";
315
+ path: string; // e.g. apps/api/src/address-master/services/address-master.service.ts
316
+ content?: string; // Code
317
+ importExtensionComponent?: string;
318
+ }
310
319
 
320
+ export interface AddListViewButtonStep {
321
+ type: "addListViewButton";
322
+ moduleName?: string;
323
+ modelName?: string;
324
+ buttonType?: string;
325
+ content?: string; // Code
326
+ }
311
327
 
312
328
  export interface McpComputedProviderResponse {
313
329
  plan: PlanStep[];
@@ -15,6 +15,7 @@ import { ModelMetadata } from '../entities/model-metadata.entity';
15
15
  import { ISelectionProviderValues } from '../interfaces';
16
16
  import { CrudHelperService } from './crud-helper.service';
17
17
  import { ERROR_MESSAGES } from 'src/constants/error-messages';
18
+ import qs from 'qs';
18
19
 
19
20
 
20
21
  @Injectable()
@@ -1046,6 +1047,11 @@ export class FieldMetadataService implements OnApplicationBootstrap {
1046
1047
  const selectionDynamicProviderCtxt = JSON.parse(entity.selectionDynamicProviderCtxt ? entity.selectionDynamicProviderCtxt : '{}');
1047
1048
  selectionDynamicProviderCtxt['limit'] = query.limit;
1048
1049
  selectionDynamicProviderCtxt['offset'] = query.offset;
1050
+ const formValues = query.formValues || {};
1051
+ // Parse the form values if they are in string format using qs
1052
+ const parsedFormValues = typeof formValues === 'string' ? qs.parse(formValues, { allowDots: true }) : formValues;
1053
+ selectionDynamicProviderCtxt['formValues'] = parsedFormValues;
1054
+
1049
1055
 
1050
1056
  // 3. get hold of the provider instance from the SolidRegistry
1051
1057
  const selectionProviderInstance = this.solidRegistry.getSelectionProviderInstance(selectionDynamicProvider);
@@ -1073,6 +1079,10 @@ export class FieldMetadataService implements OnApplicationBootstrap {
1073
1079
  const selectionDynamicProviderCtxt = JSON.parse(entity.selectionDynamicProviderCtxt ? entity.selectionDynamicProviderCtxt : '{}');
1074
1080
  selectionDynamicProviderCtxt['limit'] = query.limit;
1075
1081
  selectionDynamicProviderCtxt['offset'] = query.offset;
1082
+ const formValues = query.formValues || {};
1083
+ // Parse the form values if they are in string format using qs
1084
+ const parsedFormValues = typeof formValues === 'string' ? qs.parse(formValues, { allowDots: true }) : formValues;
1085
+ selectionDynamicProviderCtxt['formValues'] = parsedFormValues;
1076
1086
 
1077
1087
  // 3. get hold of the provider instance from the SolidRegistry
1078
1088
  const selectionProviderInstance = this.solidRegistry.getSelectionProviderInstance(selectionDynamicProvider);
@@ -0,0 +1,67 @@
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
+ @Injectable()
7
+ export class SolidAddControllerHandlerMcpHandler implements IMcpToolResponseHandler {
8
+ private readonly logger = new Logger(SolidAddControllerHandlerMcpHandler.name);
9
+
10
+ constructor(private readonly tsMorph: SolidTsMorphService) { }
11
+
12
+ async apply(aiInteraction: AiInteraction) {
13
+ const raw = this.safeParse(aiInteraction.message);
14
+ const payload: McpComputedProviderResponse | undefined = (raw?.data?.plan ? raw.data : raw) as McpComputedProviderResponse;
15
+
16
+ if (!payload || !Array.isArray(payload.plan)) {
17
+ throw new Error("SolidAddControllerHandlerMethodMcpHandler: invalid MCP response; missing plan[]");
18
+ }
19
+
20
+ // Batch all plan steps in a single txn so nodemon restarts only once.
21
+ this.tsMorph.begin();
22
+ try {
23
+ for (const step of payload.plan as PlanStep[]) {
24
+ switch (step.type) {
25
+ case "createNewFile": {
26
+ const overwrite = step.overwrite ?? false;
27
+ this.tsMorph.createNewFile(step.path, step.content, overwrite);
28
+ break;
29
+ }
30
+ case "addMethodToExistingClass": {
31
+ this.tsMorph.addMethodToExistingClass(
32
+ step.path,
33
+ step.className,
34
+ step.methodName,
35
+ step.content,
36
+ );
37
+ break;
38
+ }
39
+ default:
40
+ throw new Error(`Unsupported plan step type: ${(step as any).type}`);
41
+ }
42
+ }
43
+
44
+ const result = await this.tsMorph.commit();
45
+
46
+ return {
47
+ seedingRequired: true,
48
+ serverRebooting: true,
49
+ appliedSteps: payload.plan.length,
50
+ wroteFiles: result.wrote,
51
+ };
52
+ } catch (err) {
53
+ this.logger.error(`Apply failed; rolling back. ${String(err)}`);
54
+ this.tsMorph.rollback();
55
+ throw err;
56
+ }
57
+ }
58
+
59
+ private safeParse(str: string): any {
60
+ try {
61
+ return JSON.parse(str);
62
+ } catch {
63
+ const unescaped = str.replace(/\\'/g, "'");
64
+ return JSON.parse(unescaped);
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,141 @@
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 SolidAddHeaderButtonOrRowButtonToListViewMcpHandler implements IMcpToolResponseHandler {
12
+ private readonly logger = new Logger(SolidAddHeaderButtonOrRowButtonToListViewMcpHandler.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("SolidAddHeaderButtonOrRowButtonToListViewMcpHandler: 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 "addListViewButton": {
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 === "list" &&
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
+
88
+ let buttonContent = step.content;
89
+
90
+ // Parse only if it’s a string
91
+ if (typeof buttonContent === "string") {
92
+ try {
93
+ buttonContent = JSON.parse(buttonContent);
94
+ } catch (err) {
95
+ this.logger.error("❌ Failed to parse step.content JSON:", err);
96
+ return;
97
+ }
98
+ }
99
+
100
+
101
+ // Push the new button content
102
+ view.layout.attrs[step.buttonType].push(buttonContent);
103
+
104
+ console.log(`✅ Added ${step.buttonType} to view: ${view.name}`);
105
+ } else {
106
+ console.warn(`⚠️ No matching list view found for module=${step.moduleName} and model=${step.modelName}`);
107
+ }
108
+
109
+ const updatedContent = JSON.stringify(metaData, null, 2);
110
+ await fs.writeFile(filePath, updatedContent);
111
+ this.logger.log(`Updated list view in ${filePath}`);
112
+
113
+ break;
114
+ }
115
+ default:
116
+ throw new Error(`Unsupported plan step type: ${(step as any).type}`);
117
+ }
118
+ }
119
+
120
+ const result = await this.tsMorph.commit();
121
+
122
+ return {
123
+ seedingRequired: true,
124
+ serverRebooting: false,
125
+ };
126
+ } catch (err) {
127
+ this.logger.error(`Apply failed; rolling back. ${String(err)}`);
128
+ this.tsMorph.rollback();
129
+ throw err;
130
+ }
131
+ }
132
+
133
+ private safeParse(str: string): any {
134
+ try {
135
+ return JSON.parse(str);
136
+ } catch {
137
+ const unescaped = str.replace(/\\'/g, "'");
138
+ return JSON.parse(unescaped);
139
+ }
140
+ }
141
+ }
@@ -657,28 +657,30 @@ export class ModelMetadataService {
657
657
  // <moduleName>-metadata.json | Remove references to this model in the model metadata, menu, action & view sections. | Automatic
658
658
  const filePath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(modelEntity.module.name);
659
659
  const metaData = await this.moduleMetadataHelperService.getModuleMetadataConfiguration(filePath);
660
- const existingModelIndex = metaData.moduleMetadata.models.findIndex(
661
- (existingModel: any) => existingModel.singularName === modelEntity.singularName
662
- );
660
+ if (metaData) {
661
+ const existingModelIndex = metaData.moduleMetadata.models.findIndex(
662
+ (existingModel: any) => existingModel.singularName === modelEntity.singularName
663
+ );
663
664
 
664
- // Remove the model to be deleted from the metadata
665
- if (existingModelIndex !== -1) {
666
- metaData.moduleMetadata.models.splice(existingModelIndex, 1);
667
- }
665
+ // Remove the model to be deleted from the metadata
666
+ if (existingModelIndex !== -1) {
667
+ metaData.moduleMetadata.models.splice(existingModelIndex, 1);
668
+ }
668
669
 
669
- // Remove references to this model in the menu, action & view sections.
670
- metaData.moduleMetadata.menus = metaData.moduleMetadata.menus.filter(
671
- (menu: any) => menu.modelUserKey !== modelEntity.singularName
672
- );
673
- metaData.moduleMetadata.actions = metaData.moduleMetadata.actions.filter(
674
- (action: any) => action.modelUserKey !== modelEntity.singularName
675
- );
676
- metaData.moduleMetadata.views = metaData.moduleMetadata.views.filter(
677
- (view: any) => view.modelUserKey !== modelEntity.singularName
678
- );
670
+ // Remove references to this model in the menu, action & view sections.
671
+ metaData.moduleMetadata.menus = metaData.moduleMetadata.menus.filter(
672
+ (menu: any) => menu.modelUserKey !== modelEntity.singularName
673
+ );
674
+ metaData.moduleMetadata.actions = metaData.moduleMetadata.actions.filter(
675
+ (action: any) => action.modelUserKey !== modelEntity.singularName
676
+ );
677
+ metaData.moduleMetadata.views = metaData.moduleMetadata.views.filter(
678
+ (view: any) => view.modelUserKey !== modelEntity.singularName
679
+ );
679
680
 
680
- const updatedContent = JSON.stringify(metaData, null, 2);
681
- await fs.writeFile(filePath, updatedContent);
681
+ const updatedContent = JSON.stringify(metaData, null, 2);
682
+ await fs.writeFile(filePath, updatedContent);
683
+ }
682
684
 
683
685
  // <moduleName>.module.ts | Remove all references and imports of the above files. | Manual (X)
684
686
  // const moduleFilePath = path.resolve(modulePath, `${dasherize(modelEntity.module.name)}.module.ts`);
@@ -297,4 +297,79 @@ export class SolidTsMorphService {
297
297
  return { staged: true, overwritten: !!existingMethod, skipped: false };
298
298
  }
299
299
 
300
+ registerSolidUiExtension(
301
+ filePath: string,
302
+ lineToAdd: string
303
+ ): { staged: boolean; overwritten: boolean; skipped: boolean } {
304
+ const abs = this.resolveRepoPath(filePath);
305
+ if (!existsSync(abs))
306
+ throw new Error(`registerSolidUiExtension: File not found at ${filePath}`);
307
+
308
+ const fileContent = readFileSync(abs, "utf8");
309
+
310
+ // Check if the line already exists (avoid duplicates)
311
+ if (fileContent.includes(lineToAdd.trim())) {
312
+ this.logger.log(`Skipped adding line (already exists): ${lineToAdd}`);
313
+ return { staged: false, overwritten: false, skipped: true };
314
+ }
315
+
316
+ // Append the new line at the end, ensuring newline
317
+ const newContent = fileContent.trimEnd() + "\n" + lineToAdd.trim() + "\n";
318
+
319
+ // Write updated content back
320
+ writeFileSync(abs, newContent, "utf8");
321
+
322
+ this.dirtySourceFiles.add(abs);
323
+ this.logger.log(`Staged new line in ${this.rel(abs)}: ${lineToAdd}`);
324
+ return { staged: true, overwritten: false, skipped: false };
325
+ }
326
+
327
+ addImport(
328
+ filePath: string,
329
+ importLine: string
330
+ ): { staged: boolean; overwritten: boolean; skipped: boolean } {
331
+ const abs = this.resolveRepoPath(filePath);
332
+ if (!existsSync(abs))
333
+ throw new Error(`addImport: File not found at ${filePath}`);
334
+
335
+ let fileContent = readFileSync(abs, "utf8");
336
+
337
+ // If import already exists — skip
338
+ if (fileContent.includes(importLine.trim())) {
339
+ this.logger.log(`Skipped adding import (already exists): ${importLine}`);
340
+ return { staged: false, overwritten: false, skipped: true };
341
+ }
342
+
343
+ // Find last import statement (so we can insert after all imports)
344
+ const importRegex = /^import .+ from .+;$/gm;
345
+ let lastImportMatch: RegExpExecArray | null;
346
+ let lastImportIndex = -1;
347
+
348
+ while ((lastImportMatch = importRegex.exec(fileContent)) !== null) {
349
+ lastImportIndex = lastImportMatch.index + lastImportMatch[0].length;
350
+ }
351
+
352
+ // Insert after last import (or at top if none exist)
353
+ let newContent: string;
354
+ if (lastImportIndex !== -1) {
355
+ newContent =
356
+ fileContent.slice(0, lastImportIndex) +
357
+ "\n" +
358
+ importLine.trim() +
359
+ "\n" +
360
+ fileContent.slice(lastImportIndex);
361
+ } else {
362
+ // No imports found — insert at top
363
+ newContent = importLine.trim() + "\n\n" + fileContent;
364
+ }
365
+
366
+ writeFileSync(abs, newContent, "utf8");
367
+
368
+ this.dirtySourceFiles.add(abs);
369
+ this.logger.log(`Staged import in ${this.rel(abs)}: ${importLine}`);
370
+ return { staged: true, overwritten: false, skipped: false };
371
+ }
372
+
373
+
374
+
300
375
  }
@@ -298,6 +298,8 @@ import { SolidTsMorphService } from './services/solid-ts-morph.service';
298
298
  import { SolidAddVariableToDashboardMcpHandler } from './services/genai/mcp-handlers/solid-add-variable-to-dashboard-mcp-handler.service';
299
299
  import { SolidAddQuestionToDashboardMcpHandler } from './services/genai/mcp-handlers/solid-add-question-to-dashboard-mcp-handler.service';
300
300
  import { SolidAddCustomServiceMethodMcpHandler } from './services/genai/mcp-handlers/solid-add-custom-service-method-mcp-handler.service';
301
+ import { SolidAddHeaderButtonOrRowButtonToListViewMcpHandler } from './services/genai/mcp-handlers/solid-add-header-button-or-row-button-to-list-view-mcp-handler.service';
302
+ import { SolidAddControllerHandlerMcpHandler } from './services/genai/mcp-handlers/solid-add-controller-handler-method-mcp-handler.service';
301
303
 
302
304
 
303
305
  @Global()
@@ -645,6 +647,8 @@ import { SolidAddCustomServiceMethodMcpHandler } from './services/genai/mcp-hand
645
647
  SolidAddQuestionToDashboardMcpHandler,
646
648
 
647
649
  SolidAddCustomServiceMethodMcpHandler,
650
+ SolidAddHeaderButtonOrRowButtonToListViewMcpHandler,
651
+ SolidAddControllerHandlerMcpHandler,
648
652
  SolidTsMorphService,
649
653
 
650
654
  ViewMetadataRepository,
@@ -1,78 +0,0 @@
1
- import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common';
2
- import { ApiBearerAuth, ApiForbiddenResponse, ApiQuery, ApiTags } from '@nestjs/swagger';
3
- import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
4
- import { PaginationQueryDto } from 'src/dtos/pagination-query.dto';
5
- import { ActiveUser } from '../decorators/active-user.decorator';
6
- import { UserService } from '../services/user.service';
7
- import { MutateUserRolesDto } from '../dtos/mutate-user-roles.dto';
8
- import { MutateUserRolesBulkDto } from '../dtos/mutate-user-roles-list.dto';
9
- import { BasicFilterDto } from 'src/dtos/basic-filters.dto';
10
-
11
- @Controller('users')
12
- @ApiTags("Iam")
13
- export class UserController {
14
- constructor(
15
- private readonly userService: UserService
16
- ) { }
17
-
18
- // /api/users
19
- @ApiBearerAuth("jwt")
20
- @Get()
21
- @ApiBearerAuth("jwt")
22
- @ApiQuery({ name: 'limit', required: false, type: Number })
23
- @ApiQuery({ name: 'offset', required: false, type: Number })
24
- @ApiQuery({ name: 'fields', required: false, type: Array })
25
- @ApiQuery({ name: 'sort', required: false, type: Array })
26
- @ApiQuery({ name: 'groupBy', required: false, type: Array })
27
- @ApiQuery({ name: 'populate', required: false, type: Array })
28
- @ApiQuery({ name: 'populateMedia', required: false, type: Array })
29
- @ApiQuery({ name: 'filters', required: false, type: Array })
30
- async findMany(
31
- @Query() basicFilterDto: BasicFilterDto,
32
- @ActiveUser() activeUser: ActiveUserData,
33
- ) {
34
- return this.userService.findMany(basicFilterDto);
35
- }
36
-
37
- // /api/users/:id
38
- @ApiBearerAuth("jwt")
39
- @ApiForbiddenResponse({ description: 'Forbidden.' })
40
- @Get(':id')
41
- findOne(@Param('id', ParseIntPipe) id: number) {
42
- return this.userService.findOne(id);
43
- }
44
-
45
- @ApiBearerAuth("jwt")
46
- @ApiForbiddenResponse({ description: 'Forbidden.' })
47
- @Post('roles')
48
- addRoleToUser(@Body() mutateUserRoles: MutateUserRolesDto) {
49
- return this.userService.addRoleToUser(mutateUserRoles.username, mutateUserRoles.roleName);
50
- }
51
-
52
- @ApiBearerAuth("jwt")
53
- @ApiForbiddenResponse({ description: 'Forbidden.' })
54
- @Post('roles/bulk')
55
- addRolesToUser(@Body() mutateUserRolesBulk: MutateUserRolesBulkDto) {
56
- return this.userService.addRolesToUser(mutateUserRolesBulk.username, mutateUserRolesBulk.roleNames);
57
- }
58
-
59
- @ApiBearerAuth("jwt")
60
- @ApiForbiddenResponse({ description: 'Forbidden.' })
61
- @Delete('roles')
62
- removeRoleFromUser(userEmail: string, roleName: string) {
63
- return this.userService.removeRoleFromUser(userEmail, roleName);
64
- }
65
-
66
- @ApiBearerAuth("jwt")
67
- @Delete('/bulk')
68
- async deleteMany(@Body() ids: number[]) {
69
- return this.userService.deleteMany(ids);
70
- }
71
-
72
-
73
- @ApiBearerAuth("jwt")
74
- @Delete(':id')
75
- remove(@Param('id') id: number) {
76
- return this.userService.remove(id);
77
- }
78
- }