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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/commands/migrate-removed-fields.command.d.ts +19 -0
  2. package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
  3. package/dist/commands/migrate-removed-fields.command.js +77 -0
  4. package/dist/commands/migrate-removed-fields.command.js.map +1 -0
  5. package/dist/dtos/datasource-introspection-mapping.dto.d.ts +1 -0
  6. package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
  7. package/dist/dtos/datasource-introspection-mapping.dto.js +7 -1
  8. package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
  9. package/dist/helpers/command.service.d.ts +2 -0
  10. package/dist/helpers/command.service.d.ts.map +1 -1
  11. package/dist/helpers/command.service.js +6 -2
  12. package/dist/helpers/command.service.js.map +1 -1
  13. package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
  14. package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
  15. package/dist/helpers/nodemon-heartbeat.js +67 -0
  16. package/dist/helpers/nodemon-heartbeat.js.map +1 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
  22. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  23. package/dist/seeders/module-metadata-seeder.service.js +101 -1
  24. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  25. package/dist/seeders/seed-data/solid-core-metadata.json +109 -31
  26. package/dist/services/api-key.service.d.ts.map +1 -1
  27. package/dist/services/api-key.service.js.map +1 -1
  28. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
  29. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
  30. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
  31. package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
  32. package/dist/services/crud.service.d.ts +1 -0
  33. package/dist/services/crud.service.d.ts.map +1 -1
  34. package/dist/services/crud.service.js +27 -0
  35. package/dist/services/crud.service.js.map +1 -1
  36. package/dist/services/datasource-introspection.service.d.ts +1 -0
  37. package/dist/services/datasource-introspection.service.d.ts.map +1 -1
  38. package/dist/services/datasource-introspection.service.js +27 -16
  39. package/dist/services/datasource-introspection.service.js.map +1 -1
  40. package/dist/services/import-transaction.service.d.ts.map +1 -1
  41. package/dist/services/import-transaction.service.js +3 -0
  42. package/dist/services/import-transaction.service.js.map +1 -1
  43. package/dist/services/model-metadata.service.d.ts +1 -0
  44. package/dist/services/model-metadata.service.d.ts.map +1 -1
  45. package/dist/services/model-metadata.service.js +28 -0
  46. package/dist/services/model-metadata.service.js.map +1 -1
  47. package/dist/services/module-metadata.service.d.ts.map +1 -1
  48. package/dist/services/module-metadata.service.js +27 -0
  49. package/dist/services/module-metadata.service.js.map +1 -1
  50. package/dist/services/removed-field-migration.service.d.ts +30 -0
  51. package/dist/services/removed-field-migration.service.d.ts.map +1 -0
  52. package/dist/services/removed-field-migration.service.js +319 -0
  53. package/dist/services/removed-field-migration.service.js.map +1 -0
  54. package/dist/solid-core.module.d.ts.map +1 -1
  55. package/dist/solid-core.module.js +4 -0
  56. package/dist/solid-core.module.js.map +1 -1
  57. package/dist/subscribers/audit.subscriber.d.ts +4 -1
  58. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  59. package/dist/subscribers/audit.subscriber.js +54 -6
  60. package/dist/subscribers/audit.subscriber.js.map +1 -1
  61. package/dist-tests/api/authenticate.spec.js +119 -0
  62. package/dist-tests/api/authenticate.spec.js.map +1 -0
  63. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
  64. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
  65. package/dist-tests/api/ping.spec.js +21 -0
  66. package/dist-tests/api/ping.spec.js.map +1 -0
  67. package/dist-tests/helpers/auth.js +41 -0
  68. package/dist-tests/helpers/auth.js.map +1 -0
  69. package/dist-tests/helpers/env.js +11 -0
  70. package/dist-tests/helpers/env.js.map +1 -0
  71. package/docs/agent-hub-grooming.md +301 -0
  72. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
  73. package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
  74. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
  75. package/docs/datasource-introspection-ddl-analysis.md +326 -0
  76. package/docs/datasource-introspection-implementation-plan.md +306 -0
  77. package/docs/grouping-enhancements.md +89 -0
  78. package/docs/java-spring/README.md +3 -0
  79. package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
  80. package/docs/module-package-import-handoff.md +691 -0
  81. package/docs/seed-changes.md +65 -0
  82. package/docs/test-data-workflow.md +200 -0
  83. package/docs/type-declaration-import-issue.md +24 -0
  84. package/package.json +1 -1
  85. package/src/commands/migrate-removed-fields.command.ts +81 -0
  86. package/src/dtos/datasource-introspection-mapping.dto.ts +5 -0
  87. package/src/helpers/command.service.ts +10 -2
  88. package/src/helpers/nodemon-heartbeat.ts +59 -0
  89. package/src/index.ts +1 -0
  90. package/src/seeders/module-metadata-seeder.service.ts +118 -0
  91. package/src/seeders/seed-data/solid-core-metadata.json +109 -31
  92. package/src/services/api-key.service.ts +36 -36
  93. package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
  94. package/src/services/crud.service.ts +36 -11
  95. package/src/services/datasource-introspection.service.ts +29 -16
  96. package/src/services/import-transaction.service.ts +5 -0
  97. package/src/services/model-metadata.service.ts +45 -1
  98. package/src/services/module-metadata.service.ts +35 -0
  99. package/src/services/removed-field-migration.service.ts +334 -0
  100. package/src/solid-core.module.ts +4 -0
  101. package/src/subscribers/audit.subscriber.ts +70 -5
  102. package/.claude/settings.local.json +0 -16
  103. package/CLAUDE.md +0 -26
  104. package/src/services/1.js +0 -6
