@solidxai/core 0.1.4 → 0.1.5-beta.0

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 (209) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/dist/constants/error-messages.d.ts +1 -0
  3. package/dist/constants/error-messages.d.ts.map +1 -1
  4. package/dist/constants/error-messages.js +1 -0
  5. package/dist/constants/error-messages.js.map +1 -1
  6. package/dist/constants.d.ts +3 -3
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +12 -12
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controllers/otp-authentication.controller.d.ts +1 -4
  11. package/dist/controllers/otp-authentication.controller.d.ts.map +1 -1
  12. package/dist/controllers/otp-authentication.controller.js +1 -1
  13. package/dist/controllers/role-metadata.controller.d.ts +1 -0
  14. package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
  15. package/dist/controllers/role-metadata.controller.js +15 -0
  16. package/dist/controllers/role-metadata.controller.js.map +1 -1
  17. package/dist/dtos/create-email-template.dto.d.ts.map +1 -1
  18. package/dist/dtos/create-email-template.dto.js.map +1 -1
  19. package/dist/dtos/create-list-of-values.dto.d.ts.map +1 -1
  20. package/dist/dtos/create-list-of-values.dto.js.map +1 -1
  21. package/dist/dtos/create-menu-item-metadata.dto.d.ts.map +1 -1
  22. package/dist/dtos/create-menu-item-metadata.dto.js.map +1 -1
  23. package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
  24. package/dist/dtos/create-role-metadata.dto.js.map +1 -1
  25. package/dist/dtos/create-scheduled-job.dto.d.ts.map +1 -1
  26. package/dist/dtos/create-scheduled-job.dto.js.map +1 -1
  27. package/dist/dtos/create-security-rule.dto.d.ts.map +1 -1
  28. package/dist/dtos/create-security-rule.dto.js.map +1 -1
  29. package/dist/dtos/create-sms-template.dto.d.ts.map +1 -1
  30. package/dist/dtos/create-sms-template.dto.js.map +1 -1
  31. package/dist/dtos/create-view-metadata.dto.d.ts.map +1 -1
  32. package/dist/dtos/create-view-metadata.dto.js.map +1 -1
  33. package/dist/dtos/otp-sign-in.dto.d.ts +1 -1
  34. package/dist/dtos/otp-sign-in.dto.d.ts.map +1 -1
  35. package/dist/dtos/otp-sign-in.dto.js +2 -2
  36. package/dist/dtos/otp-sign-in.dto.js.map +1 -1
  37. package/dist/dtos/otp-sign-up.dto.d.ts +2 -2
  38. package/dist/dtos/otp-sign-up.dto.d.ts.map +1 -1
  39. package/dist/dtos/otp-sign-up.dto.js +2 -2
  40. package/dist/dtos/otp-sign-up.dto.js.map +1 -1
  41. package/dist/dtos/resolve-s3-url.dto.d.ts +2 -5
  42. package/dist/dtos/resolve-s3-url.dto.d.ts.map +1 -1
  43. package/dist/dtos/resolve-s3-url.dto.js +1 -13
  44. package/dist/dtos/resolve-s3-url.dto.js.map +1 -1
  45. package/dist/dtos/sign-up.dto.d.ts.map +1 -1
  46. package/dist/dtos/sign-up.dto.js.map +1 -1
  47. package/dist/dtos/update-email-template.dto.d.ts.map +1 -1
  48. package/dist/dtos/update-email-template.dto.js.map +1 -1
  49. package/dist/dtos/update-list-of-values.dto.d.ts.map +1 -1
  50. package/dist/dtos/update-list-of-values.dto.js.map +1 -1
  51. package/dist/dtos/update-menu-item-metadata.dto.d.ts.map +1 -1
  52. package/dist/dtos/update-menu-item-metadata.dto.js.map +1 -1
  53. package/dist/dtos/update-scheduled-job.dto.d.ts.map +1 -1
  54. package/dist/dtos/update-scheduled-job.dto.js.map +1 -1
  55. package/dist/dtos/update-security-rule.dto.d.ts.map +1 -1
  56. package/dist/dtos/update-security-rule.dto.js.map +1 -1
  57. package/dist/dtos/update-sms-template.dto.d.ts.map +1 -1
  58. package/dist/dtos/update-sms-template.dto.js.map +1 -1
  59. package/dist/dtos/update-view-metadata.dto.d.ts.map +1 -1
  60. package/dist/dtos/update-view-metadata.dto.js.map +1 -1
  61. package/dist/entities/user.entity.d.ts +1 -0
  62. package/dist/entities/user.entity.d.ts.map +1 -1
  63. package/dist/entities/user.entity.js +6 -1
  64. package/dist/entities/user.entity.js.map +1 -1
  65. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts +2 -0
  66. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
  67. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js +33 -23
  68. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
  69. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts +3 -0
  70. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
  71. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js +36 -23
  72. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
  73. package/dist/helpers/security.helper.js +1 -0
  74. package/dist/helpers/security.helper.js.map +1 -1
  75. package/dist/index.d.ts +0 -4
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +0 -4
  78. package/dist/index.js.map +1 -1
  79. package/dist/repository/solid-base.repository.d.ts +10 -1
  80. package/dist/repository/solid-base.repository.d.ts.map +1 -1
  81. package/dist/repository/solid-base.repository.js +109 -0
  82. package/dist/repository/solid-base.repository.js.map +1 -1
  83. package/dist/seeders/module-metadata-seeder.service.d.ts +2 -0
  84. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  85. package/dist/seeders/module-metadata-seeder.service.js +141 -71
  86. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  87. package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -1
  88. package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
  89. package/dist/seeders/permission-metadata-seeder.service.js +1 -1
  90. package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
  91. package/dist/seeders/seed-data/solid-core-metadata.json +12 -25
  92. package/dist/services/authentication.service.d.ts +22 -8
  93. package/dist/services/authentication.service.d.ts.map +1 -1
  94. package/dist/services/authentication.service.js +228 -214
  95. package/dist/services/authentication.service.js.map +1 -1
  96. package/dist/services/crud-helper.service.d.ts +4 -0
  97. package/dist/services/crud-helper.service.d.ts.map +1 -1
  98. package/dist/services/crud-helper.service.js +66 -32
  99. package/dist/services/crud-helper.service.js.map +1 -1
  100. package/dist/services/crud.service.d.ts.map +1 -1
  101. package/dist/services/crud.service.js +7 -4
  102. package/dist/services/crud.service.js.map +1 -1
  103. package/dist/services/field-metadata.service.d.ts.map +1 -1
  104. package/dist/services/field-metadata.service.js.map +1 -1
  105. package/dist/services/file/disk-file.service.d.ts +0 -2
  106. package/dist/services/file/disk-file.service.d.ts.map +1 -1
  107. package/dist/services/file/disk-file.service.js +7 -16
  108. package/dist/services/file/disk-file.service.js.map +1 -1
  109. package/dist/services/file/index.d.ts +1 -0
  110. package/dist/services/file/index.d.ts.map +1 -1
  111. package/dist/services/file/index.js +1 -0
  112. package/dist/services/file/index.js.map +1 -1
  113. package/dist/services/file/storage-path-builder.d.ts +17 -0
  114. package/dist/services/file/storage-path-builder.d.ts.map +1 -0
  115. package/dist/{seeders/sms-template-seeder.service.js → services/file/storage-path-builder.js} +45 -35
  116. package/dist/services/file/storage-path-builder.js.map +1 -0
  117. package/dist/services/media.service.d.ts +1 -1
  118. package/dist/services/media.service.d.ts.map +1 -1
  119. package/dist/services/media.service.js +45 -6
  120. package/dist/services/media.service.js.map +1 -1
  121. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  122. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  123. package/dist/services/mediaStorageProviders/file-storage-provider.js +46 -7
  124. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  125. package/dist/services/module-metadata.service.d.ts +4 -6
  126. package/dist/services/module-metadata.service.d.ts.map +1 -1
  127. package/dist/services/module-metadata.service.js +16 -14
  128. package/dist/services/module-metadata.service.js.map +1 -1
  129. package/dist/services/setting.service.d.ts +3 -2
  130. package/dist/services/setting.service.d.ts.map +1 -1
  131. package/dist/services/setting.service.js +7 -4
  132. package/dist/services/setting.service.js.map +1 -1
  133. package/dist/services/settings/default-settings-provider.service.d.ts +24 -2
  134. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  135. package/dist/services/settings/default-settings-provider.service.js +8 -6
  136. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  137. package/dist/solid-core.module.d.ts +3 -1
  138. package/dist/solid-core.module.d.ts.map +1 -1
  139. package/dist/solid-core.module.js +45 -9
  140. package/dist/solid-core.module.js.map +1 -1
  141. package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
  142. package/dist/testing/adapters/ui/playwright-adapter.js +35 -2
  143. package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
  144. package/package.json +8 -2
  145. package/src/constants/error-messages.ts +1 -0
  146. package/src/constants.ts +3 -3
  147. package/src/controllers/role-metadata.controller.ts +26 -18
  148. package/src/dtos/create-email-template.dto.ts +7 -0
  149. package/src/dtos/create-list-of-values.dto.ts +7 -0
  150. package/src/dtos/create-menu-item-metadata.dto.ts +12 -1
  151. package/src/dtos/create-role-metadata.dto.ts +9 -0
  152. package/src/dtos/create-scheduled-job.dto.ts +14 -0
  153. package/src/dtos/create-security-rule.dto.ts +6 -0
  154. package/src/dtos/create-sms-template.dto.ts +6 -0
  155. package/src/dtos/create-view-metadata.dto.ts +11 -0
  156. package/src/dtos/otp-sign-in.dto.ts +3 -3
  157. package/src/dtos/otp-sign-up.dto.ts +3 -3
  158. package/src/dtos/resolve-s3-url.dto.ts +2 -12
  159. package/src/dtos/sign-up.dto.ts +0 -2
  160. package/src/dtos/update-email-template.dto.ts +6 -0
  161. package/src/dtos/update-list-of-values.dto.ts +8 -0
  162. package/src/dtos/update-menu-item-metadata.dto.ts +12 -0
  163. package/src/dtos/update-scheduled-job.dto.ts +15 -0
  164. package/src/dtos/update-security-rule.dto.ts +7 -0
  165. package/src/dtos/update-sms-template.dto.ts +32 -32
  166. package/src/dtos/update-view-metadata.dto.ts +12 -0
  167. package/src/entities/user.entity.ts +3 -0
  168. package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +43 -32
  169. package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +45 -33
  170. package/src/helpers/security.helper.ts +1 -1
  171. package/src/index.ts +0 -4
  172. package/src/repository/solid-base.repository.ts +172 -1
  173. package/src/seeders/module-metadata-seeder.service.ts +188 -126
  174. package/src/seeders/permission-metadata-seeder.service.ts +1 -4
  175. package/src/seeders/seed-data/solid-core-metadata.json +12 -25
  176. package/src/services/authentication.service.ts +268 -266
  177. package/src/services/crud-helper.service.ts +79 -36
  178. package/src/services/crud.service.ts +9 -4
  179. package/src/services/field-metadata.service.ts +0 -71
  180. package/src/services/file/disk-file.service.ts +8 -18
  181. package/src/services/file/index.ts +1 -0
  182. package/src/services/file/storage-path-builder.ts +56 -0
  183. package/src/services/media.service.ts +13 -7
  184. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +1 -1
  185. package/src/services/mediaStorageProviders/file-storage-provider.ts +13 -8
  186. package/src/services/module-metadata.service.ts +18 -15
  187. package/src/services/setting.service.ts +5 -3
  188. package/src/services/settings/default-settings-provider.service.ts +5 -3
  189. package/src/solid-core.module.ts +16 -12
  190. package/src/testing/adapters/ui/playwright-adapter.ts +1 -1
  191. package/dist/passport-strategies/local.strategy.d.ts +0 -15
  192. package/dist/passport-strategies/local.strategy.d.ts.map +0 -1
  193. package/dist/passport-strategies/local.strategy.js +0 -44
  194. package/dist/passport-strategies/local.strategy.js.map +0 -1
  195. package/dist/seeders/email-template-seeder.service.d.ts +0 -10
  196. package/dist/seeders/email-template-seeder.service.d.ts.map +0 -1
  197. package/dist/seeders/email-template-seeder.service.js +0 -84
  198. package/dist/seeders/email-template-seeder.service.js.map +0 -1
  199. package/dist/seeders/sms-template-seeder.service.d.ts +0 -10
  200. package/dist/seeders/sms-template-seeder.service.d.ts.map +0 -1
  201. package/dist/seeders/sms-template-seeder.service.js.map +0 -1
  202. package/dist/seeders/user-seeder.service.d.ts +0 -10
  203. package/dist/seeders/user-seeder.service.d.ts.map +0 -1
  204. package/dist/seeders/user-seeder.service.js +0 -44
  205. package/dist/seeders/user-seeder.service.js.map +0 -1
  206. package/src/passport-strategies/local.strategy.ts +0 -28
  207. package/src/seeders/email-template-seeder.service.ts +0 -49
  208. package/src/seeders/sms-template-seeder.service.ts +0 -50
  209. package/src/seeders/user-seeder.service.ts +0 -33
