@communecter/cocolight-api-client 1.0.51 → 1.0.55

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 (179) hide show
  1. package/dist/401.cocolight-api-client.browser.js +1 -0
  2. package/dist/401.cocolight-api-client.cjs +1 -0
  3. package/dist/401.cocolight-api-client.mjs.js +1 -0
  4. package/dist/588.cocolight-api-client.browser.js +1 -0
  5. package/dist/588.cocolight-api-client.cjs +1 -0
  6. package/dist/588.cocolight-api-client.mjs.js +1 -0
  7. package/dist/593.cocolight-api-client.browser.js +1 -0
  8. package/dist/593.cocolight-api-client.cjs +1 -0
  9. package/dist/593.cocolight-api-client.mjs.js +1 -0
  10. package/dist/839.cocolight-api-client.browser.js +1 -0
  11. package/dist/839.cocolight-api-client.cjs +1 -0
  12. package/dist/839.cocolight-api-client.mjs.js +1 -0
  13. package/dist/cocolight-api-client.browser.js +3 -3
  14. package/dist/cocolight-api-client.cjs +1 -1
  15. package/dist/cocolight-api-client.mjs.js +1 -1
  16. package/dist/cocolight-api-client.vite.mjs.js +1 -1
  17. package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
  18. package/package.json +28 -10
  19. package/src/{Api.js → Api.ts} +99 -91
  20. package/src/{ApiClient.js → ApiClient.ts} +444 -240
  21. package/src/EJSONType.ts +103 -0
  22. package/src/api/{Badge.js → Badge.ts} +56 -45
  23. package/src/api/BaseEntity.ts +3897 -0
  24. package/src/api/Comment.ts +200 -0
  25. package/src/api/{EndpointApi.js → EndpointApi.ts} +365 -299
  26. package/src/api/{EndpointApi.types.d.ts → EndpointApi.types.ts} +166 -9
  27. package/src/api/EntityRegistry.ts +208 -0
  28. package/src/api/Event.ts +332 -0
  29. package/src/api/News.ts +331 -0
  30. package/src/api/{Organization.js → Organization.ts} +155 -119
  31. package/src/api/{Poi.js → Poi.ts} +68 -60
  32. package/src/api/{Project.js → Project.ts} +150 -127
  33. package/src/api/{User.js → User.ts} +321 -256
  34. package/src/api/UserApi.ts +148 -0
  35. package/src/api/serverDataType/Comment.ts +88 -0
  36. package/src/api/serverDataType/Event.ts +80 -0
  37. package/src/api/serverDataType/News.ts +138 -0
  38. package/src/api/serverDataType/Organization.ts +80 -0
  39. package/src/api/serverDataType/Project.ts +71 -0
  40. package/src/api/serverDataType/User.ts +103 -0
  41. package/src/api/serverDataType/common.ts +80 -0
  42. package/src/endpoints.module.ts +2621 -0
  43. package/src/error.ts +86 -0
  44. package/src/index.ts +86 -0
  45. package/src/mixin/UserMixin.ts +4 -0
  46. package/src/types/api-responses.ts +217 -0
  47. package/src/types/entities.ts +22 -0
  48. package/src/types/error-guards.ts +230 -0
  49. package/src/types/index.ts +39 -0
  50. package/src/types/payloads.ts +21 -0
  51. package/src/types/transforms.ts +110 -0
  52. package/src/utils/{FileOfflineStorageStrategy.node.js → FileOfflineStorageStrategy.node.ts} +15 -12
  53. package/src/utils/{FileStorageStrategy.node.js → FileStorageStrategy.node.ts} +17 -14
  54. package/src/utils/MultiServerFileStorageStrategy.node.ts +67 -0
  55. package/src/utils/MultiServerTokenStorageStrategy.ts +139 -0
  56. package/src/utils/{OfflineClientManager.js → OfflineClientManager.ts} +82 -86
  57. package/src/utils/OfflineQueueStorageStrategy.ts +47 -0
  58. package/src/utils/TokenStorage.ts +77 -0
  59. package/src/utils/compat.ts +12 -0
  60. package/src/utils/createDefaultMultiServerTokenStorageStrategy.ts +35 -0
  61. package/src/utils/{createDefaultOfflineStrategy.js → createDefaultOfflineStrategy.ts} +8 -3
  62. package/src/utils/createDefaultTokenStorageStrategy.ts +33 -0
  63. package/src/utils/{reactive.js → reactive.ts} +49 -40
  64. package/src/utils/stream-utils.node.ts +12 -0
  65. package/types/Api.d.ts +87 -0
  66. package/types/Api.d.ts.map +1 -0
  67. package/types/ApiClient.d.ts +437 -0
  68. package/types/ApiClient.d.ts.map +1 -0
  69. package/types/EJSONType.d.ts +53 -0
  70. package/types/EJSONType.d.ts.map +1 -0
  71. package/types/api/Badge.d.ts +24 -0
  72. package/types/api/Badge.d.ts.map +1 -0
  73. package/types/api/BaseEntity.d.ts +1322 -0
  74. package/types/api/BaseEntity.d.ts.map +1 -0
  75. package/types/api/Comment.d.ts +36 -0
  76. package/types/api/EndpointApi.d.ts +985 -0
  77. package/types/api/EndpointApi.d.ts.map +1 -0
  78. package/types/api/EndpointApi.types.d.ts +4233 -0
  79. package/types/api/EntityRegistry.d.ts +24 -0
  80. package/types/api/EntityRegistry.d.ts.map +1 -0
  81. package/types/api/Event.d.ts +122 -0
  82. package/types/api/Event.d.ts.map +1 -0
  83. package/types/api/News.d.ts +77 -0
  84. package/types/api/News.d.ts.map +1 -0
  85. package/types/api/Organization.d.ts +203 -0
  86. package/types/api/Organization.d.ts.map +1 -0
  87. package/types/api/Poi.d.ts +54 -0
  88. package/types/api/Poi.d.ts.map +1 -0
  89. package/types/api/Project.d.ts +180 -0
  90. package/types/api/Project.d.ts.map +1 -0
  91. package/types/api/User.d.ts +332 -0
  92. package/types/api/User.d.ts.map +1 -0
  93. package/types/api/UserApi.d.ts +64 -0
  94. package/types/api/UserApi.d.ts.map +1 -0
  95. package/types/api/serverDataType/Comment.d.ts +83 -0
  96. package/types/api/serverDataType/Event.d.ts +67 -0
  97. package/types/api/serverDataType/News.d.ts +130 -0
  98. package/types/api/serverDataType/Organization.d.ts +65 -0
  99. package/types/api/serverDataType/Organization.d.ts.map +1 -0
  100. package/types/api/serverDataType/Project.d.ts +58 -0
  101. package/types/api/serverDataType/Project.d.ts.map +1 -0
  102. package/types/api/serverDataType/User.d.ts +86 -0
  103. package/types/api/serverDataType/User.d.ts.map +1 -0
  104. package/types/api/serverDataType/common.d.ts +71 -0
  105. package/types/api/serverDataType/common.d.ts.map +1 -0
  106. package/types/endpoints.module.d.ts +20559 -0
  107. package/types/endpoints.module.d.ts.map +1 -0
  108. package/types/error.d.ts +54 -0
  109. package/types/error.d.ts.map +1 -0
  110. package/types/index.d.ts +59 -0
  111. package/types/index.d.ts.map +1 -0
  112. package/types/mixin/UserMixin.d.ts +1 -0
  113. package/types/mixin/UserMixin.d.ts.map +1 -0
  114. package/types/types/api-responses.d.ts +190 -0
  115. package/types/types/api-responses.d.ts.map +1 -0
  116. package/types/types/entities.d.ts +17 -0
  117. package/types/types/entities.d.ts.map +1 -0
  118. package/types/types/error-guards.d.ts +99 -0
  119. package/types/types/error-guards.d.ts.map +1 -0
  120. package/types/types/index.d.ts +7 -0
  121. package/types/types/payloads.d.ts +17 -0
  122. package/types/types/payloads.d.ts.map +1 -0
  123. package/types/types/transforms.d.ts +79 -0
  124. package/types/types/transforms.d.ts.map +1 -0
  125. package/types/utils/FileOfflineStorageStrategy.node.d.ts +11 -0
  126. package/types/utils/FileOfflineStorageStrategy.node.d.ts.map +1 -0
  127. package/types/utils/FileStorageStrategy.node.d.ts +14 -0
  128. package/types/utils/FileStorageStrategy.node.d.ts.map +1 -0
  129. package/types/utils/MultiServerFileStorageStrategy.node.d.ts +17 -0
  130. package/types/utils/MultiServerFileStorageStrategy.node.d.ts.map +1 -0
  131. package/types/utils/MultiServerTokenStorageStrategy.d.ts +44 -0
  132. package/types/utils/MultiServerTokenStorageStrategy.d.ts.map +1 -0
  133. package/types/utils/OfflineClientManager.d.ts +58 -0
  134. package/types/utils/OfflineClientManager.d.ts.map +1 -0
  135. package/types/utils/OfflineQueueStorageStrategy.d.ts +16 -0
  136. package/types/utils/OfflineQueueStorageStrategy.d.ts.map +1 -0
  137. package/types/utils/TokenStorage.d.ts +26 -0
  138. package/types/utils/TokenStorage.d.ts.map +1 -0
  139. package/types/utils/compat.d.ts +4 -0
  140. package/types/utils/compat.d.ts.map +1 -0
  141. package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts +2 -0
  142. package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts.map +1 -0
  143. package/types/utils/createDefaultOfflineStrategy.d.ts +2 -0
  144. package/types/utils/createDefaultOfflineStrategy.d.ts.map +1 -0
  145. package/types/utils/createDefaultTokenStorageStrategy.d.ts +2 -0
  146. package/types/utils/createDefaultTokenStorageStrategy.d.ts.map +1 -0
  147. package/types/utils/reactive.d.ts +54 -0
  148. package/types/utils/reactive.d.ts.map +1 -0
  149. package/types/utils/stream-utils.node.d.ts +3 -0
  150. package/types/utils/stream-utils.node.d.ts.map +1 -0
  151. package/dist/123.cocolight-api-client.browser.js +0 -1
  152. package/dist/123.cocolight-api-client.cjs +0 -1
  153. package/dist/22.cocolight-api-client.mjs.js +0 -1
  154. package/dist/339.cocolight-api-client.mjs.js +0 -1
  155. package/dist/394.cocolight-api-client.browser.js +0 -1
  156. package/dist/394.cocolight-api-client.cjs +0 -1
  157. package/dist/405.cocolight-api-client.browser.js +0 -1
  158. package/dist/405.cocolight-api-client.cjs +0 -1
  159. package/dist/774.cocolight-api-client.mjs.js +0 -1
  160. package/dist/790.cocolight-api-client.mjs.js +0 -1
  161. package/dist/931.cocolight-api-client.browser.js +0 -1
  162. package/dist/931.cocolight-api-client.cjs +0 -1
  163. package/src/EJSONType.js +0 -53
  164. package/src/api/BaseEntity.js +0 -2828
  165. package/src/api/EntityRegistry.js +0 -152
  166. package/src/api/Event.js +0 -226
  167. package/src/api/News.js +0 -244
  168. package/src/api/UserApi.js +0 -81
  169. package/src/endpoints.module.js +0 -5
  170. package/src/error.js +0 -68
  171. package/src/index.js +0 -34
  172. package/src/mixin/UserMixin.js +0 -8
  173. package/src/utils/MultiServerFileStorageStrategy.node.js +0 -65
  174. package/src/utils/MultiServerTokenStorageStrategy.js +0 -131
  175. package/src/utils/OfflineQueueStorageStrategy.js +0 -51
  176. package/src/utils/TokenStorage.js +0 -93
  177. package/src/utils/createDefaultMultiServerTokenStorageStrategy.js +0 -45
  178. package/src/utils/createDefaultTokenStorageStrategy.js +0 -43
  179. package/src/utils/stream-utils.node.js +0 -10
