@communecter/cocolight-api-client 1.0.82 → 1.0.83

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@communecter/cocolight-api-client",
3
- "version": "1.0.82",
3
+ "version": "1.0.83",
4
4
  "description": "Client Axios simplifié pour l'API cocolight",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3209,6 +3209,63 @@ export class BaseEntity<TServerData = any> {
3209
3209
  return arrayObjet as Record<string, any>;
3210
3210
  }
3211
3211
 
3212
+
3213
+ /**
3214
+ * Recherche des membres (utilisateurs ou organisations) liés à l'entité courante.
3215
+ *
3216
+ * Cette méthode effectue une recherche par autocomplétion parmi les membres de l'entité
3217
+ * (organisation, projet, événement). Les résultats sont filtrés selon le mode de recherche
3218
+ * et retournés sous forme d'entités User ou Organization liées à l'entité parente.
3219
+ *
3220
+ * @param {SearchMemberAutocompleteData} data - Paramètres de recherche
3221
+ * @param {string} data.search - Terme recherché (nom, slug, etc.)
3222
+ * @param {"personOnly" | "organizationOnly" | "mixte"} data.searchMode - Mode de recherche :
3223
+ * - `"personOnly"` : recherche uniquement parmi les utilisateurs
3224
+ * - `"organizationOnly"` : recherche uniquement parmi les organisations
3225
+ * - `"mixte"` : recherche parmi les utilisateurs et organisations
3226
+ * @returns {Promise<(User | Organization)[]>} Liste des entités correspondantes, liées à l'entité parente
3227
+ * @throws {ApiError} Si le type d'entité n'est pas supporté (badges, news, poi, comments)
3228
+ *
3229
+ * @example
3230
+ * ```typescript
3231
+ * // Rechercher des utilisateurs dans une organisation
3232
+ * const org = await me.organization({ slug: "myOrg" });
3233
+ * const users = await org.searchMembers({ search: "jean", searchMode: "personOnly" });
3234
+ * users.forEach(user => console.log(user.data.name));
3235
+ *
3236
+ * // Rechercher des organisations membres d'un projet
3237
+ * const project = await me.project({ slug: "myProject" });
3238
+ * const orgs = await project.searchMembers({ search: "asso", searchMode: "organizationOnly" });
3239
+ * ```
3240
+ *
3241
+ * @remarks
3242
+ * Les entités retournées sont automatiquement liées à l'entité parente via `_linkEntities`,
3243
+ * ce qui permet d'utiliser les méthodes comme `sendRequestToJoinParent()`, `isAdminPending()`, etc.
3244
+ */
3245
+ async searchMembers(data: SearchMemberAutocompleteData): Promise<(User | Organization)[]> {
3246
+ // TODO: j'ai l'impression que searchMode : "organizationOnly" ne renvois pas que des organisations
3247
+ const result = await this.endpointApi.searchMemberAutocomplete(data);
3248
+ if (!result || !Array.isArray(result)) {
3249
+ return [];
3250
+ }
3251
+
3252
+ const t = this.getEntityType();
3253
+ if (t === "badges" || t === "news" || t === "poi" || t === "comments") {
3254
+ throw new ApiError(`Le type d'entité "${t}" n'est pas supporté par searchMemberAutocomplete.`, 400);
3255
+ }
3256
+
3257
+ result.forEach(item => {
3258
+ for (const key of Object.keys(item)) {
3259
+ if (!["id", "name", "slug", "profilThumbImageUrl", "profilMarkerImageUrl", "type", "collection"].includes(key)) {
3260
+ delete item[key];
3261
+ }
3262
+ }
3263
+ });
3264
+
3265
+ const rawList = this._linkEntities(result);
3266
+ return rawList;
3267
+ }
3268
+
3212
3269
  /**
3213
3270
  * Soumet une demande pour rejoindre l'entité courante (ex. organisation, projet, événement...).
3214
3271
  * Si une invitation est en attente, elle est automatiquement acceptée.
@@ -4088,30 +4145,6 @@ export class BaseEntity<TServerData = any> {
4088
4145
  return paginator.next() as Promise<PaginatorPage<any>>;
4089
4146
  }
4090
4147
 
4091
- async searchMembers(data: SearchMemberAutocompleteData) {
4092
- // TODO: j'ai l'impression que searchMode : "organizationOnly" ne renvois pas que des organisations
4093
- const result = await this.endpointApi.searchMemberAutocomplete(data);
4094
- if (!result || !Array.isArray(result)) {
4095
- return [];
4096
- }
4097
-
4098
- const t = this.getEntityType();
4099
- if (t === "badges" || t === "news" || t === "poi" || t === "comments") {
4100
- throw new ApiError(`Le type d'entité "${t}" n'est pas supporté par searchMemberAutocomplete.`, 400);
4101
- }
4102
-
4103
- result.forEach(item => {
4104
- for (const key of Object.keys(item)) {
4105
- if (!["id", "name", "slug", "profilThumbImageUrl", "profilMarkerImageUrl", "type", "collection"].includes(key)) {
4106
- delete item[key];
4107
- }
4108
- }
4109
- });
4110
-
4111
- const rawList = this._linkEntities(result);
4112
- return rawList;
4113
- }
4114
-
4115
4148
  }
4116
4149
 
4117
4150
  export default BaseEntity;
package/src/api/User.ts CHANGED
@@ -24,6 +24,18 @@ import type { EntityTypes } from "@/types/entities.js";
24
24
  type ApiClient = import("../ApiClient.js").default;
25
25
  type UserItemNormalized = import("./serverDataType/User.js").UserItemNormalized;
26
26
 
27
+ /**
28
+ * Interface représentant le lien entre un utilisateur et une entité parente.
29
+ * Utilisée pour typer les données de relation (membre, admin, invitations, etc.)
30
+ */
31
+ interface ParentLink {
32
+ isAdmin?: boolean;
33
+ isAdminPending?: boolean;
34
+ isInviting?: boolean;
35
+ isAdminInviting?: boolean;
36
+ toBeValidated?: boolean;
37
+ }
38
+
27
39
  export class User extends BaseEntity<UserItemNormalized> {
28
40
 
29
41
  static override entityType = "citoyens" as const;
@@ -890,37 +902,88 @@ export class User extends BaseEntity<UserItemNormalized> {
890
902
  return super.entityBySlug(slug);
891
903
  }
892
904
 
905
+ // ────────────────────────────────
906
+ // Actions sur l'utilisateur par rapport à l'entité parente
907
+ // ────────────────────────────────
908
+
893
909
  /**
894
- * Valide les préconditions communes pour les méthodes de vérification de rôle.
910
+ * Valide les préconditions pour les méthodes nécessitant d'être membre.
895
911
  * @private
896
912
  * @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
897
913
  * @param expectedTypes - Types d'entité parent autorisés.
898
914
  * @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
899
- * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
900
915
  * @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
901
916
  * @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
917
+ * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas membre de l'entité parente.
902
918
  * @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
903
919
  */
904
- private _validateRoleCheckPreconditions(methodName: string, expectedTypes: string[]): void {
920
+ private _validateMemberPreconditions(methodName: string, expectedTypes: string[]): void {
905
921
  if (!this.userId) {
906
922
  throw new ApiError(`Vous devez être connecté pour ${methodName}.`, 401);
907
923
  }
908
924
 
909
- if(!this.parent?.isAdmin()){
910
- throw new ApiError("Vous devez être administrateur pour effectuer cette action.", 401);
911
- }
912
-
913
925
  if (!this.id) {
914
926
  throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
915
927
  }
916
928
 
917
- if(!this.parent?.id){
929
+ if (!this.parent?.id) {
918
930
  throw new ApiError("L'entité parente n'est pas enregistrée.", 404);
919
931
  }
920
932
 
921
933
  if (!expectedTypes.includes(this.parent.getEntityType())) {
922
934
  throw new ApiError(`L'entité doit être de type : ${expectedTypes.join(", ")}, reçu : ${this.parent.getEntityType()}`, 400);
923
935
  }
936
+
937
+ // Vérifier que l'utilisateur connecté a le bon rôle selon le type d'entité
938
+ // Pour les events, tout utilisateur connecté peut agir (pas besoin d'être participant)
939
+ const parentType = this.parent.getEntityType();
940
+
941
+ if (parentType === "events") {
942
+ // Pour les événements, être connecté suffit (déjà vérifié ci-dessus)
943
+ return;
944
+ }
945
+
946
+ let hasRole = false;
947
+
948
+ switch (parentType) {
949
+ case "organizations":
950
+ hasRole = this.parent.isMember();
951
+ break;
952
+ case "projects":
953
+ hasRole = this.parent.isContributor();
954
+ break;
955
+ default:
956
+ hasRole = false;
957
+ }
958
+
959
+ if (!hasRole) {
960
+ const roleNames: Record<string, string> = {
961
+ organizations: "membre",
962
+ projects: "contributeur"
963
+ };
964
+ const roleName = roleNames[parentType] || "membre";
965
+ throw new ApiError(`Vous devez être ${roleName} pour effectuer cette action.`, 401);
966
+ }
967
+ }
968
+
969
+ /**
970
+ * Valide les préconditions pour les méthodes nécessitant d'être administrateur.
971
+ * @private
972
+ * @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
973
+ * @param expectedTypes - Types d'entité parent autorisés.
974
+ * @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
975
+ * @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
976
+ * @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
977
+ * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas membre de l'entité parente.
978
+ * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
979
+ * @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
980
+ */
981
+ private _validateAdminPreconditions(methodName: string, expectedTypes: string[]): void {
982
+ this._validateMemberPreconditions(methodName, expectedTypes);
983
+
984
+ if (!this.parent?.isAdmin()) {
985
+ throw new ApiError("Vous devez être administrateur pour effectuer cette action.", 401);
986
+ }
924
987
  }
925
988
 
926
989
 
@@ -930,13 +993,24 @@ export class User extends BaseEntity<UserItemNormalized> {
930
993
  * @returns Le lien parent de l'utilisateur ou `null` s'il n'existe pas.
931
994
  * @private
932
995
  */
933
- private _getParentLinkForUser(): any {
996
+ private _getParentLinkForUser(): ParentLink | null {
934
997
  const { connectTypeDisconnect } = this.parent!._getLinkMeta();
935
998
  const userId = this!.id;
936
999
  if (!userId) return null;
937
1000
  return this.parent?.serverData?.links?.[connectTypeDisconnect]?.[userId] || null;
938
1001
  }
939
1002
 
1003
+ /**
1004
+ * Rafraîchit l'utilisateur et l'entité parente après une action.
1005
+ * @private
1006
+ */
1007
+ private async _refreshWithParent(): Promise<void> {
1008
+ await this.refresh();
1009
+ if (this.parent) {
1010
+ await this.parent.refresh();
1011
+ }
1012
+ }
1013
+
940
1014
  /**
941
1015
  * Vérifie si l'utilisateur est administrateur de l'entité parente.
942
1016
  *
@@ -978,7 +1052,7 @@ export class User extends BaseEntity<UserItemNormalized> {
978
1052
  * }
979
1053
  */
980
1054
  override isAdmin(): boolean {
981
- this._validateRoleCheckPreconditions("isAdmin", ["organizations", "projects"]);
1055
+ this._validateMemberPreconditions("isAdmin", ["organizations", "projects"]);
982
1056
  const parentLink = this._getParentLinkForUser();
983
1057
  return this._validateUserLink(parentLink) && parentLink?.isAdmin === true;
984
1058
  }
@@ -1009,7 +1083,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1009
1083
  * }
1010
1084
  */
1011
1085
  override isMember(): boolean {
1012
- this._validateRoleCheckPreconditions("isMember", ["organizations"]);
1086
+ this._validateMemberPreconditions("isMember", ["organizations"]);
1013
1087
  const parentLink = this._getParentLinkForUser();
1014
1088
  return this._validateUserLink(parentLink);
1015
1089
  }
@@ -1040,7 +1114,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1040
1114
  * }
1041
1115
  */
1042
1116
  override isContributor(): boolean {
1043
- this._validateRoleCheckPreconditions("isContributor", ["projects"]);
1117
+ this._validateMemberPreconditions("isContributor", ["projects"]);
1044
1118
  const parentLink = this._getParentLinkForUser();
1045
1119
  return this._validateUserLink(parentLink);
1046
1120
  }
@@ -1071,7 +1145,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1071
1145
  * }
1072
1146
  */
1073
1147
  override isAttendee(): boolean {
1074
- this._validateRoleCheckPreconditions("isAttendee", ["events"]);
1148
+ this._validateMemberPreconditions("isAttendee", ["events"]);
1075
1149
  const parentLink = this._getParentLinkForUser();
1076
1150
  return this._validateUserLink(parentLink);
1077
1151
  }
@@ -1094,7 +1168,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1094
1168
  * ```
1095
1169
  */
1096
1170
  isInviting(): boolean {
1097
- this._validateRoleCheckPreconditions("isInviting", ["organizations", "projects", "events"]);
1171
+ this._validateMemberPreconditions("isInviting", ["organizations", "projects", "events"]);
1098
1172
  const parentLink = this._getParentLinkForUser();
1099
1173
  if (!parentLink) return false;
1100
1174
  return parentLink?.isInviting === true;
@@ -1118,7 +1192,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1118
1192
  * ```
1119
1193
  */
1120
1194
  isInvitingAdmin(): boolean {
1121
- this._validateRoleCheckPreconditions("isInvitingAdmin", ["organizations", "projects"]);
1195
+ this._validateMemberPreconditions("isInvitingAdmin", ["organizations", "projects"]);
1122
1196
  const parentLink = this._getParentLinkForUser();
1123
1197
  if (!parentLink) return false;
1124
1198
  return (parentLink?.isAdminInviting === true || parentLink?.isInviting === true) && parentLink?.isAdmin === true;
@@ -1144,7 +1218,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1144
1218
  * ```
1145
1219
  */
1146
1220
  isAdminPending(): boolean {
1147
- this._validateRoleCheckPreconditions("isAdminPending", ["organizations", "projects"]);
1221
+ this._validateMemberPreconditions("isAdminPending", ["organizations", "projects"]);
1148
1222
  const parentLink = this._getParentLinkForUser();
1149
1223
  return parentLink?.isAdmin === true && parentLink?.isAdminPending === true;
1150
1224
  }
@@ -1169,16 +1243,11 @@ export class User extends BaseEntity<UserItemNormalized> {
1169
1243
  * ```
1170
1244
  */
1171
1245
  isToBeValidated(): boolean {
1172
- this._validateRoleCheckPreconditions("isToBeValidated", ["organizations", "projects", "events"]);
1246
+ this._validateMemberPreconditions("isToBeValidated", ["organizations", "projects", "events"]);
1173
1247
  const parentLink = this._getParentLinkForUser();
1174
1248
  return parentLink?.toBeValidated === true;
1175
1249
  }
1176
1250
 
1177
-
1178
- // ────────────────────────────────
1179
- // Actions d'admin sur l'utilisateur par rapport à l'entité parente
1180
- // ────────────────────────────────
1181
-
1182
1251
  /**
1183
1252
  * Envoie une demande pour rejoindre l'entité parente.
1184
1253
  *
@@ -1206,7 +1275,14 @@ export class User extends BaseEntity<UserItemNormalized> {
1206
1275
  * ```
1207
1276
  */
1208
1277
  async sendRequestToJoinParent({ admin }: { admin: boolean } = { admin: false }): Promise<unknown> {
1209
- this._validateRoleCheckPreconditions("sendRequestToJoinParent", ["organizations", "projects"]);
1278
+ // Validation selon le type d'invitation : admin requis pour inviter en tant qu'admin
1279
+ if (admin) {
1280
+ // Invitation admin : uniquement orga/project, seul un admin peut le faire
1281
+ this._validateAdminPreconditions("sendRequestToJoinParent", ["organizations", "projects"]);
1282
+ } else {
1283
+ // Invitation simple : orga/project/event, membre/contributeur/connecté peut le faire
1284
+ this._validateMemberPreconditions("sendRequestToJoinParent", ["organizations", "projects", "events"]);
1285
+ }
1210
1286
 
1211
1287
  const { connectTypeConnect } = this.parent!._getLinkMeta();
1212
1288
 
@@ -1217,7 +1293,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1217
1293
 
1218
1294
  const t = this.parent!.getEntityType();
1219
1295
 
1220
- // 2) Narrow de type pour TypeScript
1296
+ // Narrow de type pour TypeScript
1221
1297
  const parentType = t as ConnectData["parentType"];
1222
1298
 
1223
1299
  const data: ConnectData = {
@@ -1229,17 +1305,13 @@ export class User extends BaseEntity<UserItemNormalized> {
1229
1305
  };
1230
1306
 
1231
1307
  const retour = await this.callIsConnected(() => this.endpointApi.connect(data));
1232
- // Refresh l'utilisateur membre et l'entité parente
1233
- await this.refresh();
1234
- if (this.parent) {
1235
- await this.parent.refresh();
1236
- }
1308
+ await this._refreshWithParent();
1237
1309
  return retour;
1238
1310
  }
1239
1311
 
1240
- // Cas : déjà en attente
1312
+ // Cas : déjà en attente d'acceptation
1241
1313
  if (parentLink.isInviting || parentLink?.isAdminInviting) {
1242
- throw new ApiError("Vous êtes déjà en attente de validation.", 400);
1314
+ throw new ApiError("Une invitation est déjà en attente d'acceptation.", 400);
1243
1315
  }
1244
1316
 
1245
1317
  // Cas par défaut : rien à faire
@@ -1267,7 +1339,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1267
1339
  * ```
1268
1340
  */
1269
1341
  async validateMemberRequest(): Promise<unknown> {
1270
- this._validateRoleCheckPreconditions("validateMemberRequest", ["organizations", "projects"]);
1342
+ this._validateAdminPreconditions("validateMemberRequest", ["organizations", "projects"]);
1271
1343
 
1272
1344
  const parentLink = this._getParentLinkForUser();
1273
1345
 
@@ -1288,64 +1360,10 @@ export class User extends BaseEntity<UserItemNormalized> {
1288
1360
  };
1289
1361
 
1290
1362
  const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
1291
- // Refresh l'utilisateur membre et l'entité parente
1292
- await this.refresh();
1293
- if (this.parent) {
1294
- await this.parent.refresh();
1295
- }
1363
+ await this._refreshWithParent();
1296
1364
  return retour;
1297
1365
  }
1298
1366
 
1299
- /**
1300
- * Valide une invitation en attente d'acceptation.
1301
- *
1302
- * Cette méthode permet à un admin de valider une invitation envoyée à un utilisateur
1303
- * qui est en attente (`isInviting: true`). Après validation, l'utilisateur devient
1304
- * membre actif de l'entité parente.
1305
- *
1306
- * @returns {Promise<unknown>} La réponse de l'API après validation
1307
- * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1308
- * @throws {ApiError} Si l'utilisateur n'a pas d'invitation en attente
1309
- *
1310
- * @example
1311
- * ```typescript
1312
- * // Un admin valide une invitation en attente
1313
- * const org = await me.organization({ slug: "myOrg" });
1314
- * const members = await org.getMembers();
1315
- * const invitedUser = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.isInviting);
1316
- * await invitedUser.validateInvitation();
1317
- * ```
1318
- */
1319
- // async validateInvitation(): Promise<unknown> {
1320
- // this._validateRoleCheckPreconditions("validateInvitation", ["organizations", "projects"]);
1321
-
1322
- // const parentLink = this._getParentLinkForUser();
1323
-
1324
- // if (!parentLink?.isInviting) {
1325
- // throw new ApiError("Cet utilisateur n'a pas d'invitation en attente.", 400);
1326
- // }
1327
-
1328
- // const t = this.parent!.getEntityType();
1329
-
1330
- // const parentType = t as LinkValidateData["parentType"];
1331
-
1332
- // const data: LinkValidateData = {
1333
- // childId: this.id!,
1334
- // childType: "citoyens",
1335
- // parentType,
1336
- // parentId: this.parent!.id!,
1337
- // linkOption: "isInviting"
1338
- // };
1339
-
1340
- // const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
1341
- // // Refresh l'utilisateur membre et l'entité parente
1342
- // await this.refresh();
1343
- // if (this.parent) {
1344
- // await this.parent.refresh();
1345
- // }
1346
- // return retour;
1347
- // }
1348
-
1349
1367
  /**
1350
1368
  * Valide une demande d'admin en attente.
1351
1369
  *
@@ -1367,7 +1385,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1367
1385
  * ```
1368
1386
  */
1369
1387
  async validateAdminRequest(): Promise<unknown> {
1370
- this._validateRoleCheckPreconditions("validateAdminRequest", ["organizations", "projects"]);
1388
+ this._validateAdminPreconditions("validateAdminRequest", ["organizations", "projects"]);
1371
1389
 
1372
1390
  const parentLink = this._getParentLinkForUser();
1373
1391
 
@@ -1388,11 +1406,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1388
1406
  };
1389
1407
 
1390
1408
  const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
1391
- // Refresh l'utilisateur membre et l'entité parente
1392
- await this.refresh();
1393
- if (this.parent) {
1394
- await this.parent.refresh();
1395
- }
1409
+ await this._refreshWithParent();
1396
1410
  return retour;
1397
1411
  }
1398
1412
 
@@ -1416,7 +1430,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1416
1430
  * ```
1417
1431
  */
1418
1432
  async removeFromParent(): Promise<unknown> {
1419
- this._validateRoleCheckPreconditions("removeFromParent", ["organizations", "projects"]);
1433
+ this._validateAdminPreconditions("removeFromParent", ["organizations", "projects"]);
1420
1434
 
1421
1435
  const parentLink = this._getParentLinkForUser();
1422
1436
 
@@ -1439,11 +1453,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1439
1453
  };
1440
1454
 
1441
1455
  const retour = await this.callIsConnected(() => this.endpointApi.disconnect(data));
1442
- // Refresh l'utilisateur membre et l'entité parente
1443
- await this.refresh();
1444
- if (this.parent) {
1445
- await this.parent.refresh();
1446
- }
1456
+ await this._refreshWithParent();
1447
1457
  return retour;
1448
1458
  }
1449
1459
 
@@ -1468,7 +1478,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1468
1478
  * ```
1469
1479
  */
1470
1480
  async promoteToAdmin(): Promise<unknown> {
1471
- this._validateRoleCheckPreconditions("promoteToAdmin", ["organizations", "projects"]);
1481
+ this._validateAdminPreconditions("promoteToAdmin", ["organizations", "projects"]);
1472
1482
 
1473
1483
  const parentLink = this._getParentLinkForUser();
1474
1484
 
@@ -1497,11 +1507,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1497
1507
  };
1498
1508
 
1499
1509
  const retour = await this.callIsConnected(() => this.endpointApi.connect(data));
1500
- // Refresh l'utilisateur membre et l'entité parente
1501
- await this.refresh();
1502
- if (this.parent) {
1503
- await this.parent.refresh();
1504
- }
1510
+ await this._refreshWithParent();
1505
1511
  return retour;
1506
1512
  }
1507
1513
 
@@ -1526,7 +1532,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1526
1532
  * ```
1527
1533
  */
1528
1534
  async demoteFromAdmin(): Promise<unknown> {
1529
- this._validateRoleCheckPreconditions("demoteFromAdmin", ["organizations", "projects"]);
1535
+ this._validateAdminPreconditions("demoteFromAdmin", ["organizations", "projects"]);
1530
1536
 
1531
1537
  const parentLink = this._getParentLinkForUser();
1532
1538
 
@@ -1552,11 +1558,7 @@ export class User extends BaseEntity<UserItemNormalized> {
1552
1558
  };
1553
1559
 
1554
1560
  const retour = await this.callIsConnected(() => this.endpointApi.demoteAdmin(data));
1555
- // Refresh l'utilisateur membre et l'entité parente
1556
- await this.refresh();
1557
- if (this.parent) {
1558
- await this.parent.refresh();
1559
- }
1561
+ await this._refreshWithParent();
1560
1562
  return retour;
1561
1563
  }
1562
1564
 
@@ -1081,6 +1081,39 @@ export declare class BaseEntity<TServerData = any> {
1081
1081
  docType?: "image" | "file";
1082
1082
  };
1083
1083
  }): Promise<Record<string, any>>;
1084
+ /**
1085
+ * Recherche des membres (utilisateurs ou organisations) liés à l'entité courante.
1086
+ *
1087
+ * Cette méthode effectue une recherche par autocomplétion parmi les membres de l'entité
1088
+ * (organisation, projet, événement). Les résultats sont filtrés selon le mode de recherche
1089
+ * et retournés sous forme d'entités User ou Organization liées à l'entité parente.
1090
+ *
1091
+ * @param {SearchMemberAutocompleteData} data - Paramètres de recherche
1092
+ * @param {string} data.search - Terme recherché (nom, slug, etc.)
1093
+ * @param {"personOnly" | "organizationOnly" | "mixte"} data.searchMode - Mode de recherche :
1094
+ * - `"personOnly"` : recherche uniquement parmi les utilisateurs
1095
+ * - `"organizationOnly"` : recherche uniquement parmi les organisations
1096
+ * - `"mixte"` : recherche parmi les utilisateurs et organisations
1097
+ * @returns {Promise<(User | Organization)[]>} Liste des entités correspondantes, liées à l'entité parente
1098
+ * @throws {ApiError} Si le type d'entité n'est pas supporté (badges, news, poi, comments)
1099
+ *
1100
+ * @example
1101
+ * ```typescript
1102
+ * // Rechercher des utilisateurs dans une organisation
1103
+ * const org = await me.organization({ slug: "myOrg" });
1104
+ * const users = await org.searchMembers({ search: "jean", searchMode: "personOnly" });
1105
+ * users.forEach(user => console.log(user.data.name));
1106
+ *
1107
+ * // Rechercher des organisations membres d'un projet
1108
+ * const project = await me.project({ slug: "myProject" });
1109
+ * const orgs = await project.searchMembers({ search: "asso", searchMode: "organizationOnly" });
1110
+ * ```
1111
+ *
1112
+ * @remarks
1113
+ * Les entités retournées sont automatiquement liées à l'entité parente via `_linkEntities`,
1114
+ * ce qui permet d'utiliser les méthodes comme `sendRequestToJoinParent()`, `isAdminPending()`, etc.
1115
+ */
1116
+ searchMembers(data: SearchMemberAutocompleteData): Promise<(User | Organization)[]>;
1084
1117
  /**
1085
1118
  * Soumet une demande pour rejoindre l'entité courante (ex. organisation, projet, événement...).
1086
1119
  * Si une invitation est en attente, elle est automatiquement acceptée.
@@ -1381,6 +1414,5 @@ export declare class BaseEntity<TServerData = any> {
1381
1414
  */
1382
1415
  costumEventRequestLoadContextTag(data?: Partial<Omit<CostumEventRequestLoadContextTagData, "pathParams">>): Promise<unknown>;
1383
1416
  coformAnswersSearch(data?: Partial<CoformAnswersSearchData>): Promise<PaginatorPage<any>>;
1384
- searchMembers(data: SearchMemberAutocompleteData): Promise<any[]>;
1385
1417
  }
1386
1418
  export default BaseEntity;
@@ -342,17 +342,30 @@ export declare class User extends BaseEntity<UserItemNormalized> {
342
342
  */
343
343
  entityBySlug(slug: string): Promise<EntityTypes>;
344
344
  /**
345
- * Valide les préconditions communes pour les méthodes de vérification de rôle.
345
+ * Valide les préconditions pour les méthodes nécessitant d'être membre.
346
+ * @private
347
+ * @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
348
+ * @param expectedTypes - Types d'entité parent autorisés.
349
+ * @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
350
+ * @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
351
+ * @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
352
+ * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas membre de l'entité parente.
353
+ * @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
354
+ */
355
+ private _validateMemberPreconditions;
356
+ /**
357
+ * Valide les préconditions pour les méthodes nécessitant d'être administrateur.
346
358
  * @private
347
359
  * @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
348
360
  * @param expectedTypes - Types d'entité parent autorisés.
349
361
  * @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
350
- * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
351
362
  * @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
352
363
  * @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
364
+ * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas membre de l'entité parente.
365
+ * @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
353
366
  * @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
354
367
  */
355
- private _validateRoleCheckPreconditions;
368
+ private _validateAdminPreconditions;
356
369
  /**
357
370
  * Récupère le lien parent pour l'utilisateur actuel.
358
371
  *
@@ -360,6 +373,11 @@ export declare class User extends BaseEntity<UserItemNormalized> {
360
373
  * @private
361
374
  */
362
375
  private _getParentLinkForUser;
376
+ /**
377
+ * Rafraîchit l'utilisateur et l'entité parente après une action.
378
+ * @private
379
+ */
380
+ private _refreshWithParent;
363
381
  /**
364
382
  * Vérifie si l'utilisateur est administrateur de l'entité parente.
365
383
  *
@@ -605,26 +623,6 @@ export declare class User extends BaseEntity<UserItemNormalized> {
605
623
  * ```
606
624
  */
607
625
  validateMemberRequest(): Promise<unknown>;
608
- /**
609
- * Valide une invitation en attente d'acceptation.
610
- *
611
- * Cette méthode permet à un admin de valider une invitation envoyée à un utilisateur
612
- * qui est en attente (`isInviting: true`). Après validation, l'utilisateur devient
613
- * membre actif de l'entité parente.
614
- *
615
- * @returns {Promise<unknown>} La réponse de l'API après validation
616
- * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
617
- * @throws {ApiError} Si l'utilisateur n'a pas d'invitation en attente
618
- *
619
- * @example
620
- * ```typescript
621
- * // Un admin valide une invitation en attente
622
- * const org = await me.organization({ slug: "myOrg" });
623
- * const members = await org.getMembers();
624
- * const invitedUser = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.isInviting);
625
- * await invitedUser.validateInvitation();
626
- * ```
627
- */
628
626
  /**
629
627
  * Valide une demande d'admin en attente.
630
628
  *