@@ -1,7 +1,7 @@
1
1
  import { classify } from "@angular-devkit/core/src/utils/strings";
2
+ import { Logger } from "@nestjs/common";
2
3
  import { isEmpty, isEnum, isInt, isNotEmpty } from "class-validator";
3
4
  import { RelationFieldsCommand } from "src/dtos/create-field-metadata.dto";
4
- import { FieldMetadata } from "src/entities/field-metadata.entity";
5
5
  import { FieldCrudManager, ValidationError } from "src/interfaces";
6
6
  import { EntityManager, In } from "typeorm";
7
7
 
@@ -13,12 +13,15 @@ export interface OneToManyRelationFieldOptions {
13
13
  modelSingularName: string | undefined | null;
14
14
  inverseFieldName: string | undefined | null;
15
15
  entityManager: EntityManager | undefined | null;
16
+ isUpdate?: boolean;
17
+ entityId?: number;
16
18
  }
17
19
 
18
20
  const linkCommands = [RelationFieldsCommand.link, RelationFieldsCommand.unlink, RelationFieldsCommand.set];
19
21
 
20
22
  // This implementation is meant to be used for many-to-one relation field
21
23
  export class OneToManyRelationFieldCrudManager implements FieldCrudManager {
24
+ private readonly logger = new Logger(OneToManyRelationFieldCrudManager.name);
22
25
 
23
26
  private readonly valueFieldName: string;
24
27
  private readonly idFieldName: string;
@@ -74,18 +77,22 @@ export class OneToManyRelationFieldCrudManager implements FieldCrudManager {
74
77
  }
75
78
 
76
79
  async transformForCreate(dto: any): Promise<any> {
77
- // const relatedFieldData: any[] = dto[this.fieldName()];
78
80
  const currentEntityName = classify(this.options.modelSingularName);
79
81
  const currentEntityRepository = this.options.entityManager.getRepository(currentEntityName);
80
82
 
81
83
  const relatedEntityName = classify(this.options.relationCoModelSingularName);
82
84
  const relatedEntityRepository = this.options.entityManager.getRepository(relatedEntityName);
83
85
 
84
- dto[this.valueFieldName] = await this.transformByCommand(dto, relatedEntityRepository, currentEntityRepository);
86
+ const result = await this.transformByCommand(dto, relatedEntityRepository, currentEntityRepository);
87
+ if (result !== undefined) {
88
+ dto[this.valueFieldName] = result;
89
+ } else {
90
+ delete dto[this.valueFieldName];
91
+ }
85
92
  return dto;
86
93
  }
87
94
 
88
- private async transformByCommand(dto: any, relatedEntityRepository: any, currentEntityRepository: any): Promise<any[]> {
95
+ private async transformByCommand(dto: any, relatedEntityRepository: any, currentEntityRepository: any): Promise<any[] | undefined> {
89
96
  // TODO : Need to add support for the multiple commands
90
97
  const command = dto[this.commandFieldName];
91
98
  const values = dto[this.valueFieldName];
@@ -101,13 +108,14 @@ export class OneToManyRelationFieldCrudManager implements FieldCrudManager {
101
108
  case RelationFieldsCommand.clear:
102
109
  return this.transformForCommandClear();
103
110
  case RelationFieldsCommand.set:
104
- return await this.transformForCommandSet(ids, relatedEntityRepository, dto, currentEntityRepository);
111
+ return await this.transformForCommandSet(ids, relatedEntityRepository);
105
112
  case RelationFieldsCommand.link:
106
- return await this.tranformForCommandLink(ids, relatedEntityRepository, dto, currentEntityRepository);
113
+ return await this.tranformForCommandLink(ids, relatedEntityRepository);
107
114
  case RelationFieldsCommand.unlink:
108
- return await this.transformForCommandUnLink(ids, relatedEntityRepository, dto, currentEntityRepository);
115
+ return await this.transformForCommandUnLink(ids);
109
116
  default:
110
- return null; // This is equivalent to no transformation
117
+ this.logger.log(`Invalid command ${command}`);
118
+ return null;
111
119
  }
112
120
  }
113
121
 
@@ -115,46 +123,50 @@ export class OneToManyRelationFieldCrudManager implements FieldCrudManager {
115
123
  return []
116
124
  }
117
125
 
118
- private async transformForCommandSet(ids: any[], relatedEntityRepository: any, dto: any, currentEntityRepository: any): Promise<any[]> {
119
- // Load the entities with the ids passed
126
+ private async transformForCommandSet(ids: any[], relatedEntityRepository: any): Promise<any[]> {
120
127
  const loadedEntities: any[] = await relatedEntityRepository.find({
121
- where : {id: In(ids) }
128
+ where : {id: In(ids) }
122
129
  })
123
130
  if (loadedEntities.length !== ids.length) {
124
- throw new Error('Invalid entity ids provided for linking');
131
+ throw new Error('Invalid entity ids provided for set');
125
132
  }
126
133
 
127
134
  return loadedEntities;
128
135
  }
129
136
 
130
- private async tranformForCommandLink(ids: any, relatedEntityRepository: any, dto: any, currentEntityRepository: any) {
131
- const tranformedRelatedFields = [];
132
- // Load the entities with the ids passed
133
- const loadedEntities: any[] = await relatedEntityRepository.find({
134
- where : {id: In(ids) }
135
- })
136
- if (loadedEntities.length !== ids.length) {
137
+ private async tranformForCommandLink(ids: any[], relatedEntityRepository: any): Promise<undefined> {
138
+ if (!this.options.isUpdate) {
139
+ throw new Error('Link command is only supported for update operations');
140
+ }
141
+
142
+ const count: number = await relatedEntityRepository.count({
143
+ where: { id: In(ids) }
144
+ });
145
+ if (count !== ids.length) {
137
146
  throw new Error('Invalid entity ids provided for linking');
138
147
  }
139
- tranformedRelatedFields.push(...loadedEntities);
140
148
 
141
- return tranformedRelatedFields;
149
+ await this.options.entityManager
150
+ .createQueryBuilder()
151
+ .relation(classify(this.options.modelSingularName), this.valueFieldName)
152
+ .of(this.options.entityId)
153
+ .add(ids);
154
+
155
+ return undefined;
142
156
  }
143
157
 
144
- private async transformForCommandUnLink(ids: any, relatedEntityRepository: any, dto: any, currentEntityRepository: any) {
145
- if (dto.id == null) {
146
- throw new Error('Entity id is required for unlinking');
147
- }
158
+ private async transformForCommandUnLink(ids: any[]): Promise<undefined> {
159
+ if (!this.options.isUpdate) {
160
+ throw new Error('Unlink command is only supported for update operations');
161
+ }
148
162
 
149
- const tranformedRelatedFields = [];
150
- const entityInstance = await currentEntityRepository.findOne({
151
- where: {id: dto.id},
152
- relations: [this.valueFieldName]
153
- });
154
- const filteredEntities = entityInstance[this.valueFieldName].filter((entity) => !ids.includes(entity.id));
155
- tranformedRelatedFields.push(...filteredEntities);
163
+ await this.options.entityManager
164
+ .createQueryBuilder()
165
+ .relation(classify(this.options.modelSingularName), this.valueFieldName)
166
+ .of(this.options.entityId)
167
+ .remove(ids);
156
168
 
157
- return tranformedRelatedFields;
169
+ return undefined;
158
170
  }
159
171
 
160
172
  private async transformForCommandCreate(values: any[], relatedEntityRepository: any): Promise<any[]> {
@@ -24,7 +24,7 @@ export function buildDefaultSecurityHeaderOptions(): Readonly<HelmetOptions> {
24
24
 
25
25
  // clickjacking defense (modern)
26
26
  "frame-ancestors": ["'none'"],
27
-
27
+ "style-src": ["'self'"],
28
28
  // add/adjust as needed for your app:
29
29
  // "script-src": ["'self'"], // add hashes/nonces/CSPRO if needed
30
30
  // "style-src": ["'self'", "'unsafe-inline'"],
package/src/index.ts CHANGED
@@ -212,16 +212,12 @@ export * from './jobs/msg91-whatsapp-subscriber.service'
212
212
  export * from './listeners/user-registration.listener'
213
213
 
214
214
  export * from './passport-strategies/google-oauth.strategy'
215
- export * from './passport-strategies/local.strategy'
216
215
 
217
216
  export * from './services/selection-providers/list-of-values-selection-providers.service'
218
217
 
219
218
  // seed-data
220
- export * from './seeders/email-template-seeder.service'
221
219
  export * from './seeders/permission-metadata-seeder.service'
222
- export * from './seeders/sms-template-seeder.service'
223
220
  export * from './seeders/module-metadata-seeder.service'
224
- export * from './seeders/user-seeder.service'
225
221
 
226
222
  // export * from './services/access-token-storage.service'
227
223
  export * from './services/action-metadata.service'
@@ -13,9 +13,11 @@ import {
13
13
  FindOptionsWhere,
14
14
  QueryRunner,
15
15
  Repository,
16
- SelectQueryBuilder
16
+ SelectQueryBuilder,
17
+ UpdateResult
17
18
  } from 'typeorm';
18
19
  import { SecurityRuleRepository } from './security-rule.repository';
20
+ import { PickKeysByType } from 'typeorm/common/PickKeysByType';
19
21
 
20
22
  export class SolidBaseRepository<T extends CommonEntity> extends Repository<T> {
21
23
  protected readonly logger: Logger;
@@ -132,4 +134,173 @@ export class SolidBaseRepository<T extends CommonEntity> extends Repository<T> {
132
134
 
133
135
  return qb.getManyAndCount();
134
136
  }
137
+
138
+ /**
139
+ * Security-aware count(): applies security rules before counting.
140
+ */
141
+ override async count(options?: FindManyOptions<T>): Promise<number> {
142
+ const alias = this.modelSingularName();
143
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
144
+
145
+ if (options) {
146
+ qb.setFindOptions(options);
147
+ }
148
+
149
+ return qb.getCount();
150
+ }
151
+
152
+ /**
153
+ * Security-aware countBy(): convenience wrapper routed through count().
154
+ */
155
+ override async countBy(where: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number> {
156
+ return this.count({ where });
157
+ }
158
+
159
+ /**
160
+ * Security-aware average(): applies security rules before computing the average.
161
+ */
162
+ override async average(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
163
+ const alias = this.modelSingularName();
164
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
165
+
166
+ if (where) {
167
+ qb.setFindOptions({ where });
168
+ }
169
+
170
+ const result = await qb
171
+ .select(`AVG(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'avg')
172
+ .getRawOne<{ avg: string | number | null }>();
173
+
174
+ if (result?.avg === null || result?.avg === undefined) {
175
+ return null;
176
+ }
177
+
178
+ return typeof result.avg === 'number'
179
+ ? result.avg
180
+ : parseFloat(result.avg);
181
+ }
182
+
183
+ /**
184
+ * Security-aware sum(): applies security rules before computing the sum.
185
+ */
186
+ override async sum(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
187
+ const alias = this.modelSingularName();
188
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
189
+
190
+ if (where) {
191
+ qb.setFindOptions({ where });
192
+ }
193
+
194
+ const result = await qb
195
+ .select(`SUM(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'sum')
196
+ .getRawOne<{ sum: string | number | null }>();
197
+
198
+ if (result?.sum === null || result?.sum === undefined) {
199
+ return null;
200
+ }
201
+
202
+ return typeof result.sum === 'number'
203
+ ? result.sum
204
+ : parseFloat(result.sum);
205
+ }
206
+
207
+ /**
208
+ * Security-aware minimum(): applies security rules before computing the minimum.
209
+ */
210
+ override async minimum(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
211
+ const alias = this.modelSingularName();
212
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
213
+
214
+ if (where) {
215
+ qb.setFindOptions({ where });
216
+ }
217
+
218
+ const result = await qb
219
+ .select(`MIN(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'min')
220
+ .getRawOne<{ min: string | number | null }>();
221
+
222
+ if (result?.min === null || result?.min === undefined) {
223
+ return null;
224
+ }
225
+
226
+ return typeof result.min === 'number'
227
+ ? result.min
228
+ : parseFloat(result.min);
229
+ }
230
+
231
+ /**
232
+ * Security-aware maximum(): applies security rules before computing the maximum.
233
+ */
234
+ override async maximum(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
235
+ const alias = this.modelSingularName();
236
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
237
+
238
+ if (where) {
239
+ qb.setFindOptions({ where });
240
+ }
241
+
242
+ const result = await qb
243
+ .select(`MAX(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'max')
244
+ .getRawOne<{ max: string | number | null }>();
245
+
246
+ if (result?.max === null || result?.max === undefined) {
247
+ return null;
248
+ }
249
+
250
+ return typeof result.max === 'number'
251
+ ? result.max
252
+ : parseFloat(result.max);
253
+ }
254
+
255
+ /**
256
+ * Security-aware increment(): increments a column by a given value for all matching rows.
257
+ * Security rules are applied to determine which rows the user is allowed to modify.
258
+ */
259
+ override async increment(where: FindOptionsWhere<T>, propertyPath: string, value: string | number,): Promise<UpdateResult> {
260
+ const alias = this.modelSingularName();
261
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
262
+
263
+ qb.setFindOptions({ where });
264
+
265
+ const rows = await qb.select(`${alias}.id`).getMany();
266
+ const ids = rows.map((r) => (r as any).id);
267
+
268
+ if (ids.length === 0) {
269
+ return { raw: [], affected: 0, generatedMaps: [] };
270
+ }
271
+
272
+ return this.manager
273
+ .createQueryBuilder()
274
+ .update(this.metadata.target)
275
+ .set({ [propertyPath]: () => `${propertyPath} + :value` } as any)
276
+ .whereInIds(ids)
277
+ .setParameter('value', value)
278
+ .execute();
279
+ }
280
+
281
+ /**
282
+ * Security-aware decrement(): decrements a column by a given value for all matching rows.
283
+ * Security rules are applied to determine which rows the user is allowed to modify.
284
+ */
285
+ override async decrement(where: FindOptionsWhere<T>, propertyPath: string, value: string | number,): Promise<UpdateResult> {
286
+ const alias = this.modelSingularName();
287
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
288
+
289
+ qb.setFindOptions({ where });
290
+
291
+ const rows = await qb.select(`${alias}.id`).getMany();
292
+ const ids = rows.map((r) => (r as any).id);
293
+
294
+ if (ids.length === 0) {
295
+ return { raw: [], affected: 0, generatedMaps: [] };
296
+ }
297
+
298
+ return this.manager
299
+ .createQueryBuilder()
300
+ .update(this.metadata.target)
301
+ .set({ [propertyPath]: () => `${propertyPath} - :value` } as any)
302
+ .whereInIds(ids)
303
+ .setParameter('value', value)
304
+ .execute();
305
+ }
135
306
  }