@@ -8746,12 +8746,15 @@
8746
8746
  48
8747
8747
  ],
8748
8748
  "enableGlobalSearch": true,
8749
- "create": true,
8750
- "edit": true,
8749
+ "create": false,
8750
+ "edit": false,
8751
8751
  "delete": true,
8752
+ "import": false,
8753
+ "export": false,
8752
8754
  "allowedViews": [
8753
8755
  "list",
8754
- "card"
8756
+ "card",
8757
+ "tree"
8755
8758
  ]
8756
8759
  },
8757
8760
  "children": [
@@ -9012,7 +9015,13 @@
9012
9015
  "enableGlobalSearch": true,
9013
9016
  "create": false,
9014
9017
  "edit": false,
9015
- "delete": false
9018
+ "delete": false,
9019
+ "import": false,
9020
+ "export": false,
9021
+ "allowedViews": [
9022
+ "list",
9023
+ "tree"
9024
+ ]
9016
9025
  },
9017
9026
  "children": [
9018
9027
  {
@@ -10104,15 +10113,11 @@
10104
10113
  "swimlanesCount": 5,
10105
10114
  "recordsInSwimlane": 10,
10106
10115
  "enableGlobalSearch": true,
10107
- "create": true,
10108
- "edit": true,
10109
- "delete": true,
10116
+ "create": false,
10117
+ "edit": false,
10118
+ "delete": false,
10110
10119
  "groupBy": "stage",
10111
- "draggable": false,
10112
- "allowedViews": [
10113
- "list",
10114
- "kanban"
10115
- ]
10120
+ "draggable": false
10116
10121
  },
10117
10122
  "children": [
10118
10123
  {
@@ -11938,7 +11943,11 @@
11938
11943
  "edit": false,
11939
11944
  "delete": false,
11940
11945
  "import": false,
11941
- "export": false
11946
+ "export": false,
11947
+ "allowedViews": [
11948
+ "list",
11949
+ "tree"
11950
+ ]
11942
11951
  },
11943
11952
  "children": [
11944
11953
  {
@@ -13433,7 +13442,13 @@
13433
13442
  "enableGlobalSearch": true,
13434
13443
  "create": false,
13435
13444
  "edit": true,
13436
- "delete": false
13445
+ "delete": false,
13446
+ "import": false,
13447
+ "export": false,
13448
+ "allowedViews": [
13449
+ "list",
13450
+ "tree"
13451
+ ]
13437
13452
  },
13438
13453
  "children": [
13439
13454
  {
@@ -13508,7 +13523,13 @@
13508
13523
  "enableGlobalSearch": true,
13509
13524
  "create": false,
13510
13525
  "edit": false,
13511
- "delete": false
13526
+ "delete": false,
13527
+ "import": false,
13528
+ "export": false,
13529
+ "allowedViews": [
13530
+ "list",
13531
+ "tree"
13532
+ ]
13512
13533
  },
13513
13534
  "children": [
13514
13535
  {
@@ -13592,7 +13613,13 @@
13592
13613
  "enableGlobalSearch": true,
13593
13614
  "create": false,
13594
13615
  "edit": false,
13595
- "delete": false
13616
+ "delete": false,
13617
+ "import": false,
13618
+ "export": false,
13619
+ "allowedViews": [
13620
+ "list",
13621
+ "tree"
13622
+ ]
13596
13623
  },
13597
13624
  "children": [
13598
13625
  {
@@ -13654,7 +13681,13 @@
13654
13681
  "enableGlobalSearch": true,
13655
13682
  "create": false,
13656
13683
  "edit": false,
13657
- "delete": false
13684
+ "delete": false,
13685
+ "import": false,
13686
+ "export": false,
13687
+ "allowedViews": [
13688
+ "list",
13689
+ "tree"
13690
+ ]
13658
13691
  },
13659
13692
  "children": [
13660
13693
  {
@@ -13723,7 +13756,13 @@
13723
13756
  "enableGlobalSearch": true,
13724
13757
  "create": false,
13725
13758
  "edit": false,
13726
- "delete": false
13759
+ "delete": true,
13760
+ "import": false,
13761
+ "export": false,
13762
+ "allowedViews": [
13763
+ "list",
13764
+ "tree"
13765
+ ]
13727
13766
  },
13728
13767
  "children": [
13729
13768
  {
@@ -13793,7 +13832,14 @@
13793
13832
  "enableGlobalSearch": true,
13794
13833
  "create": false,
13795
13834
  "edit": false,
13796
- "delete": false
13835
+ "delete": true,
13836
+ "import": false,
13837
+ "export": false,
13838
+ "allowedViews": [
13839
+ "list",
13840
+ "card",
13841
+ "tree"
13842
+ ]
13797
13843
  },
13798
13844
  "children": [
13799
13845
  {
@@ -13859,11 +13905,14 @@
13859
13905
  50
13860
13906
  ],
13861
13907
  "enableGlobalSearch": true,
13862
- "create": false,
13863
- "edit": false,
13864
- "delete": false,
13865
- "import": false,
13866
- "export": false
13908
+ "create": true,
13909
+ "edit": true,
13910
+ "delete": true,
13911
+ "import": true,
13912
+ "export": true,
13913
+ "allowedViews": [
13914
+ "list"
13915
+ ]
13867
13916
  },
13868
13917
  "children": [
13869
13918
  {
@@ -13898,7 +13947,11 @@
13898
13947
  "edit": false,
13899
13948
  "delete": false,
13900
13949
  "import": false,
13901
- "export": false
13950
+ "export": false,
13951
+ "allowedViews": [
13952
+ "list",
13953
+ "tree"
13954
+ ]
13902
13955
  },
13903
13956
  "children": [
13904
13957
  {
@@ -13960,7 +14013,11 @@
13960
14013
  "edit": false,
13961
14014
  "delete": false,
13962
14015
  "import": false,
13963
- "export": false
14016
+ "export": false,
14017
+ "allowedViews": [
14018
+ "list",
14019
+ "tree"
14020
+ ]
13964
14021
  },
13965
14022
  "children": [
13966
14023
  {
@@ -14029,7 +14086,11 @@
14029
14086
  "edit": true,
14030
14087
  "delete": true,
14031
14088
  "import": true,
14032
- "export": true
14089
+ "export": true,
14090
+ "allowedViews": [
14091
+ "list",
14092
+ "tree"
14093
+ ]
14033
14094
  },
14034
14095
  "children": [
14035
14096
  {
@@ -14089,7 +14150,11 @@
14089
14150
  "edit": false,
14090
14151
  "delete": false,
14091
14152
  "import": false,
14092
- "export": false
14153
+ "export": false,
14154
+ "allowedViews": [
14155
+ "list",
14156
+ "tree"
14157
+ ]
14093
14158
  },
14094
14159
  "children": [
14095
14160
  {
@@ -14149,7 +14214,11 @@
14149
14214
  "edit": false,
14150
14215
  "delete": false,
14151
14216
  "import": false,
14152
- "export": false
14217
+ "export": false,
14218
+ "allowedViews": [
14219
+ "list",
14220
+ "tree"
14221
+ ]
14153
14222
  },
14154
14223
  "children": [
14155
14224
  {
@@ -14219,7 +14288,11 @@
14219
14288
  "edit": false,
14220
14289
  "delete": false,
14221
14290
  "import": false,
14222
- "export": false
14291
+ "export": false,
14292
+ "allowedViews": [
14293
+ "list",
14294
+ "tree"
14295
+ ]
14223
14296
  },
14224
14297
  "children": [
14225
14298
  {
@@ -14288,7 +14361,12 @@
14288
14361
  "enableGlobalSearch": true,
14289
14362
  "create": true,
14290
14363
  "edit": true,
14291
- "delete": true
14364
+ "delete": true,
14365
+ "import": true,
14366
+ "export": true,
14367
+ "allowedViews": [
14368
+ "list"
14369
+ ]
14292
14370
  },
14293
14371
  "children": [
14294
14372
  {
@@ -26,53 +26,53 @@ export class ApiKeyService {
26
26
  private readonly permissionMetadataService: PermissionMetadataService,
27
27
  private readonly authenticationService: AuthenticationService,
28
28
  private readonly userRepository: UserRepository,
29
- ) {}
29
+ ) { }
30
30
 
31
- async generate(userId: number, dto: CreateApiKeyDto): Promise < { apiKey: string; record: UserApiKey } > {
32
- const user = await this.apiKeyRepository.manager.findOne(User, {
33
- where: { id: userId },
34
- select: ['id', 'isAllowedToGenerateApiKeys'],
35
- });
31
+ async generate(userId: number, dto: CreateApiKeyDto): Promise<{ apiKey: string; record: UserApiKey }> {
32
+ const user = await this.apiKeyRepository.manager.findOne(User, {
33
+ where: { id: userId },
34
+ select: ['id', 'isAllowedToGenerateApiKeys'],
35
+ });
36
36
 
37
- if(!user?.isAllowedToGenerateApiKeys) {
38
- throw new ForbiddenException('You are not allowed to generate API keys');
39
- }
37
+ if (!user?.isAllowedToGenerateApiKeys) {
38
+ throw new ForbiddenException('You are not allowed to generate API keys');
39
+ }
40
40
 
41
41
  const rawKey = 'sldx_' + randomBytes(32).toString('hex');
42
- const hashedKey = this.hash(rawKey);
43
- const maskedKey = 'sldx_****' + rawKey.slice(-4);
44
-
45
- const record = this.apiKeyRepository.create({
46
- name: dto.name,
47
- hashedKey,
48
- maskedKey,
49
- isActive: true,
50
- expiresAt: dto.expiresAt ? new Date(dto.expiresAt) : null,
51
- user,
52
- });
42
+ const hashedKey = this.hash(rawKey);
43
+ const maskedKey = 'sldx_****' + rawKey.slice(-4);
44
+
45
+ const record = this.apiKeyRepository.create({
46
+ name: dto.name,
47
+ hashedKey,
48
+ maskedKey,
49
+ isActive: true,
50
+ expiresAt: dto.expiresAt ? new Date(dto.expiresAt) : null,
51
+ user,
52
+ });
53
53
 
54
- await this.apiKeyRepository.save(record);
54
+ await this.apiKeyRepository.save(record);
55
55
 
56
- // Strip hashedKey from the returned record — maskedKey is all the UI needs
57
- delete(record as any).hashedKey;
56
+ // Strip hashedKey from the returned record — maskedKey is all the UI needs
57
+ delete (record as any).hashedKey;
58
58
 
59
- return { apiKey: rawKey, record };
60
- }
59
+ return { apiKey: rawKey, record };
60
+ }
61
61
 
62
- async validate(rawKey: string): Promise < ActiveUserData > {
63
- const hashedKey = this.hash(rawKey);
62
+ async validate(rawKey: string): Promise<ActiveUserData> {
63
+ const hashedKey = this.hash(rawKey);
64
64
 
65
- // Bypass security rules for auth validation — must find the key regardless of caller context
66
- const keyRecord = await this.apiKeyRepository.findOne({
67
- where: { hashedKey, isActive: true },
68
- relations: ['user', 'user.roles'],
69
- });
65
+ // Bypass security rules for auth validation — must find the key regardless of caller context
66
+ const keyRecord = await this.apiKeyRepository.findOne({
67
+ where: { hashedKey, isActive: true },
68
+ relations: ['user', 'user.roles'],
69
+ });
70
70
 
71
- if(!keyRecord) {
72
- throw new UnauthorizedException();
73
- }
71
+ if (!keyRecord) {
72
+ throw new UnauthorizedException();
73
+ }
74
74
 
75
- if(keyRecord.expiresAt && keyRecord.expiresAt < new Date()) {
75
+ if (keyRecord.expiresAt && keyRecord.expiresAt < new Date()) {
76
76
  throw new UnauthorizedException('API key expired');
77
77
  }
78
78
 
@@ -1,21 +1,29 @@
1
+ import dayjs from "dayjs";
1
2
  import { Injectable } from "@nestjs/common";
2
3
  import { kebabCase, get } from "lodash";
3
4
  import { ComputedFieldProvider } from "src/decorators/computed-field-provider.decorator";
4
5
  import { CommonEntity } from "src/entities/common.entity";
5
6
  import { ComputedFieldMetadata } from "src/helpers/solid-registry";
6
7
  import { IEntityPreComputeFieldProvider } from "src/interfaces";
8
+ import { SettingService } from "src/services/setting.service";
9
+ import type { SolidCoreSetting } from "src/services/settings/default-settings-provider.service";
7
10
 
8
11
 
9
12
  export interface ConcatComputedFieldContext {
10
13
  separator: string; // The separator to use between concatenated values
11
14
  fields: string[]; // The fields to concatenate
12
15
  slugify?: boolean; // Optional: if true, slugify each field value before concatenation
16
+ dateFormat?: string; // Optional: format to use for Date values. Defaults to the configured dateFormat setting
13
17
  }
14
18
 
15
19
  @ComputedFieldProvider()
16
20
  @Injectable()
17
21
  export class ConcatEntityComputedFieldProvider<T extends CommonEntity> implements IEntityPreComputeFieldProvider<T, ConcatComputedFieldContext> {
18
22
 
23
+ constructor(
24
+ private readonly settingService: SettingService,
25
+ ) { }
26
+
19
27
  name(): string {
20
28
  return "ConcatEntityComputedFieldProvider";
21
29
  }
@@ -29,6 +37,8 @@ export class ConcatEntityComputedFieldProvider<T extends CommonEntity> implement
29
37
  const separator = computedFieldValueProviderCtxt.separator ?? ' ';
30
38
  const fields: string[] = computedFieldValueProviderCtxt.fields ?? [];
31
39
  const slugify = computedFieldValueProviderCtxt.slugify ?? false;
40
+ const dateFormat = computedFieldValueProviderCtxt.dateFormat
41
+ ?? this.settingService.getConfigValue<SolidCoreSetting>("dateFormat");
32
42
 
33
43
  const parts: string[] = [];
34
44
 
@@ -38,6 +48,10 @@ export class ConcatEntityComputedFieldProvider<T extends CommonEntity> implement
38
48
  // normalize to string (skip null/undefined)
39
49
  if (val == null) continue;
40
50
 
51
+ if (val instanceof Date) {
52
+ val = dayjs(val).format(dateFormat);
53
+ }
54
+
41
55
  if (typeof val !== 'string') {
42
56
  val = String(val);
43
57
  }
@@ -58,4 +72,4 @@ export class ConcatEntityComputedFieldProvider<T extends CommonEntity> implement
58
72
  (triggerEntity as any)[computedFieldMetadata.fieldName] = concatenatedString;
59
73
  }
60
74
 
61
- }
75
+ }
@@ -34,6 +34,7 @@ import { SelectionDynamicFieldCrudManager } from "../helpers/field-crud-managers
34
34
  import { SelectionStaticFieldCrudManager } from "../helpers/field-crud-managers/SelectionStaticFieldCrudManager";
35
35
  import { ShortTextFieldCrudManager } from "../helpers/field-crud-managers/ShortTextFieldCrudManager";
36
36
  import { UUIDFieldCrudManager } from "../helpers/field-crud-managers/UUIDFieldCrudManager";
37
+ import { ModelMetadataHelperService } from "src/helpers/model-metadata-helper.service";
37
38
  import { FieldCrudManager, MediaWithFullUrl } from "../interfaces";
38
39
  import { CrudHelperService, FilterCombinator, UserIdFields } from "./crud-helper.service";
39
40
  import { HashingService } from "./hashing.service";
@@ -42,6 +43,7 @@ import { getMediaStorageProvider } from "./mediaStorageProviders";
42
43
  import { ModelMetadataService } from "./model-metadata.service";
43
44
  import { RequestContextService } from "./request-context.service";
44
45
 
46
+ const AUDIT_BEFORE_SNAPSHOT = '__auditBeforeSnapshot';
45
47
 
46
48
  export class CRUDService<T extends CommonEntity> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
47
49
 
@@ -185,7 +187,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
185
187
  }));
186
188
  }
187
189
 
188
- //TODO: Will the updates be partial i.e PATCH or full i.e PUT
190
+ //TODO: Will the updates be partial i.e PATCH or full i.e PUT
189
191
  async update(id: number, updateDto: any, files: Express.Multer.File[] = [], isPartialUpdate: boolean = false, solidRequestContext: any = {}, isUpdate: boolean = false): Promise<T> {
190
192
  if (!id) {
191
193
  throw new Error(ERROR_MESSAGES.ID_REQUIRED_FOR_UPDATE);
@@ -213,16 +215,8 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
213
215
  );
214
216
  }
215
217
 
216
- // // In some instances for legacy tables sometimes id is mapped as a bigint.
217
- // // in these cases the update method ends up attempting to insert records due to some type orm type mismatch issue.
218
- // const idFieldMetadata = model.fields.find(f => f.name === 'id');
219
- // updateDto.id = idFieldMetadata?.type === 'bigint' ? BigInt(id) : id;
220
-
221
- // This class will be extended by the generated service class i.e PersonService
222
- // The data required to identify the model and module name will be passed from the generate CrudService subclass
223
- //TODO: Algorithm to create the entity
224
- // 1. Fire a query and load all the fields in the provided model name for a particular module
225
- // FIXME This can be optimized to take in module name i.e (handle scenario wherein same model exists in multiple modules)
218
+ // Capture pre-update many-to-many relation state for audit tracking
219
+ await this.prepareManyToManyAuditSnapshot(entity,id,model.singularName);
226
220
  let hasMediaFields = false;
227
221
 
228
222
  const fieldsToProcess = [...model.fields];
@@ -249,6 +243,37 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
249
243
  return savedEntity;
250
244
  }
251
245
 
246
+ /**
247
+ * Captures the current state of audit-enabled many-to-many relations
248
+ * before the entity is updated. The snapshot is attached to the entity
249
+ * and can be used later for audit comparison.
250
+ */
251
+ private async prepareManyToManyAuditSnapshot(entity: T,id: number,modelSingularName: string): Promise<void> {
252
+ const modelMetadataHelperService = this.moduleRef.get(ModelMetadataHelperService, { strict: false });
253
+ const auditRelationFields = (await modelMetadataHelperService.loadFieldHierarchy(modelSingularName)).filter(field =>
254
+ field.enableAuditTracking &&
255
+ field.type === 'relation' &&
256
+ field.relationType !== 'one-to-many'
257
+ );
258
+ if (auditRelationFields.length > 0) {
259
+ const relations: any = {};
260
+ auditRelationFields.forEach(field => relations[field.name] = true);
261
+ const auditBeforeEntity = await this.repo.findOne({
262
+ where: {
263
+ id: id,
264
+ } as unknown as FindOptionsWhere<T>,
265
+ relations: relations as any,
266
+ });
267
+ if (auditBeforeEntity) {
268
+ Object.defineProperty(entity, AUDIT_BEFORE_SNAPSHOT, {
269
+ configurable: true,
270
+ enumerable: false,
271
+ value: auditBeforeEntity,
272
+ writable: true,
273
+ });
274
+ }
275
+ }
276
+ }
252
277
  //TODO: Will the updates be partial i.e PATCH or full i.e PUT
253
278
  async delete(id: number, solidRequestContext: any = {}) {
254
279
  if (!id) {
@@ -157,7 +157,7 @@ const DATASOURCE_TYPE_FAMILY_ALIASES: Record<DatasourceTypeFamily, string[]> = {
157
157
  int: ["int", "integer", "smallint", "tinyint"],
158
158
  numeric: ["numeric"],
159
159
  decimal: ["decimal", "money", "smallmoney"],
160
- float: ["float", "double", "real"],
160
+ float: ["float", "double", "double precision", "real", "float4", "float8"],
161
161
  date: ["date"],
162
162
  time: ["time", "time without time zone", "time with time zone"],
163
163
  datetime: ["datetime", "datetime2", "smalldatetime", "timestamp", "timestamp without time zone"],
@@ -818,6 +818,14 @@ export class DatasourceIntrospectionService {
818
818
  return rest;
819
819
  }
820
820
 
821
+ private sanitizeConfiguredFieldPayload(fieldConfig: Record<string, any> | undefined) {
822
+ if (!fieldConfig || typeof fieldConfig !== "object" || Array.isArray(fieldConfig)) {
823
+ return {};
824
+ }
825
+
826
+ return this.sanitizeExistingFieldPayload(fieldConfig);
827
+ }
828
+
821
829
  private normalizeReviewedMetadataModel(
822
830
  reviewedModel: Record<string, any> | undefined,
823
831
  mappingDto: DatasourceIntrospectionMappingDto,
@@ -972,34 +980,39 @@ export class DatasourceIntrospectionService {
972
980
  existingField?: any,
973
981
  ) {
974
982
  const preserved = existingField ? this.sanitizeExistingFieldPayload(existingField) : {};
983
+ const configured = this.sanitizeConfiguredFieldPayload(column.fieldConfig);
984
+ const merged = {
985
+ ...preserved,
986
+ ...configured,
987
+ };
975
988
  const maxLength =
976
989
  column.characterMaximumLength && ["shortText", "longText"].includes(column.solidFieldType)
977
990
  ? column.characterMaximumLength
978
- : preserved.max;
991
+ : merged.max;
979
992
 
980
993
  return {
981
- ...preserved,
994
+ ...merged,
982
995
  ...(column.fieldId ? { id: column.fieldId } : {}),
983
996
  name: column.fieldName,
984
- displayName: column.displayName?.trim() || preserved.displayName || startCase(column.fieldName),
985
- description: preserved.description ?? "",
997
+ displayName: column.displayName?.trim() || merged.displayName || startCase(column.fieldName),
998
+ description: merged.description ?? "",
986
999
  type: column.solidFieldType,
987
1000
  ormType: column.ormType,
988
- length: preserved.length ?? column.characterMaximumLength ?? undefined,
989
- defaultValue: preserved.defaultValue ?? column.defaultValue ?? undefined,
990
- required: preserved.required ?? (!column.nullable || column.isPrimaryKey),
991
- unique: preserved.unique ?? false,
992
- encrypt: preserved.encrypt ?? false,
993
- index: preserved.index ?? false,
1001
+ length: merged.length ?? column.characterMaximumLength ?? undefined,
1002
+ defaultValue: merged.defaultValue ?? column.defaultValue ?? undefined,
1003
+ required: merged.required ?? (!column.nullable || column.isPrimaryKey),
1004
+ unique: merged.unique ?? false,
1005
+ encrypt: merged.encrypt ?? false,
1006
+ index: merged.index ?? false,
994
1007
  max: maxLength ?? undefined,
995
- min: preserved.min ?? undefined,
996
- private: preserved.private ?? false,
997
- isSystem: preserved.isSystem ?? false,
1008
+ min: merged.min ?? undefined,
1009
+ private: merged.private ?? false,
1010
+ isSystem: merged.isSystem ?? false,
998
1011
  isMarkedForRemoval: false,
999
1012
  columnName: column.columnName,
1000
1013
  isUserKey: mappingDto.userKeyField === column.fieldName,
1001
- enableAuditTracking: preserved.enableAuditTracking ?? mappingDto.enableAuditTracking,
1002
- isMultiSelect: preserved.isMultiSelect ?? false,
1014
+ enableAuditTracking: merged.enableAuditTracking ?? mappingDto.enableAuditTracking,
1015
+ isMultiSelect: merged.isMultiSelect ?? false,
1003
1016
  isPrimaryKey: column.isPrimaryKey,
1004
1017
  };
1005
1018
  }
@@ -204,6 +204,11 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
204
204
 
205
205
  // Iterate through the fields and populate the standard instructions
206
206
  for (const field of allFields) {
207
+
208
+ // Skip Computed fields
209
+ if (field.type === SolidFieldType.computed) {
210
+ continue;
211
+ }
207
212
  // Skip system fields
208
213
  if (systemFieldNames.includes(field.name)) {
209
214
  continue;
@@ -27,6 +27,7 @@ import { PermissionMetadata } from '../entities/permission-metadata.entity';
27
27
  import { RoleMetadata } from '../entities/role-metadata.entity';
28
28
  import { ViewMetadata } from '../entities/view-metadata.entity';
29
29
  import { CommandService } from '../helpers/command.service';
30
+ import { startNodemonHeartbeat } from '../helpers/nodemon-heartbeat';
30
31
  import {
31
32
  REFRESH_MODEL_COMMAND,
32
33
  REMOVE_FIELDS_COMMAND,
@@ -967,6 +968,26 @@ export class ModelMetadataService {
967
968
  @DisallowInProduction()
968
969
  async generateCodeViaCtl(modelId: number): Promise<string> {
969
970
  const model = await this.findOne(modelId);
971
+ const isEmbedded = process.env.DEFAULT_DATABASE_DRIVER === 'pglite';
972
+
973
+ // When using an embedded PGlite database, the single-connection limit means
974
+ // a spawned `solid refresh-model` subprocess cannot get its own DB connection
975
+ // while the API server is already holding it. Fall back to in-process code
976
+ // generation (which reuses the existing connection).
977
+ if (isEmbedded) {
978
+ this.logger.log('Embedded database detected — generating API code in-process');
979
+ // Prevent nodemon from restarting the server while schematics write files.
980
+ const heartbeat = startNodemonHeartbeat(path.join(process.cwd(), 'src'));
981
+ try {
982
+ return await this.handleGenerateCode({
983
+ modelUserKey: model.singularName,
984
+ dryRun: false,
985
+ });
986
+ } finally {
987
+ heartbeat.stop();
988
+ }
989
+ }
990
+
970
991
  return this.commandService.executeCommandWithArgs({
971
992
  command: 'npx',
972
993
  args: ['@solidxai/solidctl@latest', 'generate', 'model', `--name=${model.singularName}`],
@@ -1478,7 +1499,7 @@ export class ModelMetadataService {
1478
1499
  //Filter out the fields by id
1479
1500
  const fieldsForRemoval = model.fields.filter((field) => options.fieldIdsForRemoval.includes(+field.id));
1480
1501
  const removeOutput = await this.executeRemoveFieldsCommand(model, fieldsForRemoval, options.dryRun);
1481
-
1502
+ const migrationOutput = await this.executeMigrationRemoveFieldsCommand(model, fieldsForRemoval, options.dryRun);
1482
1503
  // Remove the fields from the database as well. This also checks, if the field is marked for removal
1483
1504
  for (const field of fieldsForRemoval) {
1484
1505
  if (field.isMarkedForRemoval) {
@@ -1515,6 +1536,13 @@ export class ModelMetadataService {
1515
1536
 
1516
1537
  return removeOutput;
1517
1538
  }
1539
+ // model-metadata.service.ts mein naya method add karo
1540
+ // async executeRemoveFieldsWithModel(model: ModelMetadata, fieldNames: string[], dryRun = false,) {
1541
+ // const fieldsForRemoval = model.fields.filter(
1542
+ // field => fieldNames.includes(field.name)
1543
+ // );
1544
+ // return this.executeRemoveFieldsCommand(model, fieldsForRemoval, dryRun);
1545
+ // }
1518
1546
 
1519
1547
  async generateModelCode(options: CodeGenerationOptions): Promise<string> {
1520
1548
  if (!options.modelId && !options.modelUserKey) {
@@ -1563,6 +1591,22 @@ export class ModelMetadataService {
1563
1591
  return output;
1564
1592
  }
1565
1593
 
1594
+ private async executeMigrationRemoveFieldsCommand(model: ModelMetadata, fieldsForRemoval: FieldMetadata[], dryRun: boolean = false,): Promise<string> {
1595
+ if (!fieldsForRemoval || fieldsForRemoval.length === 0) {
1596
+ return "";
1597
+ }
1598
+
1599
+ const output = await this.commandService.executeCommandWithArgs({
1600
+ command: 'npx',
1601
+ args: ['@solidxai/solidctl@latest', 'migration', '-n', model.singularName, 'remove-field', ...(dryRun ? [] : ['--apply'])],
1602
+ cwd: path.join(process.cwd(), '..'),
1603
+ });
1604
+
1605
+ this.logger.debug(`Schematic output : ${output}`);
1606
+ return output;
1607
+ }
1608
+
1609
+
1566
1610
  async updateUserKey(data: any) {
1567
1611
  const { modelName, fieldName } = data;
1568
1612