@@ -1,31 +1,99 @@
1
1
  import { EventEmitter } from "events";
2
2
 
3
3
  import { AggregateAjvError } from "@segment/ajv-human-errors";
4
- import Ajv from "ajv";
5
- import addFormats from "ajv-formats";
6
4
  import axios from "axios";
7
- import axiosRetry from "axios-retry";
8
- import EJSON from "ejson";
5
+ import axiosRetryImport from "axios-retry";
9
6
  import { jwtDecode } from "jwt-decode";
10
- import pino from "pino";
7
+
11
8
 
12
9
  import MongoID from "./EJSONType.js";
13
10
  import endpointsJson from "./endpoints.module.js";
14
11
  import { ApiClientError, ApiResponseError, ApiValidationError, CircuitBreakerError } from "./error.js";
12
+ import { getErrorMessage, getErrorStatusCode, hasResponse } from "./types/error-guards.js";
13
+ import { Ajv, addFormats, pino, EJSON } from "./utils/compat.js";
15
14
  import { MultiServerTokenStorageStrategy } from "./utils/MultiServerTokenStorageStrategy.js";
16
- import { MemoryStorageStrategy } from "./utils/TokenStorage.js";
15
+ import { MemoryStorageStrategy, type TokenStorageStrategy } from "./utils/TokenStorage.js";
16
+
17
+ import type { ApiErrorDetails } from "./types/api-responses.js";
18
+ import type OfflineClientManager from "./utils/OfflineClientManager.js";
19
+ import type { ValidateFunction } from "ajv";
20
+ import type { AxiosInstance } from "axios";
21
+ import type { JwtPayload } from "jwt-decode";
22
+
23
+ // Fix pour le typage de axios-retry avec les modules CommonJS
24
+ interface AxiosRetryType {
25
+ (axiosInstance: AxiosInstance, config?: any): void;
26
+ exponentialDelay: (retryNumber?: number, error?: Error, delayFactor?: number) => number;
27
+ isNetworkError: (error: Error) => boolean;
28
+ isRetryableError: (error: Error) => boolean;
29
+ }
30
+ const axiosRetry = axiosRetryImport as unknown as AxiosRetryType;
31
+
32
+ /**
33
+ * Résultat retourné quand une requête est **mise en file** (offline ou breaker).
34
+ * Permet de typer précisément les chemins « non-réseau » de `callEndpoint`.
35
+ */
36
+ export interface ApiClientOfflineEnqueueResult {
37
+ data: null;
38
+ offline?: true; // Présent si l'enqueue est dû au mode offline.
39
+ breaker?: true; // Présent si l'enqueue est dû au circuit breaker.
40
+ }
41
+
42
+ /**
43
+ * Options de configuration pour ApiClient
44
+ */
45
+ export interface ApiClientOptions {
46
+ baseURL: string;
47
+ accessToken?: string | null;
48
+ refreshToken?: string | null;
49
+ refreshUrl?: string;
50
+ endpoints?: any[];
51
+ timeout?: number;
52
+ debug?: boolean;
53
+ maxRetries?: number;
54
+ circuitBreakerThreshold?: number;
55
+ circuitBreakerResetTime?: number;
56
+ fromJSONValue?: boolean;
57
+ tokenStorageStrategy?: TokenStorageStrategy | null;
58
+ }
17
59
 
60
+ /**
61
+ * Structure des données pour les appels d'endpoint
62
+ */
63
+ export interface CallEndpointData {
64
+ pathParams?: Record<string, any>;
65
+ [key: string]: any;
66
+ }
18
67
 
19
- EJSON.addType("oid", value => {
20
- return new MongoID.ObjectID(value);
68
+ /**
69
+ * Résultat d'un appel d'endpoint réussi
70
+ */
71
+ export interface CallEndpointResult<T = any> {
72
+ data: T;
73
+ offline?: never;
74
+ breaker?: never;
75
+ }
76
+
77
+ /**
78
+ * Union type pour tous les résultats possibles de callEndpoint
79
+ */
80
+ export type CallEndpointResponse<T = any> = CallEndpointResult<T> | ApiClientOfflineEnqueueResult;
81
+
82
+ EJSON.addType("oid", (value: any) => {
83
+ if (typeof value === "string") {
84
+ return new MongoID.ObjectID(value);
85
+ }
86
+ if (typeof value === "undefined") {
87
+ return new MongoID.ObjectID();
88
+ }
89
+ throw new Error("Expected string value for ObjectID");
21
90
  });
22
91
 
92
+
23
93
  /**
24
94
  * Client générique pour consommer une API REST avec validation AJV, gestion des tokens,
25
95
  * circuit breaker, retry automatique, et support offline.
26
96
  *
27
- * @extends EventEmitter
28
- *
29
97
  * @fires ApiClient#retryAttempt
30
98
  * @fires ApiClient#queuedOffline
31
99
  * @fires ApiClient#circuitBreakerOpen
@@ -38,21 +106,36 @@ EJSON.addType("oid", value => {
38
106
  * @fires ApiClient#userLoggedIn
39
107
  */
40
108
  export default class ApiClient extends EventEmitter {
41
- /**
42
- * @param {Object} options
43
- * @param {string} options.baseURL
44
- * @param {string} [options.accessToken]
45
- * @param {string} [options.refreshToken]
46
- * @param {string} [options.refreshUrl=/api/cocolight/refreshtoken]
47
- * @param {Array} [options.endpoints=endpointsJson.endpoints]
48
- * @param {number} [options.timeout=30000]
49
- * @param {boolean} [options.debug=false]
50
- * @param {number} [options.maxRetries=0] - Nombre de tentatives auto (axios-retry)
51
- * @param {number} [options.circuitBreakerThreshold=5] - Nb d'erreurs avant de bloquer
52
- * @param {number} [options.circuitBreakerResetTime=60000] - Ms avant de reset le breaker
53
- * @param {boolean} [options.fromJSONValue=true] - Si true, les données sont transformées en EJSON
54
- * @param {TokenStorageStrategy} [options.tokenStorageStrategy=null] - Stratégie de stockage des tokens
55
- */
109
+ // Public properties
110
+ public readonly __entityTag: string = "ApiClient";
111
+ public readonly userId: string | null = null;
112
+
113
+ // Private properties
114
+ private readonly _baseURL: string;
115
+ private readonly _refreshUrl: string;
116
+ private readonly _endpoints: any[];
117
+ private readonly _debug: boolean;
118
+ private readonly _fromJSONValue: boolean;
119
+ private readonly _tokenStorage: TokenStorageStrategy;
120
+
121
+ private _offlineClientManager: OfflineClientManager | null = null;
122
+ private _ajv: any;
123
+ public _logger: any;
124
+ private _client: AxiosInstance;
125
+
126
+ // Circuit breaker properties
127
+ private readonly _breakerThreshold: number;
128
+ private readonly _breakerResetTime: number;
129
+ private _breakerErrorCount: number = 0;
130
+ private _breakerOpen: boolean = false;
131
+ private _lastBreakerOpenTime: number | null = null;
132
+
133
+ // Token properties
134
+ private _accessToken: string | null = null;
135
+ private _refreshToken: string | null = null;
136
+
137
+ // Internal userId setter
138
+ private _setUserId: (id: string | null) => void;
56
139
  constructor({
57
140
  baseURL,
58
141
  accessToken,
@@ -66,24 +149,23 @@ export default class ApiClient extends EventEmitter {
66
149
  circuitBreakerResetTime = 60000,
67
150
  fromJSONValue = true,
68
151
  tokenStorageStrategy = null
69
- } = {}) {
152
+ }: ApiClientOptions) {
70
153
  super(); // EventEmitter
71
154
 
72
155
  if (!baseURL) {
73
156
  throw new ApiClientError("Le paramètre \"baseURL\" est obligatoire.", 500);
74
157
  }
75
158
 
76
- this.__entityTag = "ApiClient";
77
-
78
159
  this._baseURL = baseURL;
79
160
  this._refreshUrl = refreshUrl;
80
- this._endpoints = endpoints;
161
+ this._endpoints = endpoints || endpointsJson.endpoints;
81
162
  this._debug = debug;
82
- let _userId = null;
83
- this._offlineClientManager = null;
84
-
85
- // Active la transformation des données en EJSON globalement
86
163
  this._fromJSONValue = fromJSONValue;
164
+ this._breakerThreshold = circuitBreakerThreshold;
165
+ this._breakerResetTime = circuitBreakerResetTime;
166
+
167
+ // Setup userId with getter/setter pattern
168
+ let _userId: string | null = null;
87
169
 
88
170
  Object.defineProperty(this, "userId", {
89
171
  get: () => _userId,
@@ -93,9 +175,9 @@ export default class ApiClient extends EventEmitter {
93
175
  enumerable: true
94
176
  });
95
177
 
96
- this._setUserId = (id) => {
178
+ this._setUserId = (id: string | null) => {
97
179
  _userId = id;
98
- this._logger.debug(`[ApiClient] userId set: ${id}`);
180
+ this._logger?.debug(`[ApiClient] userId set: ${id}`);
99
181
  };
100
182
 
101
183
  // AJV
@@ -131,11 +213,11 @@ export default class ApiClient extends EventEmitter {
131
213
  axiosRetry(this._client, {
132
214
  retries: maxRetries,
133
215
  retryDelay: axiosRetry.exponentialDelay,
134
- retryCondition: (error) => {
216
+ retryCondition: (error: any) => {
135
217
  // Retry sur erreurs 5xx ou erreurs réseau
136
218
  return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error);
137
219
  },
138
- onRetry: (retryCount, error, requestConfig) => {
220
+ onRetry: (retryCount: number, error: any, requestConfig: any) => {
139
221
  this._logger.warn(`[Retry] Tentative #${retryCount} pour ${requestConfig?.url}`);
140
222
  this.emit("retryAttempt", { retryCount, url: requestConfig?.url });
141
223
  }
@@ -143,20 +225,10 @@ export default class ApiClient extends EventEmitter {
143
225
  this._logger.info(`[ApiClient] Retry activé : ${maxRetries} max`);
144
226
  }
145
227
 
146
- // Circuit breaker (simplifié)
147
- this._breakerThreshold = circuitBreakerThreshold;
148
- this._breakerResetTime = circuitBreakerResetTime;
149
- this._breakerErrorCount = 0;
150
- this._breakerOpen = false;
151
- this._lastBreakerOpenTime = null;
152
-
153
- this._accessToken = null;
154
- this._refreshToken = null;
155
-
156
228
  if (tokenStorageStrategy instanceof MultiServerTokenStorageStrategy) {
157
229
  tokenStorageStrategy.use(this._baseURL);
158
230
  }
159
-
231
+
160
232
  this._tokenStorage = tokenStorageStrategy || new MemoryStorageStrategy();
161
233
 
162
234
  if (
@@ -170,7 +242,7 @@ export default class ApiClient extends EventEmitter {
170
242
  }
171
243
 
172
244
  // Applique un token initial s'il est fourni
173
- if(refreshToken){
245
+ if (refreshToken) {
174
246
  this.setRefreshToken(refreshToken);
175
247
  }
176
248
 
@@ -212,6 +284,7 @@ export default class ApiClient extends EventEmitter {
212
284
  this._logger.info("[ApiClient] Token rafraîchi avec succès.");
213
285
 
214
286
  // 🔑 Mise à jour EXPLICITE du header Authorization dans la requête originale
287
+ originalRequest.headers = originalRequest.headers ?? {};
215
288
  originalRequest.headers["Authorization"] = "Bearer " + this.getToken();
216
289
 
217
290
  this._logger.info("[ApiClient] Retente la requête originale avec le nouveau token.");
@@ -222,7 +295,11 @@ export default class ApiClient extends EventEmitter {
222
295
  }
223
296
  } catch (err) {
224
297
  this.resetSession();
225
- throw new ApiClientError("Erreur lors du rafraîchissement du token.", 401, err);
298
+ const errorDetails: ApiErrorDetails = {
299
+ code: getErrorStatusCode(err) || 401,
300
+ message: getErrorMessage(err)
301
+ };
302
+ throw new ApiClientError("Erreur lors du rafraîchissement du token.", 401, errorDetails);
226
303
  }
227
304
  }
228
305
 
@@ -234,13 +311,15 @@ export default class ApiClient extends EventEmitter {
234
311
 
235
312
  /**
236
313
  * Sets the access token for the API client and updates the authorization header.
237
- *
238
- * @param {string} token - The access token to be set.
239
314
  */
240
- setToken(token) {
315
+ setToken(token: string | null): void {
241
316
  this._accessToken = token;
242
- this._tokenStorage.setAccessToken(token);
243
- this._client.defaults.headers.common["Authorization"] = "Bearer " + token;
317
+ this._tokenStorage.setAccessToken(token ?? "");
318
+ if (token) {
319
+ this._client.defaults.headers.common["Authorization"] = "Bearer " + token;
320
+ } else {
321
+ delete this._client.defaults.headers.common["Authorization"];
322
+ }
244
323
  // Extrait l'id depuis le token et le stocke si disponible
245
324
  const userId = this._getIdFromToken(token);
246
325
  if (userId) {
@@ -252,21 +331,17 @@ export default class ApiClient extends EventEmitter {
252
331
 
253
332
  /**
254
333
  * Retrieves the current access token.
255
- *
256
- * @returns {string} The access token.
257
334
  */
258
- getToken() {
335
+ getToken(): string | null {
259
336
  return this._accessToken;
260
337
  }
261
338
 
262
339
  /**
263
340
  * Sets the refresh token for the API client.
264
- *
265
- * @param {string} refreshToken - The refresh token to be set.
266
341
  */
267
- setRefreshToken(token) {
342
+ setRefreshToken(token: string | null): void {
268
343
  this._refreshToken = token;
269
- this._tokenStorage.setRefreshToken(token);
344
+ this._tokenStorage.setRefreshToken(token ?? "");
270
345
  if(this.userId === null){
271
346
  // Extrait l'id depuis le token et le stocke si disponible
272
347
  const userId = this._getIdFromToken(token);
@@ -280,34 +355,27 @@ export default class ApiClient extends EventEmitter {
280
355
 
281
356
  /**
282
357
  * Retrieves the current refresh token.
283
- *
284
- * @returns {string} The refresh token.
285
358
  */
286
- getRefreshToken() {
359
+ getRefreshToken(): string | null {
287
360
  return this._refreshToken;
288
361
  }
289
362
 
290
363
  /**
291
364
  * Indique si le client est connecté.
292
365
  * On considère que le client est connecté si un token d'accès (_accessToken) est défini.
293
- *
294
- * @returns {boolean} True si connecté, false sinon.
295
366
  */
296
- get isConnected() {
367
+ get isConnected(): boolean {
297
368
  return !!this._accessToken;
298
369
  }
299
370
 
300
371
  /**
301
- * Extrait l'identifiant depuis un JWT.
302
- *
303
- * @param {string} token - Le token JWT (accessToken ou refreshToken).
304
- * @returns {string|null} L'identifiant extrait ou null si non trouvé.
305
- */
306
- _getIdFromToken(token) {
372
+ * Extrait l'identifiant depuis un JWT.
373
+ */
374
+ private _getIdFromToken(token: string | null): string | null {
307
375
  if (!token) return null;
308
376
  try {
309
377
  // Décodage du token grâce à jwt-decode
310
- const payload = jwtDecode(token);
378
+ const payload = jwtDecode<JwtPayload & { id?: string; userId?: string }>(token);
311
379
  // L'identifiant peut être dans "id" ou "userId"
312
380
  this._logger.debug("[ApiClient] Payload décodé :", payload);
313
381
  return payload.id || payload.userId || null;
@@ -318,10 +386,10 @@ export default class ApiClient extends EventEmitter {
318
386
  }
319
387
 
320
388
  /**
321
- * Méthode simplifiée de refresh (en JSON).
389
+ * Méthode simplifiée de refresh (en JSON).
322
390
  * Emet un event refreshSuccess si ça marche
323
391
  */
324
- async _refreshAccessToken() {
392
+ private async _refreshAccessToken(): Promise<boolean> {
325
393
  if (!this._refreshToken) return false;
326
394
 
327
395
  const refreshClient = axios.create({
@@ -345,22 +413,21 @@ export default class ApiClient extends EventEmitter {
345
413
  return false;
346
414
  } catch (err) {
347
415
  // Si on a une erreur, on reset la session
348
- this.emit("refreshFailed", { error: err.message });
416
+ this.emit("refreshFailed", { error: getErrorMessage(err) });
349
417
  this.resetSession();
350
- this._logger.error(`[ApiClient] Refresh Error : ${err.message}`);
418
+ this._logger.error(`[ApiClient] Refresh Error : ${getErrorMessage(err)}`);
351
419
  return false;
352
420
  }
353
421
  }
354
422
 
355
423
  /**
356
424
  * checkCircuitBreaker : vérifie si on peut appeler l'API ou non
357
- * si le breaker est \"open\", on regarde si on peut \"reset\"
425
+ * si le breaker est "open", on regarde si on peut "reset"
358
426
  */
359
- _checkCircuitBreaker() {
427
+ private _checkCircuitBreaker(): boolean {
360
428
  if (!this._breakerOpen) return true;
361
429
  const now = Date.now();
362
- if (now - this._lastBreakerOpenTime > this._breakerResetTime) {
363
- // On reset
430
+ if (this._lastBreakerOpenTime != null && (now - this._lastBreakerOpenTime) > this._breakerResetTime) {
364
431
  this._breakerOpen = false;
365
432
  this._breakerErrorCount = 0;
366
433
  this._logger.warn("[ApiClient] Circuit breaker réinitialisé");
@@ -371,10 +438,10 @@ export default class ApiClient extends EventEmitter {
371
438
  }
372
439
 
373
440
  /**
374
- * updateCircuitBreaker : incremente le compteur d'erreurs
375
- * si on dépasse le threshold, on \"open\" le breaker.
441
+ * updateCircuitBreaker : incremente le compteur d'erreurs
442
+ * si on dépasse le threshold, on "open" le breaker.
376
443
  */
377
- _updateCircuitBreakerError() {
444
+ private _updateCircuitBreakerError(): void {
378
445
  this._breakerErrorCount += 1;
379
446
  this._logger.warn(`[ApiClient] Erreur #${this._breakerErrorCount} sur ${this._breakerThreshold}`);
380
447
  if (this._breakerErrorCount >= this._breakerThreshold) {
@@ -388,7 +455,7 @@ export default class ApiClient extends EventEmitter {
388
455
  /**
389
456
  * resetCircuitBreaker : en cas de succès on reset le compteur
390
457
  */
391
- _resetCircuitBreakerSuccess() {
458
+ private _resetCircuitBreakerSuccess(): void {
392
459
  this._breakerErrorCount = 0;
393
460
  if (this._breakerOpen) {
394
461
  this._breakerOpen = false;
@@ -396,7 +463,7 @@ export default class ApiClient extends EventEmitter {
396
463
  }
397
464
  }
398
465
 
399
- static stripNullsInPlace(obj) {
466
+ static stripNullsInPlace(obj: any): any {
400
467
  // Si l'objet semble être un "fichier uploadé", c'est-à-dire qu'il possède une propriété "value" qui est un stream, on le laisse intact.
401
468
  if (obj && typeof obj === "object" && obj.value && typeof obj.value.pipe === "function") {
402
469
  return obj;
@@ -424,13 +491,13 @@ export default class ApiClient extends EventEmitter {
424
491
  return obj;
425
492
  }
426
493
 
427
- _resolveSpecialValuesInPlace(obj, pathParams = {}) {
428
- const aliasMap = {
494
+ private _resolveSpecialValuesInPlace(obj: any, pathParams: any = {}) {
495
+ const aliasMap: any = {
429
496
  userId: () => this.userId,
430
497
  accessToken: () => this._accessToken,
431
498
  refreshToken: () => this._refreshToken,
432
499
  baseURL: () => this._baseURL,
433
- pathParams: (subPath) => {
500
+ pathParams: (subPath: any) => {
434
501
  // Si la valeur existe dans pathParams, on la retourne
435
502
  const value = this._getValueByPath(pathParams, subPath);
436
503
  return value !== undefined && value !== null ? value : undefined;
@@ -440,7 +507,7 @@ export default class ApiClient extends EventEmitter {
440
507
  // Expression régulière qui capture les alias sous forme délimitée @{...} ou simple @...
441
508
  const regex = /@(?:\{([^}]+)\}|([\w.]+))/g;
442
509
 
443
- const resolveString = (str) => {
510
+ const resolveString = (str: any) => {
444
511
  if (typeof str !== "string") return str;
445
512
  return str.replace(regex, (_, delimitedAlias, plainAlias) => {
446
513
  // Si alias délimité, on traite toute la chaîne à l'intérieur des accolades.
@@ -489,17 +556,17 @@ export default class ApiClient extends EventEmitter {
489
556
 
490
557
 
491
558
  // Vérifie si l'objet est un binaire (par exemple un flux ou un Buffer)
492
- const isBinary = (input) => {
559
+ const isBinary = (input: any) => {
493
560
  return input && typeof input === "object" &&
494
561
  (typeof input.pipe === "function" || (typeof Buffer !== "undefined" && input instanceof Buffer));
495
562
  };
496
-
563
+
497
564
  // Vérifie si l'objet est un "plain object"
498
- const isPlainObject = (input) => {
565
+ const isPlainObject = (input: any) => {
499
566
  return Object.prototype.toString.call(input) === "[object Object]";
500
567
  };
501
-
502
- const internal = (input) => {
568
+
569
+ const internal = (input: any): any => {
503
570
  // Si c'est un binaire, ne rien modifier
504
571
  if (isBinary(input)) {
505
572
  return input;
@@ -510,7 +577,7 @@ export default class ApiClient extends EventEmitter {
510
577
  }
511
578
  // Si c'est un plain object, on crée un nouvel objet avec remplacement dans les clés et valeurs
512
579
  if (isPlainObject(input)) {
513
- const result = {};
580
+ const result: any = {};
514
581
  for (const [key, value] of Object.entries(input)) {
515
582
  const resolvedKey = resolveString(key);
516
583
  result[resolvedKey] = internal(value);
@@ -529,22 +596,22 @@ export default class ApiClient extends EventEmitter {
529
596
  }
530
597
 
531
598
 
532
- _cleanSchemaLeftoverAlias(obj) {
599
+ private _cleanSchemaLeftoverAlias(obj: any): any {
533
600
  // Expression régulière qui détecte n'importe quel alias du type "@quelqueChose"
534
601
  const aliasRegex = /@\w+/;
535
602
 
536
603
  if (Array.isArray(obj)) {
537
- return obj.map(item => this._cleanSchemaLeftoverAlias(item));
604
+ return obj.map((item: any) => this._cleanSchemaLeftoverAlias(item));
538
605
  } else if (obj && typeof obj === "object") {
539
- const newObj = {};
606
+ const newObj: any = {};
540
607
  for (const key of Object.keys(obj)) {
541
608
  const val = obj[key];
542
-
609
+
543
610
  // Si le nom de la clé contient un alias (exemple : "@userId", "@anything"), on ne la copie pas
544
611
  if (aliasRegex.test(key)) {
545
612
  continue;
546
613
  }
547
-
614
+
548
615
  // Si la propriété s'appelle "default"
549
616
  if (key === "default") {
550
617
  // Si la valeur est une chaîne et qu'elle contient un alias, on ne la copie pas
@@ -561,12 +628,12 @@ export default class ApiClient extends EventEmitter {
561
628
  continue;
562
629
  }
563
630
  }
564
-
631
+
565
632
  // Si la valeur est une chaîne contenant un alias, on ne la copie pas
566
633
  if (typeof val === "string" && aliasRegex.test(val)) {
567
634
  continue;
568
635
  }
569
-
636
+
570
637
  // Sinon, on nettoie récursivement la valeur
571
638
  newObj[key] = this._cleanSchemaLeftoverAlias(val);
572
639
  }
@@ -579,32 +646,39 @@ export default class ApiClient extends EventEmitter {
579
646
  * Safely calls an asynchronous function and handles any errors that occur.
580
647
  * Logs the error message using the instance's logger before re-throwing the error.
581
648
  *
582
- * @param {Function} fn - The asynchronous function to be called.
583
- * @param {...any} args - The arguments to pass to the function.
584
- * @returns {Promise<any>} The result of the asynchronous function.
649
+ * @param fn - The asynchronous function to be called.
650
+ * @param args - The arguments to pass to the function.
651
+ * @returns The result of the asynchronous function.
585
652
  * @throws {Error} Re-throws any error that occurs during the function execution.
586
653
  */
587
- async safeCall(fn, ...args) {
654
+ async safeCall(fn: (...args: any[]) => Promise<any>, ...args: any[]) {
588
655
  try {
589
656
  return await fn(...args);
590
657
  } catch (error) {
591
- this._logger.error(`[ApiClient.safeCall] Erreur: ${error.message}`);
658
+ this._logger.error(`[ApiClient.safeCall] Erreur: ${getErrorMessage(error)}`);
592
659
  throw error;
593
660
  }
594
661
  }
595
662
 
596
663
  /**
597
- * Calls an API endpoint with the specified parameters.
664
+ * Appelle un endpoint avec validations AJV, auth et circuit breaker.
665
+ * En cas d’indisponibilité (offline ou breaker), **met en file** l’action via
666
+ * `_offlineClientManager` si présent.
598
667
  *
599
- * @param {string} constant - The constant representing the endpoint to call.
600
- * @param {Object} [data={}] - The data to send with the request.
601
- * @param {boolean|function} [transformResponseData=true] - Whether to transform the response data or a function to transform it.
602
- * @param {boolean} [validateResponseSchema=true] - Whether to validate the response schema.
603
- * @returns {Promise<Object>} The response from the API.
668
+ * @param constant - The constant representing the endpoint to call.
669
+ * @param data - Données envoyées (incluant éventuellement `pathParams`).
670
+ * @param transformResponseData - `true` (transforme via `_transformData`), `false`, ou une fonction `(data) => any`.
671
+ * @param validateResponseSchema - Whether to validate the response schema.
672
+ * @returns The response from the endpoint call, or an enqueue result if offline or breaker is active.
604
673
  * @throws {CircuitBreakerError} If the circuit breaker is activated.
605
674
  * @throws {ApiClientError} If the endpoint is not found, token is required but not provided, or validation fails.
606
675
  */
607
- async callEndpoint(constant, data = {}, transformResponseData = true, validateResponseSchema = true) {
676
+ async callEndpoint<T = any>(
677
+ constant: string,
678
+ data: CallEndpointData = {},
679
+ transformResponseData: boolean | ((data: any) => any) = true,
680
+ validateResponseSchema: boolean = true
681
+ ): Promise<CallEndpointResponse<T>> {
608
682
 
609
683
  const endpoint = this._endpoints.find((ep) => ep.constant === constant);
610
684
  if (!endpoint) {
@@ -623,8 +697,8 @@ export default class ApiClient extends EventEmitter {
623
697
 
624
698
  const lowerMethod = (method || "GET").toLowerCase();
625
699
  const realContentType = contentType || "application/json";
626
- const headers = { "Content-Type": realContentType };
627
-
700
+ const headers: Record<string, string> = { "Content-Type": realContentType };
701
+
628
702
  // Auth headers
629
703
  if (this._accessToken) {
630
704
  if (auth === "bearer") {
@@ -650,19 +724,19 @@ export default class ApiClient extends EventEmitter {
650
724
  }
651
725
 
652
726
  const pathParams = data.pathParams || {};
653
- const validatePathParams = this._ajv.compile(schemaToCompile);
727
+ const validatePathParams: ValidateFunction = this._ajv.compile(schemaToCompile);
654
728
  const valid = validatePathParams(pathParams);
655
729
 
656
730
  if (!valid) {
657
- const errorMessages = this._ajvErrorHuman(validatePathParams.errors);
731
+ const errorMessages = this._ajvErrorHuman(validatePathParams.errors ?? []);
658
732
  this.emit("validationError", { stage: "pathParams", errors: validatePathParams.errors });
659
733
  throw new ApiValidationError(`callEndpoint: ${constant} - Path parameter validation failed.`, 400, errorMessages, validatePathParams.errors);
660
734
  }
661
735
 
662
736
  resolvedParams = this._resolveSpecialValuesInPlace(pathParams);
663
-
664
- resolvedPath = resolvedPath.replace(/\{(\w+)\}/g, (_, key) => {
665
- const val = resolvedParams[key];
737
+
738
+ resolvedPath = resolvedPath.replace(/\{(\w+)\}/g, (_: any, key: string) => {
739
+ const val = (resolvedParams as any)[key];
666
740
  if (val !== undefined) return encodeURIComponent(val);
667
741
  throw new ApiClientError(`Path param manquant ou non résolu : {${key}}`, 400);
668
742
  });
@@ -676,11 +750,19 @@ export default class ApiClient extends EventEmitter {
676
750
  schemaToCompile = this._cleanSchemaLeftoverAlias(requestSchema);
677
751
  }
678
752
 
679
- const dataForValidation = { ...data };
753
+ let dataForValidation = { ...data };
680
754
  delete dataForValidation.pathParams;
755
+
756
+ // Normalize Dates to ISO strings for x-www-form-urlencoded BEFORE validation
757
+ if (realContentType === "application/x-www-form-urlencoded") {
758
+ dataForValidation = ApiClient.normalizeDatesForValidation(dataForValidation);
759
+ // For fields with format:"date", extract only YYYY-MM-DD
760
+ dataForValidation = ApiClient.normalizeSchemaDateFields(dataForValidation, schemaToCompile);
761
+ }
762
+
681
763
  const cleanedData = ApiClient.stripNullsInPlace(dataForValidation);
682
-
683
- const validateRequest = this._ajv.compile(schemaToCompile);
764
+
765
+ const validateRequest: ValidateFunction = this._ajv.compile(schemaToCompile);
684
766
  const valid = validateRequest(cleanedData);
685
767
  if (!valid) {
686
768
  const errorMessages = validateRequest.errors ? this._ajvErrorHuman(validateRequest.errors) : [];
@@ -709,7 +791,8 @@ export default class ApiClient extends EventEmitter {
709
791
  data
710
792
  });
711
793
 
712
- return { data: null, offline: true };
794
+ const enqueued = { data: null, offline: true as const };
795
+ return enqueued;
713
796
  } else {
714
797
  this._logger.warn("[ApiClient] Mode dégradé actif mais offlineManager non initialisé correctement");
715
798
  throw new ApiClientError("Mode hors-ligne actif, mais gestionnaire offline non disponible.", 503);
@@ -737,7 +820,8 @@ export default class ApiClient extends EventEmitter {
737
820
  data
738
821
  });
739
822
 
740
- return { data: null, breaker: true };
823
+ const enqueued = { data: null, breaker: true as const };
824
+ return enqueued;
741
825
  }
742
826
 
743
827
  throw new CircuitBreakerError("Le circuit breaker est activé, impossible d'appeler l'API");
@@ -762,15 +846,15 @@ export default class ApiClient extends EventEmitter {
762
846
  });
763
847
 
764
848
  if (validateResponseSchema) {
765
- const status = response.status.toString();
766
- const schema = responses?.[status];
849
+ const statusStr = String(response.status);
850
+ const schema = responses?.[statusStr];
767
851
  if (schema) {
768
- const validateResponse = this._ajv.compile(schema);
852
+ const validateResponse: ValidateFunction = this._ajv.compile(schema);
769
853
  const valid = validateResponse(response.data);
770
854
  if (!valid) {
771
- const errorMessages = this._ajvErrorHuman(validateResponse.errors);
855
+ const errorMessages = this._ajvErrorHuman(validateResponse.errors ?? []);
772
856
  this.emit("validationError", { stage: "response", errors: validateResponse.errors });
773
- throw new ApiValidationError("Response validation failed.", status, errorMessages, validateResponse.errors);
857
+ throw new ApiValidationError("Response validation failed.", response.status, errorMessages, validateResponse.errors);
774
858
  }
775
859
  }
776
860
  }
@@ -785,7 +869,7 @@ export default class ApiClient extends EventEmitter {
785
869
 
786
870
  // postActions éventuelles
787
871
  if (Array.isArray(endpoint.postActions)) {
788
- endpoint.postActions.forEach(action => {
872
+ endpoint.postActions.forEach((action: any) => {
789
873
  const value = action.path ? this._getValueByPath(response.data, action.path) : null;
790
874
 
791
875
  switch (action.type) {
@@ -813,8 +897,8 @@ export default class ApiClient extends EventEmitter {
813
897
  break;
814
898
 
815
899
  case "callMethod":
816
- if (typeof this[action.method] === "function") {
817
- this[action.method]();
900
+ if (typeof (this as any)[action.method] === "function") {
901
+ (this as any)[action.method]();
818
902
  this._logger.debug(`[ApiClient] Méthode appelée : ${action.method}`);
819
903
  } else {
820
904
  this._logger.warn(`[ApiClient] Méthode inconnue : ${action.method}`);
@@ -831,14 +915,16 @@ export default class ApiClient extends EventEmitter {
831
915
  return response;
832
916
  } catch (error) {
833
917
  this._updateCircuitBreakerError();
834
- this._logger.error(`[ApiClient] Erreur lors de l'appel de ${constant}: ${error.message}`);
918
+ this._logger.error(`[ApiClient] Erreur lors de l'appel de ${constant}: ${getErrorMessage(error)}`);
835
919
  if(error instanceof ApiValidationError) {
836
920
  throw error;
837
921
  } else {
922
+ const statusCode = hasResponse(error) ? error.response.status : 500;
923
+ const responseData = hasResponse(error) ? error.response.data : null;
838
924
  throw new ApiClientError(
839
- `Erreur lors de l'appel de l'API : ${error.message}`,
840
- error.response ? error.response.status : 500,
841
- error.response ? error.response.data : null
925
+ `Erreur lors de l'appel de l'API : ${getErrorMessage(error)}`,
926
+ statusCode,
927
+ responseData
842
928
  );
843
929
  }
844
930
 
@@ -848,17 +934,18 @@ export default class ApiClient extends EventEmitter {
848
934
  /**
849
935
  * Converts AJV (Another JSON Schema Validator) errors into human-readable messages.
850
936
  *
851
- * @param {Array} errors - An array of AJV validation error objects.
852
- * @returns {Array<string>} An array of human-readable error messages extracted from the AJV errors.
937
+ * @param errors - An array of AJV validation error objects.
938
+ * @returns An array of human-readable error messages extracted from the AJV errors.
853
939
  */
854
- _ajvErrorHuman(errors){
940
+ private _ajvErrorHuman(errors: any[]){
855
941
  try {
856
942
  const errorsMessages = new AggregateAjvError(errors);
857
- const messages = errorsMessages.errors.map(({ message }) => message);
943
+ // @ts-expect-error - champ non typé public dans les defs
944
+ const messages = errorsMessages.errors.map(({ message }: any) => message);
858
945
  return messages;
859
946
  } catch (e) {
860
947
  this._logger.error("[ApiClient] _ajvErrorHuman", e);
861
- return errors.map(({ message }) => message);
948
+ return errors.map(({ message }: any) => message);
862
949
  }
863
950
  }
864
951
 
@@ -874,13 +961,15 @@ export default class ApiClient extends EventEmitter {
874
961
  *
875
962
  * If no errors are found, the original `response` is returned.
876
963
  *
877
- * @param {Object} response - The API response object to check.
878
- * @param {Object} response.data - The data payload of the response.
879
- * @param {number} response.status - The HTTP status code of the response.
964
+ * @param {import("axios").AxiosResponse | { data: any } } response
965
+ * @returns {import("axios").AxiosResponse | { data: any }}
880
966
  * @throws {ApiResponseError} If an error is detected in the response data.
881
- * @returns {Object} The original `response` object if no errors are found.
882
967
  */
883
- checkAndThrowApiResponseError(response) {
968
+ checkAndThrowApiResponseError(response: any) {
969
+ if (!("status" in response)) {
970
+ return response;
971
+ }
972
+
884
973
  const data = response.data;
885
974
  if (!data || typeof data !== "object") {
886
975
  return response;
@@ -916,7 +1005,7 @@ export default class ApiClient extends EventEmitter {
916
1005
  * - userId
917
1006
  * - En-têtes Axios
918
1007
  */
919
- resetSession() {
1008
+ resetSession(): void {
920
1009
  this.setToken(null);
921
1010
  this.setRefreshToken(null);
922
1011
  this._setUserId(null);
@@ -931,12 +1020,8 @@ export default class ApiClient extends EventEmitter {
931
1020
 
932
1021
  /**
933
1022
  * Retrieves the value from an object based on a dot-separated path.
934
- *
935
- * @param {Object} obj - The object from which to retrieve the value.
936
- * @param {string} path - The dot-separated path string indicating the value to retrieve.
937
- * @returns {*} - The value found at the specified path, or undefined if the path is invalid.
938
1023
  */
939
- _getValueByPath(obj, path) {
1024
+ private _getValueByPath(obj: any, path: string): any {
940
1025
  if (!path) return undefined;
941
1026
  const keys = path.split(".");
942
1027
  return keys.reduce((acc, key) => acc && acc[key], obj);
@@ -944,54 +1029,139 @@ export default class ApiClient extends EventEmitter {
944
1029
 
945
1030
  // Conversions
946
1031
 
1032
+ /**
1033
+ * Normalizes Date objects to ISO strings for AJV validation.
1034
+ * This preserves the object structure while converting only Dates.
1035
+ * Handles reactive Proxies by unwrapping them first.
1036
+ *
1037
+ * @param obj - The object to normalize
1038
+ * @returns The normalized object with Dates as ISO strings
1039
+ */
1040
+ static normalizeDatesForValidation(obj: any): any {
1041
+ if (obj === null || obj === undefined) return obj;
1042
+
1043
+ // Unwrap reactive Proxies BEFORE checking for Date
1044
+ if (obj && typeof obj === "object" && obj.__isReactive && obj.__raw) {
1045
+ return ApiClient.normalizeDatesForValidation(obj.__raw);
1046
+ }
1047
+
1048
+ if (obj instanceof Date) {
1049
+ return obj.toISOString();
1050
+ }
1051
+
1052
+ if (Array.isArray(obj)) {
1053
+ return obj.map((item: any) => ApiClient.normalizeDatesForValidation(item));
1054
+ }
1055
+
1056
+ if (typeof obj === "object") {
1057
+ const normalized: any = {};
1058
+ for (const key of Object.keys(obj)) {
1059
+ normalized[key] = ApiClient.normalizeDatesForValidation(obj[key]);
1060
+ }
1061
+ return normalized;
1062
+ }
1063
+
1064
+ return obj;
1065
+ }
1066
+
1067
+ /**
1068
+ * Normalizes fields with format:"date" in schema to YYYY-MM-DD format.
1069
+ * Extracts only the date part from ISO datetime strings.
1070
+ *
1071
+ * @param data - The data object to normalize
1072
+ * @param schema - The JSON schema containing field definitions
1073
+ * @returns The normalized data with date-only strings for format:"date" fields
1074
+ */
1075
+ static normalizeSchemaDateFields(data: any, schema: any) {
1076
+ if (!data || !schema || typeof data !== "object") return data;
1077
+
1078
+ const normalized = { ...data };
1079
+
1080
+ // Récupérer les propriétés du schéma (gérer allOf, then, properties, etc.)
1081
+ let properties = schema.properties || {};
1082
+
1083
+ // Si le schéma utilise allOf, parcourir tous les éléments
1084
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1085
+ for (const allOfItem of schema.allOf) {
1086
+ // Chercher dans properties directes
1087
+ if (allOfItem.properties) {
1088
+ properties = { ...properties, ...allOfItem.properties };
1089
+ }
1090
+ // Chercher dans if/then/properties
1091
+ if (allOfItem.then && allOfItem.then.properties) {
1092
+ properties = { ...properties, ...allOfItem.then.properties };
1093
+ }
1094
+ }
1095
+ }
1096
+
1097
+ // Aussi vérifier schema.then.properties si présent
1098
+ if (schema.then && schema.then.properties) {
1099
+ properties = { ...properties, ...schema.then.properties };
1100
+ }
1101
+
1102
+ // Pour chaque propriété ayant format:"date"
1103
+ for (const [key, propDef] of Object.entries(properties)) {
1104
+ const typedPropDef = propDef as any;
1105
+ if (typedPropDef?.format === "date" && normalized[key]) {
1106
+ const value = normalized[key];
1107
+
1108
+ // Si c'est une string ISO datetime, extraire YYYY-MM-DD
1109
+ if (typeof value === "string" && value.includes("T")) {
1110
+ normalized[key] = value.split("T")[0];
1111
+ }
1112
+ }
1113
+ }
1114
+
1115
+ return normalized;
1116
+ }
1117
+
947
1118
  /**
948
1119
  * Converts an object to URL search parameters.
949
1120
  *
950
- * @param {Object} obj - The object to be converted to URL search parameters.
951
- * @param {Object} [options={}] - Optional settings for the conversion.
952
- * @returns {URLSearchParams} The URL search parameters generated from the object.
1121
+ * @param obj - The object to be converted to URL search parameters.
1122
+ * @param [options={}] - Optional settings for the conversion.
1123
+ * @returns The URL search parameters generated from the object.
953
1124
  */
954
- static toURLSearchParams(obj, options = {}) {
955
- return this._buildParams(obj, new URLSearchParams(), options);
1125
+ static toURLSearchParams(obj: any, options: any = {}) {
1126
+ return this._buildParams(obj, new URLSearchParams(), options) as URLSearchParams;
956
1127
  }
957
1128
 
958
1129
  /**
959
1130
  * Builds parameters for an API request from a given object.
960
- *
961
- * @param {Object} obj - The object to be converted into parameters.
962
- * @param {FormData|URLSearchParams} paramsInstance - The instance to which the parameters will be appended.
963
- * @param {Object} [options={}] - Optional settings.
964
- * @param {boolean} [options.dots=false] - Whether to use dots in the parameter keys.
965
- * @param {boolean} [options.indexes=false] - Whether to include array indexes in the parameter keys.
966
- * @param {boolean} [options.metaTokens=true] - Whether to include meta tokens in the parameter keys.
967
- * @throws {TypeError} If the provided obj is not an object or is null.
968
- * @returns {FormData|URLSearchParams} The instance with the appended parameters.
969
1131
  */
970
- static _buildParams(obj, paramsInstance, options = {}) {
1132
+ static _buildParams(
1133
+ obj: any,
1134
+ paramsInstance: FormData | URLSearchParams,
1135
+ options: {
1136
+ dots?: boolean;
1137
+ indexes?: boolean;
1138
+ metaTokens?: boolean;
1139
+ } = {}
1140
+ ): FormData | URLSearchParams {
971
1141
  if (typeof obj !== "object" || obj === null) {
972
1142
  throw new TypeError("La donnée doit être un objet non nul.");
973
1143
  }
974
1144
 
975
1145
  const { dots = false, indexes = false, metaTokens = true } = options;
976
- const stack = [];
1146
+ const stack: any[] = [];
977
1147
 
978
- function isVisitable(thing) {
1148
+ function isVisitable(thing: any) {
979
1149
  return Object.prototype.toString.call(thing) === "[object Object]" || Array.isArray(thing);
980
1150
  }
981
1151
 
982
- function removeBrackets(key) {
1152
+ function removeBrackets(key: any) {
983
1153
  return key.endsWith("[]") ? key.slice(0, -2) : key;
984
1154
  }
985
1155
 
986
- function renderKey(path, key, useDots = false) {
1156
+ function renderKey(path: any, key: any, useDots = false) {
987
1157
  if (!path) return key;
988
- return path.concat(key).map((token, i) => {
1158
+ return path.concat(key).map((token: any, i: any) => {
989
1159
  token = removeBrackets(token);
990
1160
  return !useDots && i ? `[${token}]` : token;
991
1161
  }).join(useDots ? "." : "");
992
1162
  }
993
1163
 
994
- function convertValue(value) {
1164
+ function convertValue(value: any) {
995
1165
  if (value === null || value === undefined) return "";
996
1166
  if (value instanceof Date) return value.toISOString();
997
1167
  if (
@@ -1005,7 +1175,7 @@ export default class ApiClient extends EventEmitter {
1005
1175
  return value;
1006
1176
  }
1007
1177
 
1008
- function defaultVisitor(value, key, path) {
1178
+ function defaultVisitor(value: any, key: any, path: any) {
1009
1179
  if (value && typeof value === "object") {
1010
1180
  if (key.endsWith("{}")) {
1011
1181
  key = metaTokens ? key : key.slice(0, -2);
@@ -1015,7 +1185,7 @@ export default class ApiClient extends EventEmitter {
1015
1185
 
1016
1186
  if ((Array.isArray(value) && !value.some(isVisitable)) || key.endsWith("[]")) {
1017
1187
  key = removeBrackets(key);
1018
- value.forEach((el, index) => {
1188
+ value.forEach((el: any, index: any) => {
1019
1189
  if (el !== undefined && el !== null) {
1020
1190
  // const fieldKey = indexes ? renderKey([key], index, dots) : `${key}[]`;
1021
1191
  const fieldKey = indexes
@@ -1033,9 +1203,9 @@ export default class ApiClient extends EventEmitter {
1033
1203
  return false;
1034
1204
  }
1035
1205
 
1036
- function build(value, path = []) {
1037
- if (value === undefined || value === null || stack.includes(value)) return;
1038
- stack.push(value);
1206
+ function build(value: any, path: any = []) {
1207
+ if (value === undefined || value === null || (stack as any).includes(value)) return;
1208
+ (stack as any).push(value);
1039
1209
 
1040
1210
  Object.entries(value).forEach(([k, el]) => {
1041
1211
  if (el !== undefined && defaultVisitor(el, k.trim(), path)) {
@@ -1059,12 +1229,12 @@ export default class ApiClient extends EventEmitter {
1059
1229
  * nested structures like `resultGoods`, `resultErrors`, `results`, `news`,
1060
1230
  * `notif`, `citoyens`, `organizations`, `cities`, `newComment`, `map`, and `object`.
1061
1231
  *
1062
- * @param {Object|Array} data - The data to be transformed.
1063
- * @returns {Object|Array} - The transformed data.
1232
+ * @param data - The data to be transformed.
1233
+ * @returns - The transformed data.
1064
1234
  *
1065
1235
  * @private
1066
1236
  */
1067
- _transformData(data) {
1237
+ private _transformData(data: any): any {
1068
1238
  if (data && typeof data === "object") {
1069
1239
  if (data.resultGoods?.msg) {
1070
1240
  data = this._normalizeJsonData({ msg: data.resultGoods.msg, ...data });
@@ -1075,7 +1245,7 @@ export default class ApiClient extends EventEmitter {
1075
1245
  return this._normalizeJsonData({ id: key, ...data.results[key] });
1076
1246
  });
1077
1247
  } else if (Array.isArray(data.results) && data.results.length > 0) {
1078
- data.results = data.results.map((item) => this._normalizeJsonData(item));
1248
+ data.results = data.results.map((item: any) => this._normalizeJsonData(item));
1079
1249
  } else if (data.news && Array.isArray(data.news) && data.news.length === 0) {
1080
1250
  data = data.news;
1081
1251
  } else if (data.news && typeof data.news === "object" && !Array.isArray(data.news)) {
@@ -1130,8 +1300,8 @@ export default class ApiClient extends EventEmitter {
1130
1300
  /**
1131
1301
  * Normalizes JSON data by transforming specific fields and ensuring URLs are complete.
1132
1302
  *
1133
- * @param {Object} item - The JSON object to be normalized.
1134
- * @returns {Object} - The normalized JSON object.
1303
+ * @param item - The JSON object to be normalized.
1304
+ * @returns - The normalized JSON object.
1135
1305
  *
1136
1306
  * The function performs the following transformations:
1137
1307
  * - Normalizes the ID field if it matches a specific pattern.
@@ -1141,7 +1311,7 @@ export default class ApiClient extends EventEmitter {
1141
1311
  * - Removes the `timeAgo` field if it exists.
1142
1312
  * - Converts the object to EJSON format if necessary.
1143
1313
  */
1144
- _normalizeJsonData(item) {
1314
+ private _normalizeJsonData(item: any) {
1145
1315
  if (!item || typeof item !== "object") {
1146
1316
  return item;
1147
1317
  }
@@ -1179,7 +1349,7 @@ export default class ApiClient extends EventEmitter {
1179
1349
 
1180
1350
  if (item?.openingHours && Array.isArray(item.openingHours) && item.openingHours.length > 0) {
1181
1351
  item.openingHours = item.openingHours.filter(
1182
- (day) => day.dayOfWeek && day.hours && day.hours[0] && day.hours[0].opens
1352
+ (day: any) => day.dayOfWeek && day.hours && day.hours[0] && day.hours[0].opens
1183
1353
  );
1184
1354
  }
1185
1355
 
@@ -1208,6 +1378,10 @@ export default class ApiClient extends EventEmitter {
1208
1378
  if (item?.timeAgo) {
1209
1379
  delete item.timeAgo;
1210
1380
  }
1381
+
1382
+ if(item?.author?.typeSig === "people"){
1383
+ item.author.type = "citoyens";
1384
+ }
1211
1385
 
1212
1386
  // Convertit en EJSON si besoin
1213
1387
  return this._fromJSONValue ? EJSON.fromJSONValue(item) : item;
@@ -1219,10 +1393,10 @@ export default class ApiClient extends EventEmitter {
1219
1393
  * (i.e., it starts with "http://" or "https://"), it is returned as is. Otherwise, the image path
1220
1394
  * is concatenated with the base URL of the ApiClient instance.
1221
1395
  *
1222
- * @param {string} imagePath - The image path to ensure as a full URL.
1223
- * @returns {string} - The full URL of the image path.
1396
+ * @param imagePath - The image path to ensure as a full URL.
1397
+ * @returns - The full URL of the image path.
1224
1398
  */
1225
- _ensureFullURL(imagePath) {
1399
+ private _ensureFullURL(imagePath: any) {
1226
1400
  if (!imagePath) return imagePath;
1227
1401
  imagePath = imagePath.trim();
1228
1402
  if (!imagePath || /^https?:\/\//i.test(imagePath)) {
@@ -1246,8 +1420,8 @@ export default class ApiClient extends EventEmitter {
1246
1420
  * Pour chaque clé présente dans dateFields, si la valeur est un nombre ou un objet
1247
1421
  * contenant une propriété sec (nombre), la transforme en { $date: valeurEnMs }.
1248
1422
  *
1249
- * @param {Object} obj - L’objet à normaliser.
1250
- * @returns {Object} L’objet normalisé.
1423
+ * @param obj - L’objet à normaliser.
1424
+ * @returns L’objet normalisé.
1251
1425
  */
1252
1426
  // _normalizeDatesRecursively(obj) {
1253
1427
  // if (obj === null || typeof obj !== "object") return obj;
@@ -1278,8 +1452,8 @@ export default class ApiClient extends EventEmitter {
1278
1452
  * vérifie et convertit le chemin en URL complète à l’aide de _ensureFullURL.
1279
1453
  * Les cas particuliers (comme mediaImg.images ou mediaFile.files) sont également gérés.
1280
1454
  *
1281
- * @param {Object} obj - L’objet à normaliser.
1282
- * @returns {Object} L’objet normalisé.
1455
+ * @param obj - L’objet à normaliser.
1456
+ * @returns L’objet normalisé.
1283
1457
  */
1284
1458
  // _normalizeImagesRecursively(obj) {
1285
1459
  // if (obj === null || typeof obj !== "object") return obj;
@@ -1311,8 +1485,8 @@ export default class ApiClient extends EventEmitter {
1311
1485
  * Si un objet possède une propriété "_id" ou "id" contenant un sous-objet "$id" valide,
1312
1486
  * l'ID est normalisé et la propriété _id est convertie au format EJSON attendu.
1313
1487
  *
1314
- * @param {Object|Array} obj - L'objet ou le tableau à normaliser.
1315
- * @returns {Object|Array} L'objet ou le tableau normalisé.
1488
+ * @param obj - L'objet ou le tableau à normaliser.
1489
+ * @returns L'objet ou le tableau normalisé.
1316
1490
  */
1317
1491
  // _normalizeIdRecursively(obj) {
1318
1492
  // if (obj === null || typeof obj !== "object") return obj;
@@ -1346,8 +1520,8 @@ export default class ApiClient extends EventEmitter {
1346
1520
  * Normalise récursivement les valeurs booléennes.
1347
1521
  * Si une valeur est une chaîne "true" ou "false", elle est convertie en booléen.
1348
1522
  *
1349
- * @param {any} data - L'objet, le tableau ou la valeur à normaliser.
1350
- * @returns {any} La donnée normalisée.
1523
+ * @param data - L'objet, le tableau ou la valeur à normaliser.
1524
+ * @returns La donnée normalisée.
1351
1525
  */
1352
1526
  // _normalizeBooleansRecursively(data) {
1353
1527
  // if (typeof data === "string") {
@@ -1369,10 +1543,10 @@ export default class ApiClient extends EventEmitter {
1369
1543
  /**
1370
1544
  * Transforme une chaîne "true" ou "false" en booléen.
1371
1545
  *
1372
- * @param {string} str - La chaîne à normaliser.
1373
- * @returns {boolean|string} La valeur booléenne ou la chaîne originale.
1546
+ * @param str - La chaîne à normaliser.
1547
+ * @returns La valeur booléenne ou la chaîne originale.
1374
1548
  */
1375
- _normalizeString(str) {
1549
+ private _normalizeString(str: any) {
1376
1550
  if (str === "true") return true;
1377
1551
  if (str === "false") return false;
1378
1552
  return str;
@@ -1383,10 +1557,10 @@ export default class ApiClient extends EventEmitter {
1383
1557
  * Si l'objet possède une propriété "_id" ou "id" contenant un sous-objet "$id" valide,
1384
1558
  * il met à jour l'objet en assignant la valeur de l'ID et en formattant _id en EJSON.
1385
1559
  *
1386
- * @param {Object} obj - L'objet à normaliser.
1387
- * @returns {Object} L'objet avec l'ID normalisé.
1560
+ * @param obj - L'objet à normaliser.
1561
+ * @returns L'objet avec l'ID normalisé.
1388
1562
  */
1389
- _normalizeId(obj) {
1563
+ private _normalizeId(obj: any) {
1390
1564
  if (obj._id && obj._id.$id && /^[0-9a-fA-F]{24}$/.test(obj._id.$id)) {
1391
1565
  obj.id = obj._id.$id;
1392
1566
  obj._id = { $type: "oid", $value: obj._id.$id };
@@ -1403,10 +1577,10 @@ export default class ApiClient extends EventEmitter {
1403
1577
  * Si la valeur est un objet contenant "sec" (nombre) ou un nombre,
1404
1578
  * elle est convertie en { $date: valeurEnMs }.
1405
1579
  *
1406
- * @param {any} value - La valeur à normaliser.
1407
- * @returns {any} La valeur normalisée.
1580
+ * @param value - La valeur à normaliser.
1581
+ * @returns La valeur normalisée.
1408
1582
  */
1409
- _normalizeDate(value) {
1583
+ private _normalizeDate(value: any) {
1410
1584
  if (value && typeof value === "object" && typeof value.sec === "number") {
1411
1585
  return { $date: value.sec * 1000 };
1412
1586
  } else if (typeof value === "number") {
@@ -1419,10 +1593,10 @@ export default class ApiClient extends EventEmitter {
1419
1593
  * Normalise une URL d'image.
1420
1594
  * Si la valeur est une chaîne non vide, retourne l'URL complète via _ensureFullURL.
1421
1595
  *
1422
- * @param {any} value - La valeur à normaliser.
1423
- * @returns {any} La valeur normalisée.
1596
+ * @param value - La valeur à normaliser.
1597
+ * @returns La valeur normalisée.
1424
1598
  */
1425
- _normalizeImage(value) {
1599
+ private _normalizeImage(value: any) {
1426
1600
  if (typeof value === "string" && value.trim() !== "") {
1427
1601
  return this._ensureFullURL(value);
1428
1602
  }
@@ -1432,7 +1606,7 @@ export default class ApiClient extends EventEmitter {
1432
1606
  /**
1433
1607
  * Liste des champs d'image à normaliser.
1434
1608
  */
1435
- _imageFields = [
1609
+ private _imageFields = [
1436
1610
  "profilImageUrl",
1437
1611
  "profilThumbImageUrl",
1438
1612
  "profilMediumImageUrl",
@@ -1449,7 +1623,7 @@ export default class ApiClient extends EventEmitter {
1449
1623
  /**
1450
1624
  * Liste des champs de date à normaliser.
1451
1625
  */
1452
- _dateFields = ["modified", "created", "updated", "birthDate", "lastLoginDate", "startDate", "endDate", "date", "issuedOn"];
1626
+ private _dateFields = ["modified", "created", "updated", "birthDate", "lastLoginDate", "startDate", "endDate", "date", "issuedOn"];
1453
1627
 
1454
1628
  /**
1455
1629
  * Normalise récursivement un objet, un tableau ou une valeur simple.
@@ -1460,10 +1634,10 @@ export default class ApiClient extends EventEmitter {
1460
1634
  * - Transformation récursive des objets et tableaux.
1461
1635
  * - Normalisation des identifiants.
1462
1636
  *
1463
- * @param {any} data - La donnée à normaliser.
1464
- * @returns {any} La donnée normalisée.
1637
+ * @param data - La donnée à normaliser.
1638
+ * @returns La donnée normalisée.
1465
1639
  */
1466
- _normalizeRecursively(data) {
1640
+ private _normalizeRecursively(data: any): any {
1467
1641
  // Cas de base pour les chaînes
1468
1642
  if (typeof data === "string") {
1469
1643
  return this._normalizeString(data);
@@ -1471,13 +1645,13 @@ export default class ApiClient extends EventEmitter {
1471
1645
 
1472
1646
  // Traitement des tableaux
1473
1647
  if (Array.isArray(data)) {
1474
- return data.map(item => this._normalizeRecursively(item));
1648
+ return data.map((item: any) => this._normalizeRecursively(item));
1475
1649
  }
1476
1650
 
1477
1651
  // Traitement des objets non nuls
1478
1652
  if (data !== null && typeof data === "object") {
1479
1653
  // On crée une copie de l'objet pour éviter les effets de bord.
1480
- const normalizedData = {};
1654
+ const normalizedData: any = {};
1481
1655
 
1482
1656
  Object.keys(data).forEach(key => {
1483
1657
  // Appliquer récursivement la normalisation sur la valeur.
@@ -1509,11 +1683,12 @@ export default class ApiClient extends EventEmitter {
1509
1683
  return data;
1510
1684
  }
1511
1685
 
1512
- _startBeforeEndValidate = (schema, data) => {
1686
+ private _startBeforeEndValidate = (schema: any, data: any) => {
1513
1687
  if (!data.startDate || !data.endDate) return true;
1514
1688
 
1515
1689
  const isValid = new Date(data.startDate) < new Date(data.endDate);
1516
1690
  if (!isValid) {
1691
+ // @ts-expect-error - on sait que ValidateFunction expose .errors au runtime
1517
1692
  this._startBeforeEndValidate.errors = [
1518
1693
  {
1519
1694
  instancePath: "/startDate", // ou "." si tu veux le chemin actuel
@@ -1528,31 +1703,41 @@ export default class ApiClient extends EventEmitter {
1528
1703
  };
1529
1704
 
1530
1705
 
1531
- getRequestSchema(constant) {
1706
+ /**
1707
+ * Récupère le schéma de requête pour un endpoint donné.
1708
+ * @param constant - Le nom de l'endpoint.
1709
+ * @returns Le schéma de requête ou null si non trouvé.
1710
+ */
1711
+ getRequestSchema(constant: string) {
1532
1712
  const endpoint = this._endpoints.find(e => e.constant === constant);
1533
1713
  return endpoint?.request || null;
1534
1714
  }
1535
1715
 
1536
- getPathSchema(constant) {
1716
+ /**
1717
+ * Récupère le schéma de chemin pour un endpoint donné.
1718
+ * @param constant - Le nom de l'endpoint.
1719
+ * @returns Le schéma de chemin ou null si non trouvé.
1720
+ */
1721
+ getPathSchema(constant: string) {
1537
1722
  return this._endpoints.find(e => e.constant === constant)?.pathParams || null;
1538
1723
  }
1539
1724
 
1540
1725
  /**
1541
1726
  * Permet d'écouter facilement un ensemble d'événements importants émis par l'ApiClient.
1542
1727
  *
1543
- * @param {Object} handlers - Un objet avec des fonctions à appeler selon l'événement.
1544
- * @param {Function} [handlers.retryAttempt] - Lors d'une tentative de retry axios.
1545
- * @param {Function} [handlers.queuedOffline] - Lorsqu'une requête est mise en file.
1546
- * @param {Function} [handlers.circuitBreakerOpen] - Quand le breaker s'ouvre.
1547
- * @param {Function} [handlers.circuitBreakerReset] - Quand le breaker se referme.
1548
- * @param {Function} [handlers.refreshSuccess] - Quand le token est rafraîchi.
1549
- * @param {Function} [handlers.refreshFailed] - Quand le refresh échoue.
1550
- * @param {Function} [handlers.sessionReset] - Quand la session est réinitialisée.
1551
- * @param {Function} [handlers.validationError] - Quand une validation échoue.
1552
- * @param {Function} [handlers.offlineModeChanged] - Quand le mode offline change.
1553
- * @param {Function} [handlers.userLoggedIn] - Quand un utilisateur se connecte.
1728
+ * @param handlers - Un objet avec des fonctions à appeler selon l'événement.
1729
+ * @param [handlers.retryAttempt] - Lors d'une tentative de retry axios.
1730
+ * @param [handlers.queuedOffline] - Lorsqu'une requête est mise en file.
1731
+ * @param [handlers.circuitBreakerOpen] - Quand le breaker s'ouvre.
1732
+ * @param [handlers.circuitBreakerReset] - Quand le breaker se referme.
1733
+ * @param [handlers.refreshSuccess] - Quand le token est rafraîchi.
1734
+ * @param [handlers.refreshFailed] - Quand le refresh échoue.
1735
+ * @param [handlers.sessionReset] - Quand la session est réinitialisée.
1736
+ * @param [handlers.validationError] - Quand une validation échoue.
1737
+ * @param [handlers.offlineModeChanged] - Quand le mode offline change.
1738
+ * @param [handlers.userLoggedIn] - Quand un utilisateur se connecte.
1554
1739
  */
1555
- onEvent(handlers = {}) {
1740
+ onEvent(handlers: Record<string, (...args: any[]) => void> = {}) {
1556
1741
  const availableEvents = [
1557
1742
  "retryAttempt",
1558
1743
  "queuedOffline",
@@ -1568,7 +1753,7 @@ export default class ApiClient extends EventEmitter {
1568
1753
 
1569
1754
  for (const eventName of availableEvents) {
1570
1755
  if (typeof handlers[eventName] === "function") {
1571
- this.on(eventName, handlers[eventName]);
1756
+ this.on(eventName, handlers[eventName] as (...args: any[]) => void);
1572
1757
  }
1573
1758
  }
1574
1759
  }
@@ -1577,7 +1762,7 @@ export default class ApiClient extends EventEmitter {
1577
1762
  * Retourne la liste des noms d'événements personnalisés déclarés dans les endpoints.
1578
1763
  * Utile pour introspection ou documentation.
1579
1764
  *
1580
- * @returns {string[]} Liste des événements émis dynamiquement via postActions.
1765
+ * @returns Liste des événements émis dynamiquement via postActions.
1581
1766
  */
1582
1767
  getDeclaredEvents() {
1583
1768
  const events = new Set();
@@ -1594,46 +1779,65 @@ export default class ApiClient extends EventEmitter {
1594
1779
  }
1595
1780
 
1596
1781
  }
1782
+
1597
1783
  /**
1598
1784
  * @event ApiClient#retryAttempt
1599
1785
  * @type {Object}
1600
1786
  * @property {number} retryCount - Le numéro de tentative.
1601
1787
  * @property {string} url - L'URL de la requête ayant échoué.
1788
+ */
1602
1789
 
1790
+ /**
1603
1791
  * @event ApiClient#queuedOffline
1604
1792
  * @type {Object}
1605
1793
  * @property {string} constant - Le nom de l'endpoint.
1606
1794
  * @property {Object} data - Les données envoyées.
1607
1795
  * @property {"offlineMode"|"circuitBreaker"} reason - La raison de la mise en file.
1796
+ */
1608
1797
 
1798
+ /**
1609
1799
  * @event ApiClient#circuitBreakerOpen
1610
1800
  * @type {Object}
1611
1801
  * @property {number} timestamp - Date (ms) à laquelle le breaker s'est ouvert.
1802
+ */
1612
1803
 
1804
+ /**
1613
1805
  * @event ApiClient#circuitBreakerReset
1614
1806
  * @type {void}
1807
+ */
1615
1808
 
1809
+ /**
1616
1810
  * @event ApiClient#refreshSuccess
1617
1811
  * @type {Object}
1618
1812
  * @property {string} token - Le nouveau token d'accès.
1619
1813
  * @property {string} [refreshToken] - Le nouveau refreshToken (optionnel).
1814
+ */
1620
1815
 
1816
+ /**
1621
1817
  * @event ApiClient#refreshFailed
1622
1818
  * @type {Object}
1623
1819
  * @property {string} error - Le message d’erreur de refresh.
1820
+ */
1624
1821
 
1822
+ /**
1625
1823
  * @event ApiClient#sessionReset
1626
1824
  * @type {void}
1825
+ */
1627
1826
 
1827
+ /**
1628
1828
  * @event ApiClient#validationError
1629
1829
  * @type {Object}
1630
1830
  * @property {"pathParams"|"request"|"response"} stage - Étape de validation échouée.
1631
1831
  * @property {Array<Object>} errors - Erreurs AJV brutes.
1832
+ */
1632
1833
 
1834
+ /**
1633
1835
  * @event ApiClient#offlineModeChanged
1634
1836
  * @type {boolean} - true si le client est offline, false sinon.
1635
- *
1837
+ */
1838
+
1839
+ /**
1636
1840
  * @event ApiClient#userLoggedIn
1637
- * @param {Object} user - Les données utilisateur extraites de la réponse.
1841
+ * @param user - Les données utilisateur extraites de la réponse.
1638
1842
  */
1639
1843