@communecter/cocolight-api-client 1.0.54 → 1.0.56

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 +29 -17
  19. package/src/{Api.js → Api.ts} +85 -95
  20. package/src/{ApiClient.js → ApiClient.ts} +436 -247
  21. package/src/EJSONType.ts +103 -0
  22. package/src/api/{Badge.js → Badge.ts} +56 -45
  23. package/src/api/BaseEntity.ts +3890 -0
  24. package/src/api/Comment.ts +200 -0
  25. package/src/api/{EndpointApi.js → EndpointApi.ts} +363 -297
  26. package/src/api/EndpointApi.types.ts +4609 -0
  27. package/src/api/EntityRegistry.ts +203 -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} +16 -39
  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 +38 -82
  66. package/types/Api.d.ts.map +1 -0
  67. package/types/ApiClient.d.ts +244 -184
  68. package/types/ApiClient.d.ts.map +1 -0
  69. package/types/EJSONType.d.ts +48 -22
  70. package/types/EJSONType.d.ts.map +1 -0
  71. package/types/api/Badge.d.ts +20 -20
  72. package/types/api/Badge.d.ts.map +1 -0
  73. package/types/api/BaseEntity.d.ts +751 -446
  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 +347 -295
  77. package/types/api/EndpointApi.d.ts.map +1 -0
  78. package/types/api/EndpointApi.types.d.ts +3914 -4133
  79. package/types/api/EntityRegistry.d.ts +18 -16
  80. package/types/api/EntityRegistry.d.ts.map +1 -0
  81. package/types/api/Event.d.ts +119 -35
  82. package/types/api/Event.d.ts.map +1 -0
  83. package/types/api/News.d.ts +52 -20
  84. package/types/api/News.d.ts.map +1 -0
  85. package/types/api/Organization.d.ts +165 -49
  86. package/types/api/Organization.d.ts.map +1 -0
  87. package/types/api/Poi.d.ts +51 -22
  88. package/types/api/Poi.d.ts.map +1 -0
  89. package/types/api/Project.d.ts +151 -52
  90. package/types/api/Project.d.ts.map +1 -0
  91. package/types/api/User.d.ts +222 -93
  92. package/types/api/User.d.ts.map +1 -0
  93. package/types/api/UserApi.d.ts +60 -9
  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 +6922 -1215
  107. package/types/endpoints.module.d.ts.map +1 -0
  108. package/types/error.d.ts +25 -51
  109. package/types/error.d.ts.map +1 -0
  110. package/types/index.d.ts +55 -48
  111. package/types/index.d.ts.map +1 -0
  112. package/types/mixin/UserMixin.d.ts +1 -1
  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 +10 -9
  126. package/types/utils/FileOfflineStorageStrategy.node.d.ts.map +1 -0
  127. package/types/utils/FileStorageStrategy.node.d.ts +9 -20
  128. package/types/utils/FileStorageStrategy.node.d.ts.map +1 -0
  129. package/types/utils/MultiServerFileStorageStrategy.node.d.ts +13 -18
  130. package/types/utils/MultiServerFileStorageStrategy.node.d.ts.map +1 -0
  131. package/types/utils/MultiServerTokenStorageStrategy.d.ts +30 -51
  132. package/types/utils/MultiServerTokenStorageStrategy.d.ts.map +1 -0
  133. package/types/utils/OfflineClientManager.d.ts +52 -88
  134. package/types/utils/OfflineClientManager.d.ts.map +1 -0
  135. package/types/utils/OfflineQueueStorageStrategy.d.ts +12 -9
  136. package/types/utils/OfflineQueueStorageStrategy.d.ts.map +1 -0
  137. package/types/utils/TokenStorage.d.ts +20 -70
  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 -11
  142. package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts.map +1 -0
  143. package/types/utils/createDefaultOfflineStrategy.d.ts +2 -3
  144. package/types/utils/createDefaultOfflineStrategy.d.ts.map +1 -0
  145. package/types/utils/createDefaultTokenStorageStrategy.d.ts +2 -12
  146. package/types/utils/createDefaultTokenStorageStrategy.d.ts.map +1 -0
  147. package/types/utils/reactive.d.ts +10 -16
  148. package/types/utils/reactive.d.ts.map +1 -0
  149. package/types/utils/stream-utils.node.d.ts +3 -2
  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 -121
  171. package/src/index.js +0 -97
  172. package/src/mixin/UserMixin.js +0 -8
  173. package/src/utils/MultiServerFileStorageStrategy.node.js +0 -87
  174. package/src/utils/MultiServerTokenStorageStrategy.js +0 -188
  175. package/src/utils/OfflineQueueStorageStrategy.js +0 -51
  176. package/src/utils/TokenStorage.js +0 -153
  177. package/src/utils/createDefaultMultiServerTokenStorageStrategy.js +0 -51
  178. package/src/utils/createDefaultTokenStorageStrategy.js +0 -49
  179. package/src/utils/stream-utils.node.js +0 -10
@@ -1,50 +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;
17
31
 
18
- /** @typedef {import("./utils/TokenStorage.js").TokenStorageStrategy} TokenStorageStrategy */
19
- /** @typedef {import("./utils/TokenStorage.js").MemoryStorageStrategy} MemoryStorageStrategy */
20
- /** @typedef {import("./utils/MultiServerTokenStorageStrategy.js").MultiServerTokenStorageStrategy} MultiServerTokenStorageStrategy */
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
+ }
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
+ }
67
+
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
+ }
21
76
 
22
77
  /**
23
- * @typedef ApiClientOptions
24
- * @property {string} baseURL
25
- * @property {string} [accessToken]
26
- * @property {string} [refreshToken]
27
- * @property {string} [refreshUrl="/api/cocolight/refreshtoken"]
28
- * @property {any[]} [endpoints]
29
- * @property {number} [timeout=30000]
30
- * @property {boolean} [debug=false]
31
- * @property {number} [maxRetries=0]
32
- * @property {number} [circuitBreakerThreshold=5]
33
- * @property {number} [circuitBreakerResetTime=60000]
34
- * @property {boolean} [fromJSONValue=true]
35
- * @property {TokenStorageStrategy|null} [tokenStorageStrategy=null]
78
+ * Union type pour tous les résultats possibles de callEndpoint
36
79
  */
80
+ export type CallEndpointResponse<T = any> = CallEndpointResult<T> | ApiClientOfflineEnqueueResult;
37
81
 
38
- EJSON.addType("oid", value => {
39
- return new MongoID.ObjectID(value);
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");
40
90
  });
41
91
 
92
+
42
93
  /**
43
94
  * Client générique pour consommer une API REST avec validation AJV, gestion des tokens,
44
95
  * circuit breaker, retry automatique, et support offline.
45
96
  *
46
- * @extends EventEmitter
47
- *
48
97
  * @fires ApiClient#retryAttempt
49
98
  * @fires ApiClient#queuedOffline
50
99
  * @fires ApiClient#circuitBreakerOpen
@@ -57,9 +106,36 @@ EJSON.addType("oid", value => {
57
106
  * @fires ApiClient#userLoggedIn
58
107
  */
59
108
  export default class ApiClient extends EventEmitter {
60
- /**
61
- * @param {ApiClientOptions} [options]
62
- */
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;
63
139
  constructor({
64
140
  baseURL,
65
141
  accessToken,
@@ -73,24 +149,23 @@ export default class ApiClient extends EventEmitter {
73
149
  circuitBreakerResetTime = 60000,
74
150
  fromJSONValue = true,
75
151
  tokenStorageStrategy = null
76
- } = {}) {
152
+ }: ApiClientOptions) {
77
153
  super(); // EventEmitter
78
154
 
79
155
  if (!baseURL) {
80
156
  throw new ApiClientError("Le paramètre \"baseURL\" est obligatoire.", 500);
81
157
  }
82
158
 
83
- this.__entityTag = "ApiClient";
84
-
85
159
  this._baseURL = baseURL;
86
160
  this._refreshUrl = refreshUrl;
87
- this._endpoints = endpoints;
161
+ this._endpoints = endpoints || endpointsJson.endpoints;
88
162
  this._debug = debug;
89
- let _userId = null;
90
- this._offlineClientManager = null;
91
-
92
- // Active la transformation des données en EJSON globalement
93
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;
94
169
 
95
170
  Object.defineProperty(this, "userId", {
96
171
  get: () => _userId,
@@ -100,9 +175,9 @@ export default class ApiClient extends EventEmitter {
100
175
  enumerable: true
101
176
  });
102
177
 
103
- this._setUserId = (id) => {
178
+ this._setUserId = (id: string | null) => {
104
179
  _userId = id;
105
- this._logger.debug(`[ApiClient] userId set: ${id}`);
180
+ this._logger?.debug(`[ApiClient] userId set: ${id}`);
106
181
  };
107
182
 
108
183
  // AJV
@@ -138,11 +213,11 @@ export default class ApiClient extends EventEmitter {
138
213
  axiosRetry(this._client, {
139
214
  retries: maxRetries,
140
215
  retryDelay: axiosRetry.exponentialDelay,
141
- retryCondition: (error) => {
216
+ retryCondition: (error: any) => {
142
217
  // Retry sur erreurs 5xx ou erreurs réseau
143
218
  return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error);
144
219
  },
145
- onRetry: (retryCount, error, requestConfig) => {
220
+ onRetry: (retryCount: number, error: any, requestConfig: any) => {
146
221
  this._logger.warn(`[Retry] Tentative #${retryCount} pour ${requestConfig?.url}`);
147
222
  this.emit("retryAttempt", { retryCount, url: requestConfig?.url });
148
223
  }
@@ -150,24 +225,10 @@ export default class ApiClient extends EventEmitter {
150
225
  this._logger.info(`[ApiClient] Retry activé : ${maxRetries} max`);
151
226
  }
152
227
 
153
- // Circuit breaker (simplifié)
154
- this._breakerThreshold = circuitBreakerThreshold;
155
- this._breakerResetTime = circuitBreakerResetTime;
156
- this._breakerErrorCount = 0;
157
- this._breakerOpen = false;
158
- /** @type {string|null} */
159
- this._lastBreakerOpenTime = null;
160
-
161
- /** @type {string|null} */
162
- this._accessToken = null;
163
- /** @type {string|null} */
164
- this._refreshToken = null;
165
-
166
228
  if (tokenStorageStrategy instanceof MultiServerTokenStorageStrategy) {
167
229
  tokenStorageStrategy.use(this._baseURL);
168
230
  }
169
-
170
- /** @type {TokenStorageStrategy} */
231
+
171
232
  this._tokenStorage = tokenStorageStrategy || new MemoryStorageStrategy();
172
233
 
173
234
  if (
@@ -181,7 +242,7 @@ export default class ApiClient extends EventEmitter {
181
242
  }
182
243
 
183
244
  // Applique un token initial s'il est fourni
184
- if(refreshToken){
245
+ if (refreshToken) {
185
246
  this.setRefreshToken(refreshToken);
186
247
  }
187
248
 
@@ -223,6 +284,7 @@ export default class ApiClient extends EventEmitter {
223
284
  this._logger.info("[ApiClient] Token rafraîchi avec succès.");
224
285
 
225
286
  // 🔑 Mise à jour EXPLICITE du header Authorization dans la requête originale
287
+ originalRequest.headers = originalRequest.headers ?? {};
226
288
  originalRequest.headers["Authorization"] = "Bearer " + this.getToken();
227
289
 
228
290
  this._logger.info("[ApiClient] Retente la requête originale avec le nouveau token.");
@@ -233,7 +295,11 @@ export default class ApiClient extends EventEmitter {
233
295
  }
234
296
  } catch (err) {
235
297
  this.resetSession();
236
- 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);
237
303
  }
238
304
  }
239
305
 
@@ -245,12 +311,10 @@ export default class ApiClient extends EventEmitter {
245
311
 
246
312
  /**
247
313
  * Sets the access token for the API client and updates the authorization header.
248
- *
249
- * @param {string|null} token - The access token to be set.
250
314
  */
251
- setToken(token) {
315
+ setToken(token: string | null): void {
252
316
  this._accessToken = token;
253
- this._tokenStorage.setAccessToken(token);
317
+ this._tokenStorage.setAccessToken(token ?? "");
254
318
  if (token) {
255
319
  this._client.defaults.headers.common["Authorization"] = "Bearer " + token;
256
320
  } else {
@@ -267,21 +331,17 @@ export default class ApiClient extends EventEmitter {
267
331
 
268
332
  /**
269
333
  * Retrieves the current access token.
270
- *
271
- * @returns {string|null} The access token.
272
334
  */
273
- getToken() {
335
+ getToken(): string | null {
274
336
  return this._accessToken;
275
337
  }
276
338
 
277
339
  /**
278
340
  * Sets the refresh token for the API client.
279
- *
280
- * @param {string|null} token - The refresh token to be set.
281
341
  */
282
- setRefreshToken(token) {
342
+ setRefreshToken(token: string | null): void {
283
343
  this._refreshToken = token;
284
- this._tokenStorage.setRefreshToken(token);
344
+ this._tokenStorage.setRefreshToken(token ?? "");
285
345
  if(this.userId === null){
286
346
  // Extrait l'id depuis le token et le stocke si disponible
287
347
  const userId = this._getIdFromToken(token);
@@ -295,34 +355,27 @@ export default class ApiClient extends EventEmitter {
295
355
 
296
356
  /**
297
357
  * Retrieves the current refresh token.
298
- *
299
- * @returns {string|null} The refresh token.
300
358
  */
301
- getRefreshToken() {
359
+ getRefreshToken(): string | null {
302
360
  return this._refreshToken;
303
361
  }
304
362
 
305
363
  /**
306
364
  * Indique si le client est connecté.
307
365
  * On considère que le client est connecté si un token d'accès (_accessToken) est défini.
308
- *
309
- * @returns {boolean} True si connecté, false sinon.
310
366
  */
311
- get isConnected() {
367
+ get isConnected(): boolean {
312
368
  return !!this._accessToken;
313
369
  }
314
370
 
315
371
  /**
316
- * Extrait l'identifiant depuis un JWT.
317
- *
318
- * @param {string} token - Le token JWT (accessToken ou refreshToken).
319
- * @returns {string|null} L'identifiant extrait ou null si non trouvé.
320
- */
321
- _getIdFromToken(token) {
372
+ * Extrait l'identifiant depuis un JWT.
373
+ */
374
+ private _getIdFromToken(token: string | null): string | null {
322
375
  if (!token) return null;
323
376
  try {
324
377
  // Décodage du token grâce à jwt-decode
325
- const payload = jwtDecode(token);
378
+ const payload = jwtDecode<JwtPayload & { id?: string; userId?: string }>(token);
326
379
  // L'identifiant peut être dans "id" ou "userId"
327
380
  this._logger.debug("[ApiClient] Payload décodé :", payload);
328
381
  return payload.id || payload.userId || null;
@@ -333,10 +386,10 @@ export default class ApiClient extends EventEmitter {
333
386
  }
334
387
 
335
388
  /**
336
- * Méthode simplifiée de refresh (en JSON).
389
+ * Méthode simplifiée de refresh (en JSON).
337
390
  * Emet un event refreshSuccess si ça marche
338
391
  */
339
- async _refreshAccessToken() {
392
+ private async _refreshAccessToken(): Promise<boolean> {
340
393
  if (!this._refreshToken) return false;
341
394
 
342
395
  const refreshClient = axios.create({
@@ -360,22 +413,21 @@ export default class ApiClient extends EventEmitter {
360
413
  return false;
361
414
  } catch (err) {
362
415
  // Si on a une erreur, on reset la session
363
- this.emit("refreshFailed", { error: err.message });
416
+ this.emit("refreshFailed", { error: getErrorMessage(err) });
364
417
  this.resetSession();
365
- this._logger.error(`[ApiClient] Refresh Error : ${err.message}`);
418
+ this._logger.error(`[ApiClient] Refresh Error : ${getErrorMessage(err)}`);
366
419
  return false;
367
420
  }
368
421
  }
369
422
 
370
423
  /**
371
424
  * checkCircuitBreaker : vérifie si on peut appeler l'API ou non
372
- * si le breaker est \"open\", on regarde si on peut \"reset\"
425
+ * si le breaker est "open", on regarde si on peut "reset"
373
426
  */
374
- _checkCircuitBreaker() {
427
+ private _checkCircuitBreaker(): boolean {
375
428
  if (!this._breakerOpen) return true;
376
429
  const now = Date.now();
377
- if (now - this._lastBreakerOpenTime > this._breakerResetTime) {
378
- // On reset
430
+ if (this._lastBreakerOpenTime != null && (now - this._lastBreakerOpenTime) > this._breakerResetTime) {
379
431
  this._breakerOpen = false;
380
432
  this._breakerErrorCount = 0;
381
433
  this._logger.warn("[ApiClient] Circuit breaker réinitialisé");
@@ -386,10 +438,10 @@ export default class ApiClient extends EventEmitter {
386
438
  }
387
439
 
388
440
  /**
389
- * updateCircuitBreaker : incremente le compteur d'erreurs
390
- * 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.
391
443
  */
392
- _updateCircuitBreakerError() {
444
+ private _updateCircuitBreakerError(): void {
393
445
  this._breakerErrorCount += 1;
394
446
  this._logger.warn(`[ApiClient] Erreur #${this._breakerErrorCount} sur ${this._breakerThreshold}`);
395
447
  if (this._breakerErrorCount >= this._breakerThreshold) {
@@ -403,7 +455,7 @@ export default class ApiClient extends EventEmitter {
403
455
  /**
404
456
  * resetCircuitBreaker : en cas de succès on reset le compteur
405
457
  */
406
- _resetCircuitBreakerSuccess() {
458
+ private _resetCircuitBreakerSuccess(): void {
407
459
  this._breakerErrorCount = 0;
408
460
  if (this._breakerOpen) {
409
461
  this._breakerOpen = false;
@@ -411,7 +463,7 @@ export default class ApiClient extends EventEmitter {
411
463
  }
412
464
  }
413
465
 
414
- static stripNullsInPlace(obj) {
466
+ static stripNullsInPlace(obj: any): any {
415
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.
416
468
  if (obj && typeof obj === "object" && obj.value && typeof obj.value.pipe === "function") {
417
469
  return obj;
@@ -439,13 +491,13 @@ export default class ApiClient extends EventEmitter {
439
491
  return obj;
440
492
  }
441
493
 
442
- _resolveSpecialValuesInPlace(obj, pathParams = {}) {
443
- const aliasMap = {
494
+ private _resolveSpecialValuesInPlace(obj: any, pathParams: any = {}) {
495
+ const aliasMap: any = {
444
496
  userId: () => this.userId,
445
497
  accessToken: () => this._accessToken,
446
498
  refreshToken: () => this._refreshToken,
447
499
  baseURL: () => this._baseURL,
448
- pathParams: (subPath) => {
500
+ pathParams: (subPath: any) => {
449
501
  // Si la valeur existe dans pathParams, on la retourne
450
502
  const value = this._getValueByPath(pathParams, subPath);
451
503
  return value !== undefined && value !== null ? value : undefined;
@@ -455,7 +507,7 @@ export default class ApiClient extends EventEmitter {
455
507
  // Expression régulière qui capture les alias sous forme délimitée @{...} ou simple @...
456
508
  const regex = /@(?:\{([^}]+)\}|([\w.]+))/g;
457
509
 
458
- const resolveString = (str) => {
510
+ const resolveString = (str: any) => {
459
511
  if (typeof str !== "string") return str;
460
512
  return str.replace(regex, (_, delimitedAlias, plainAlias) => {
461
513
  // Si alias délimité, on traite toute la chaîne à l'intérieur des accolades.
@@ -504,17 +556,17 @@ export default class ApiClient extends EventEmitter {
504
556
 
505
557
 
506
558
  // Vérifie si l'objet est un binaire (par exemple un flux ou un Buffer)
507
- const isBinary = (input) => {
559
+ const isBinary = (input: any) => {
508
560
  return input && typeof input === "object" &&
509
561
  (typeof input.pipe === "function" || (typeof Buffer !== "undefined" && input instanceof Buffer));
510
562
  };
511
-
563
+
512
564
  // Vérifie si l'objet est un "plain object"
513
- const isPlainObject = (input) => {
565
+ const isPlainObject = (input: any) => {
514
566
  return Object.prototype.toString.call(input) === "[object Object]";
515
567
  };
516
-
517
- const internal = (input) => {
568
+
569
+ const internal = (input: any): any => {
518
570
  // Si c'est un binaire, ne rien modifier
519
571
  if (isBinary(input)) {
520
572
  return input;
@@ -525,7 +577,7 @@ export default class ApiClient extends EventEmitter {
525
577
  }
526
578
  // Si c'est un plain object, on crée un nouvel objet avec remplacement dans les clés et valeurs
527
579
  if (isPlainObject(input)) {
528
- const result = {};
580
+ const result: any = {};
529
581
  for (const [key, value] of Object.entries(input)) {
530
582
  const resolvedKey = resolveString(key);
531
583
  result[resolvedKey] = internal(value);
@@ -544,22 +596,22 @@ export default class ApiClient extends EventEmitter {
544
596
  }
545
597
 
546
598
 
547
- _cleanSchemaLeftoverAlias(obj) {
599
+ private _cleanSchemaLeftoverAlias(obj: any): any {
548
600
  // Expression régulière qui détecte n'importe quel alias du type "@quelqueChose"
549
601
  const aliasRegex = /@\w+/;
550
602
 
551
603
  if (Array.isArray(obj)) {
552
- return obj.map(item => this._cleanSchemaLeftoverAlias(item));
604
+ return obj.map((item: any) => this._cleanSchemaLeftoverAlias(item));
553
605
  } else if (obj && typeof obj === "object") {
554
- const newObj = {};
606
+ const newObj: any = {};
555
607
  for (const key of Object.keys(obj)) {
556
608
  const val = obj[key];
557
-
609
+
558
610
  // Si le nom de la clé contient un alias (exemple : "@userId", "@anything"), on ne la copie pas
559
611
  if (aliasRegex.test(key)) {
560
612
  continue;
561
613
  }
562
-
614
+
563
615
  // Si la propriété s'appelle "default"
564
616
  if (key === "default") {
565
617
  // Si la valeur est une chaîne et qu'elle contient un alias, on ne la copie pas
@@ -576,12 +628,12 @@ export default class ApiClient extends EventEmitter {
576
628
  continue;
577
629
  }
578
630
  }
579
-
631
+
580
632
  // Si la valeur est une chaîne contenant un alias, on ne la copie pas
581
633
  if (typeof val === "string" && aliasRegex.test(val)) {
582
634
  continue;
583
635
  }
584
-
636
+
585
637
  // Sinon, on nettoie récursivement la valeur
586
638
  newObj[key] = this._cleanSchemaLeftoverAlias(val);
587
639
  }
@@ -594,32 +646,39 @@ export default class ApiClient extends EventEmitter {
594
646
  * Safely calls an asynchronous function and handles any errors that occur.
595
647
  * Logs the error message using the instance's logger before re-throwing the error.
596
648
  *
597
- * @param {Function} fn - The asynchronous function to be called.
598
- * @param {...any} args - The arguments to pass to the function.
599
- * @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.
600
652
  * @throws {Error} Re-throws any error that occurs during the function execution.
601
653
  */
602
- async safeCall(fn, ...args) {
654
+ async safeCall(fn: (...args: any[]) => Promise<any>, ...args: any[]) {
603
655
  try {
604
656
  return await fn(...args);
605
657
  } catch (error) {
606
- this._logger.error(`[ApiClient.safeCall] Erreur: ${error.message}`);
658
+ this._logger.error(`[ApiClient.safeCall] Erreur: ${getErrorMessage(error)}`);
607
659
  throw error;
608
660
  }
609
661
  }
610
662
 
611
663
  /**
612
- * 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.
613
667
  *
614
- * @param {string} constant - The constant representing the endpoint to call.
615
- * @param {Object} [data={}] - The data to send with the request.
616
- * @param {boolean|function} [transformResponseData=true] - Whether to transform the response data or a function to transform it.
617
- * @param {boolean} [validateResponseSchema=true] - Whether to validate the response schema.
618
- * @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.
619
673
  * @throws {CircuitBreakerError} If the circuit breaker is activated.
620
674
  * @throws {ApiClientError} If the endpoint is not found, token is required but not provided, or validation fails.
621
675
  */
622
- 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>> {
623
682
 
624
683
  const endpoint = this._endpoints.find((ep) => ep.constant === constant);
625
684
  if (!endpoint) {
@@ -638,8 +697,8 @@ export default class ApiClient extends EventEmitter {
638
697
 
639
698
  const lowerMethod = (method || "GET").toLowerCase();
640
699
  const realContentType = contentType || "application/json";
641
- const headers = { "Content-Type": realContentType };
642
-
700
+ const headers: Record<string, string> = { "Content-Type": realContentType };
701
+
643
702
  // Auth headers
644
703
  if (this._accessToken) {
645
704
  if (auth === "bearer") {
@@ -665,19 +724,19 @@ export default class ApiClient extends EventEmitter {
665
724
  }
666
725
 
667
726
  const pathParams = data.pathParams || {};
668
- const validatePathParams = this._ajv.compile(schemaToCompile);
727
+ const validatePathParams: ValidateFunction = this._ajv.compile(schemaToCompile);
669
728
  const valid = validatePathParams(pathParams);
670
729
 
671
730
  if (!valid) {
672
- const errorMessages = this._ajvErrorHuman(validatePathParams.errors);
731
+ const errorMessages = this._ajvErrorHuman(validatePathParams.errors ?? []);
673
732
  this.emit("validationError", { stage: "pathParams", errors: validatePathParams.errors });
674
733
  throw new ApiValidationError(`callEndpoint: ${constant} - Path parameter validation failed.`, 400, errorMessages, validatePathParams.errors);
675
734
  }
676
735
 
677
736
  resolvedParams = this._resolveSpecialValuesInPlace(pathParams);
678
-
679
- resolvedPath = resolvedPath.replace(/\{(\w+)\}/g, (_, key) => {
680
- const val = resolvedParams[key];
737
+
738
+ resolvedPath = resolvedPath.replace(/\{(\w+)\}/g, (_: any, key: string) => {
739
+ const val = (resolvedParams as any)[key];
681
740
  if (val !== undefined) return encodeURIComponent(val);
682
741
  throw new ApiClientError(`Path param manquant ou non résolu : {${key}}`, 400);
683
742
  });
@@ -691,11 +750,19 @@ export default class ApiClient extends EventEmitter {
691
750
  schemaToCompile = this._cleanSchemaLeftoverAlias(requestSchema);
692
751
  }
693
752
 
694
- const dataForValidation = { ...data };
753
+ let dataForValidation = { ...data };
695
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
+
696
763
  const cleanedData = ApiClient.stripNullsInPlace(dataForValidation);
697
-
698
- const validateRequest = this._ajv.compile(schemaToCompile);
764
+
765
+ const validateRequest: ValidateFunction = this._ajv.compile(schemaToCompile);
699
766
  const valid = validateRequest(cleanedData);
700
767
  if (!valid) {
701
768
  const errorMessages = validateRequest.errors ? this._ajvErrorHuman(validateRequest.errors) : [];
@@ -724,7 +791,8 @@ export default class ApiClient extends EventEmitter {
724
791
  data
725
792
  });
726
793
 
727
- return { data: null, offline: true };
794
+ const enqueued = { data: null, offline: true as const };
795
+ return enqueued;
728
796
  } else {
729
797
  this._logger.warn("[ApiClient] Mode dégradé actif mais offlineManager non initialisé correctement");
730
798
  throw new ApiClientError("Mode hors-ligne actif, mais gestionnaire offline non disponible.", 503);
@@ -752,7 +820,8 @@ export default class ApiClient extends EventEmitter {
752
820
  data
753
821
  });
754
822
 
755
- return { data: null, breaker: true };
823
+ const enqueued = { data: null, breaker: true as const };
824
+ return enqueued;
756
825
  }
757
826
 
758
827
  throw new CircuitBreakerError("Le circuit breaker est activé, impossible d'appeler l'API");
@@ -777,15 +846,15 @@ export default class ApiClient extends EventEmitter {
777
846
  });
778
847
 
779
848
  if (validateResponseSchema) {
780
- const status = response.status.toString();
781
- const schema = responses?.[status];
849
+ const statusStr = String(response.status);
850
+ const schema = responses?.[statusStr];
782
851
  if (schema) {
783
- const validateResponse = this._ajv.compile(schema);
852
+ const validateResponse: ValidateFunction = this._ajv.compile(schema);
784
853
  const valid = validateResponse(response.data);
785
854
  if (!valid) {
786
- const errorMessages = this._ajvErrorHuman(validateResponse.errors);
855
+ const errorMessages = this._ajvErrorHuman(validateResponse.errors ?? []);
787
856
  this.emit("validationError", { stage: "response", errors: validateResponse.errors });
788
- throw new ApiValidationError("Response validation failed.", status, errorMessages, validateResponse.errors);
857
+ throw new ApiValidationError("Response validation failed.", response.status, errorMessages, validateResponse.errors);
789
858
  }
790
859
  }
791
860
  }
@@ -800,7 +869,7 @@ export default class ApiClient extends EventEmitter {
800
869
 
801
870
  // postActions éventuelles
802
871
  if (Array.isArray(endpoint.postActions)) {
803
- endpoint.postActions.forEach(action => {
872
+ endpoint.postActions.forEach((action: any) => {
804
873
  const value = action.path ? this._getValueByPath(response.data, action.path) : null;
805
874
 
806
875
  switch (action.type) {
@@ -828,8 +897,8 @@ export default class ApiClient extends EventEmitter {
828
897
  break;
829
898
 
830
899
  case "callMethod":
831
- if (typeof this[action.method] === "function") {
832
- this[action.method]();
900
+ if (typeof (this as any)[action.method] === "function") {
901
+ (this as any)[action.method]();
833
902
  this._logger.debug(`[ApiClient] Méthode appelée : ${action.method}`);
834
903
  } else {
835
904
  this._logger.warn(`[ApiClient] Méthode inconnue : ${action.method}`);
@@ -846,14 +915,16 @@ export default class ApiClient extends EventEmitter {
846
915
  return response;
847
916
  } catch (error) {
848
917
  this._updateCircuitBreakerError();
849
- 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)}`);
850
919
  if(error instanceof ApiValidationError) {
851
920
  throw error;
852
921
  } else {
922
+ const statusCode = hasResponse(error) ? error.response.status : 500;
923
+ const responseData = hasResponse(error) ? error.response.data : null;
853
924
  throw new ApiClientError(
854
- `Erreur lors de l'appel de l'API : ${error.message}`,
855
- error.response ? error.response.status : 500,
856
- error.response ? error.response.data : null
925
+ `Erreur lors de l'appel de l'API : ${getErrorMessage(error)}`,
926
+ statusCode,
927
+ responseData
857
928
  );
858
929
  }
859
930
 
@@ -863,17 +934,18 @@ export default class ApiClient extends EventEmitter {
863
934
  /**
864
935
  * Converts AJV (Another JSON Schema Validator) errors into human-readable messages.
865
936
  *
866
- * @param {Array} errors - An array of AJV validation error objects.
867
- * @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.
868
939
  */
869
- _ajvErrorHuman(errors){
940
+ private _ajvErrorHuman(errors: any[]){
870
941
  try {
871
942
  const errorsMessages = new AggregateAjvError(errors);
872
- 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);
873
945
  return messages;
874
946
  } catch (e) {
875
947
  this._logger.error("[ApiClient] _ajvErrorHuman", e);
876
- return errors.map(({ message }) => message);
948
+ return errors.map(({ message }: any) => message);
877
949
  }
878
950
  }
879
951
 
@@ -889,13 +961,15 @@ export default class ApiClient extends EventEmitter {
889
961
  *
890
962
  * If no errors are found, the original `response` is returned.
891
963
  *
892
- * @param {Object} response - The API response object to check.
893
- * @param {Object} response.data - The data payload of the response.
894
- * @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 }}
895
966
  * @throws {ApiResponseError} If an error is detected in the response data.
896
- * @returns {Object} The original `response` object if no errors are found.
897
967
  */
898
- checkAndThrowApiResponseError(response) {
968
+ checkAndThrowApiResponseError(response: any) {
969
+ if (!("status" in response)) {
970
+ return response;
971
+ }
972
+
899
973
  const data = response.data;
900
974
  if (!data || typeof data !== "object") {
901
975
  return response;
@@ -931,7 +1005,7 @@ export default class ApiClient extends EventEmitter {
931
1005
  * - userId
932
1006
  * - En-têtes Axios
933
1007
  */
934
- resetSession() {
1008
+ resetSession(): void {
935
1009
  this.setToken(null);
936
1010
  this.setRefreshToken(null);
937
1011
  this._setUserId(null);
@@ -946,12 +1020,8 @@ export default class ApiClient extends EventEmitter {
946
1020
 
947
1021
  /**
948
1022
  * Retrieves the value from an object based on a dot-separated path.
949
- *
950
- * @param {Object} obj - The object from which to retrieve the value.
951
- * @param {string} path - The dot-separated path string indicating the value to retrieve.
952
- * @returns {*} - The value found at the specified path, or undefined if the path is invalid.
953
1023
  */
954
- _getValueByPath(obj, path) {
1024
+ private _getValueByPath(obj: any, path: string): any {
955
1025
  if (!path) return undefined;
956
1026
  const keys = path.split(".");
957
1027
  return keys.reduce((acc, key) => acc && acc[key], obj);
@@ -959,54 +1029,139 @@ export default class ApiClient extends EventEmitter {
959
1029
 
960
1030
  // Conversions
961
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
+
962
1118
  /**
963
1119
  * Converts an object to URL search parameters.
964
1120
  *
965
- * @param {Object} obj - The object to be converted to URL search parameters.
966
- * @param {Object} [options={}] - Optional settings for the conversion.
967
- * @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.
968
1124
  */
969
- static toURLSearchParams(obj, options = {}) {
970
- return this._buildParams(obj, new URLSearchParams(), options);
1125
+ static toURLSearchParams(obj: any, options: any = {}) {
1126
+ return this._buildParams(obj, new URLSearchParams(), options) as URLSearchParams;
971
1127
  }
972
1128
 
973
1129
  /**
974
1130
  * Builds parameters for an API request from a given object.
975
- *
976
- * @param {Object} obj - The object to be converted into parameters.
977
- * @param {FormData|URLSearchParams} paramsInstance - The instance to which the parameters will be appended.
978
- * @param {Object} [options={}] - Optional settings.
979
- * @param {boolean} [options.dots=false] - Whether to use dots in the parameter keys.
980
- * @param {boolean} [options.indexes=false] - Whether to include array indexes in the parameter keys.
981
- * @param {boolean} [options.metaTokens=true] - Whether to include meta tokens in the parameter keys.
982
- * @throws {TypeError} If the provided obj is not an object or is null.
983
- * @returns {FormData|URLSearchParams} The instance with the appended parameters.
984
1131
  */
985
- 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 {
986
1141
  if (typeof obj !== "object" || obj === null) {
987
1142
  throw new TypeError("La donnée doit être un objet non nul.");
988
1143
  }
989
1144
 
990
1145
  const { dots = false, indexes = false, metaTokens = true } = options;
991
- const stack = [];
1146
+ const stack: any[] = [];
992
1147
 
993
- function isVisitable(thing) {
1148
+ function isVisitable(thing: any) {
994
1149
  return Object.prototype.toString.call(thing) === "[object Object]" || Array.isArray(thing);
995
1150
  }
996
1151
 
997
- function removeBrackets(key) {
1152
+ function removeBrackets(key: any) {
998
1153
  return key.endsWith("[]") ? key.slice(0, -2) : key;
999
1154
  }
1000
1155
 
1001
- function renderKey(path, key, useDots = false) {
1156
+ function renderKey(path: any, key: any, useDots = false) {
1002
1157
  if (!path) return key;
1003
- return path.concat(key).map((token, i) => {
1158
+ return path.concat(key).map((token: any, i: any) => {
1004
1159
  token = removeBrackets(token);
1005
1160
  return !useDots && i ? `[${token}]` : token;
1006
1161
  }).join(useDots ? "." : "");
1007
1162
  }
1008
1163
 
1009
- function convertValue(value) {
1164
+ function convertValue(value: any) {
1010
1165
  if (value === null || value === undefined) return "";
1011
1166
  if (value instanceof Date) return value.toISOString();
1012
1167
  if (
@@ -1020,7 +1175,7 @@ export default class ApiClient extends EventEmitter {
1020
1175
  return value;
1021
1176
  }
1022
1177
 
1023
- function defaultVisitor(value, key, path) {
1178
+ function defaultVisitor(value: any, key: any, path: any) {
1024
1179
  if (value && typeof value === "object") {
1025
1180
  if (key.endsWith("{}")) {
1026
1181
  key = metaTokens ? key : key.slice(0, -2);
@@ -1030,7 +1185,7 @@ export default class ApiClient extends EventEmitter {
1030
1185
 
1031
1186
  if ((Array.isArray(value) && !value.some(isVisitable)) || key.endsWith("[]")) {
1032
1187
  key = removeBrackets(key);
1033
- value.forEach((el, index) => {
1188
+ value.forEach((el: any, index: any) => {
1034
1189
  if (el !== undefined && el !== null) {
1035
1190
  // const fieldKey = indexes ? renderKey([key], index, dots) : `${key}[]`;
1036
1191
  const fieldKey = indexes
@@ -1048,9 +1203,9 @@ export default class ApiClient extends EventEmitter {
1048
1203
  return false;
1049
1204
  }
1050
1205
 
1051
- function build(value, path = []) {
1052
- if (value === undefined || value === null || stack.includes(value)) return;
1053
- 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);
1054
1209
 
1055
1210
  Object.entries(value).forEach(([k, el]) => {
1056
1211
  if (el !== undefined && defaultVisitor(el, k.trim(), path)) {
@@ -1074,12 +1229,12 @@ export default class ApiClient extends EventEmitter {
1074
1229
  * nested structures like `resultGoods`, `resultErrors`, `results`, `news`,
1075
1230
  * `notif`, `citoyens`, `organizations`, `cities`, `newComment`, `map`, and `object`.
1076
1231
  *
1077
- * @param {Object|Array} data - The data to be transformed.
1078
- * @returns {Object|Array} - The transformed data.
1232
+ * @param data - The data to be transformed.
1233
+ * @returns - The transformed data.
1079
1234
  *
1080
1235
  * @private
1081
1236
  */
1082
- _transformData(data) {
1237
+ private _transformData(data: any): any {
1083
1238
  if (data && typeof data === "object") {
1084
1239
  if (data.resultGoods?.msg) {
1085
1240
  data = this._normalizeJsonData({ msg: data.resultGoods.msg, ...data });
@@ -1090,7 +1245,7 @@ export default class ApiClient extends EventEmitter {
1090
1245
  return this._normalizeJsonData({ id: key, ...data.results[key] });
1091
1246
  });
1092
1247
  } else if (Array.isArray(data.results) && data.results.length > 0) {
1093
- data.results = data.results.map((item) => this._normalizeJsonData(item));
1248
+ data.results = data.results.map((item: any) => this._normalizeJsonData(item));
1094
1249
  } else if (data.news && Array.isArray(data.news) && data.news.length === 0) {
1095
1250
  data = data.news;
1096
1251
  } else if (data.news && typeof data.news === "object" && !Array.isArray(data.news)) {
@@ -1145,8 +1300,8 @@ export default class ApiClient extends EventEmitter {
1145
1300
  /**
1146
1301
  * Normalizes JSON data by transforming specific fields and ensuring URLs are complete.
1147
1302
  *
1148
- * @param {Object} item - The JSON object to be normalized.
1149
- * @returns {Object} - The normalized JSON object.
1303
+ * @param item - The JSON object to be normalized.
1304
+ * @returns - The normalized JSON object.
1150
1305
  *
1151
1306
  * The function performs the following transformations:
1152
1307
  * - Normalizes the ID field if it matches a specific pattern.
@@ -1156,7 +1311,7 @@ export default class ApiClient extends EventEmitter {
1156
1311
  * - Removes the `timeAgo` field if it exists.
1157
1312
  * - Converts the object to EJSON format if necessary.
1158
1313
  */
1159
- _normalizeJsonData(item) {
1314
+ private _normalizeJsonData(item: any) {
1160
1315
  if (!item || typeof item !== "object") {
1161
1316
  return item;
1162
1317
  }
@@ -1194,7 +1349,7 @@ export default class ApiClient extends EventEmitter {
1194
1349
 
1195
1350
  if (item?.openingHours && Array.isArray(item.openingHours) && item.openingHours.length > 0) {
1196
1351
  item.openingHours = item.openingHours.filter(
1197
- (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
1198
1353
  );
1199
1354
  }
1200
1355
 
@@ -1223,6 +1378,10 @@ export default class ApiClient extends EventEmitter {
1223
1378
  if (item?.timeAgo) {
1224
1379
  delete item.timeAgo;
1225
1380
  }
1381
+
1382
+ if(item?.author?.typeSig === "people"){
1383
+ item.author.type = "citoyens";
1384
+ }
1226
1385
 
1227
1386
  // Convertit en EJSON si besoin
1228
1387
  return this._fromJSONValue ? EJSON.fromJSONValue(item) : item;
@@ -1234,10 +1393,10 @@ export default class ApiClient extends EventEmitter {
1234
1393
  * (i.e., it starts with "http://" or "https://"), it is returned as is. Otherwise, the image path
1235
1394
  * is concatenated with the base URL of the ApiClient instance.
1236
1395
  *
1237
- * @param {string} imagePath - The image path to ensure as a full URL.
1238
- * @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.
1239
1398
  */
1240
- _ensureFullURL(imagePath) {
1399
+ private _ensureFullURL(imagePath: any) {
1241
1400
  if (!imagePath) return imagePath;
1242
1401
  imagePath = imagePath.trim();
1243
1402
  if (!imagePath || /^https?:\/\//i.test(imagePath)) {
@@ -1261,8 +1420,8 @@ export default class ApiClient extends EventEmitter {
1261
1420
  * Pour chaque clé présente dans dateFields, si la valeur est un nombre ou un objet
1262
1421
  * contenant une propriété sec (nombre), la transforme en { $date: valeurEnMs }.
1263
1422
  *
1264
- * @param {Object} obj - L’objet à normaliser.
1265
- * @returns {Object} L’objet normalisé.
1423
+ * @param obj - L’objet à normaliser.
1424
+ * @returns L’objet normalisé.
1266
1425
  */
1267
1426
  // _normalizeDatesRecursively(obj) {
1268
1427
  // if (obj === null || typeof obj !== "object") return obj;
@@ -1293,8 +1452,8 @@ export default class ApiClient extends EventEmitter {
1293
1452
  * vérifie et convertit le chemin en URL complète à l’aide de _ensureFullURL.
1294
1453
  * Les cas particuliers (comme mediaImg.images ou mediaFile.files) sont également gérés.
1295
1454
  *
1296
- * @param {Object} obj - L’objet à normaliser.
1297
- * @returns {Object} L’objet normalisé.
1455
+ * @param obj - L’objet à normaliser.
1456
+ * @returns L’objet normalisé.
1298
1457
  */
1299
1458
  // _normalizeImagesRecursively(obj) {
1300
1459
  // if (obj === null || typeof obj !== "object") return obj;
@@ -1326,8 +1485,8 @@ export default class ApiClient extends EventEmitter {
1326
1485
  * Si un objet possède une propriété "_id" ou "id" contenant un sous-objet "$id" valide,
1327
1486
  * l'ID est normalisé et la propriété _id est convertie au format EJSON attendu.
1328
1487
  *
1329
- * @param {Object|Array} obj - L'objet ou le tableau à normaliser.
1330
- * @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é.
1331
1490
  */
1332
1491
  // _normalizeIdRecursively(obj) {
1333
1492
  // if (obj === null || typeof obj !== "object") return obj;
@@ -1361,8 +1520,8 @@ export default class ApiClient extends EventEmitter {
1361
1520
  * Normalise récursivement les valeurs booléennes.
1362
1521
  * Si une valeur est une chaîne "true" ou "false", elle est convertie en booléen.
1363
1522
  *
1364
- * @param {any} data - L'objet, le tableau ou la valeur à normaliser.
1365
- * @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.
1366
1525
  */
1367
1526
  // _normalizeBooleansRecursively(data) {
1368
1527
  // if (typeof data === "string") {
@@ -1384,10 +1543,10 @@ export default class ApiClient extends EventEmitter {
1384
1543
  /**
1385
1544
  * Transforme une chaîne "true" ou "false" en booléen.
1386
1545
  *
1387
- * @param {string} str - La chaîne à normaliser.
1388
- * @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.
1389
1548
  */
1390
- _normalizeString(str) {
1549
+ private _normalizeString(str: any) {
1391
1550
  if (str === "true") return true;
1392
1551
  if (str === "false") return false;
1393
1552
  return str;
@@ -1398,10 +1557,10 @@ export default class ApiClient extends EventEmitter {
1398
1557
  * Si l'objet possède une propriété "_id" ou "id" contenant un sous-objet "$id" valide,
1399
1558
  * il met à jour l'objet en assignant la valeur de l'ID et en formattant _id en EJSON.
1400
1559
  *
1401
- * @param {Object} obj - L'objet à normaliser.
1402
- * @returns {Object} L'objet avec l'ID normalisé.
1560
+ * @param obj - L'objet à normaliser.
1561
+ * @returns L'objet avec l'ID normalisé.
1403
1562
  */
1404
- _normalizeId(obj) {
1563
+ private _normalizeId(obj: any) {
1405
1564
  if (obj._id && obj._id.$id && /^[0-9a-fA-F]{24}$/.test(obj._id.$id)) {
1406
1565
  obj.id = obj._id.$id;
1407
1566
  obj._id = { $type: "oid", $value: obj._id.$id };
@@ -1418,10 +1577,10 @@ export default class ApiClient extends EventEmitter {
1418
1577
  * Si la valeur est un objet contenant "sec" (nombre) ou un nombre,
1419
1578
  * elle est convertie en { $date: valeurEnMs }.
1420
1579
  *
1421
- * @param {any} value - La valeur à normaliser.
1422
- * @returns {any} La valeur normalisée.
1580
+ * @param value - La valeur à normaliser.
1581
+ * @returns La valeur normalisée.
1423
1582
  */
1424
- _normalizeDate(value) {
1583
+ private _normalizeDate(value: any) {
1425
1584
  if (value && typeof value === "object" && typeof value.sec === "number") {
1426
1585
  return { $date: value.sec * 1000 };
1427
1586
  } else if (typeof value === "number") {
@@ -1434,10 +1593,10 @@ export default class ApiClient extends EventEmitter {
1434
1593
  * Normalise une URL d'image.
1435
1594
  * Si la valeur est une chaîne non vide, retourne l'URL complète via _ensureFullURL.
1436
1595
  *
1437
- * @param {any} value - La valeur à normaliser.
1438
- * @returns {any} La valeur normalisée.
1596
+ * @param value - La valeur à normaliser.
1597
+ * @returns La valeur normalisée.
1439
1598
  */
1440
- _normalizeImage(value) {
1599
+ private _normalizeImage(value: any) {
1441
1600
  if (typeof value === "string" && value.trim() !== "") {
1442
1601
  return this._ensureFullURL(value);
1443
1602
  }
@@ -1447,7 +1606,7 @@ export default class ApiClient extends EventEmitter {
1447
1606
  /**
1448
1607
  * Liste des champs d'image à normaliser.
1449
1608
  */
1450
- _imageFields = [
1609
+ private _imageFields = [
1451
1610
  "profilImageUrl",
1452
1611
  "profilThumbImageUrl",
1453
1612
  "profilMediumImageUrl",
@@ -1464,7 +1623,7 @@ export default class ApiClient extends EventEmitter {
1464
1623
  /**
1465
1624
  * Liste des champs de date à normaliser.
1466
1625
  */
1467
- _dateFields = ["modified", "created", "updated", "birthDate", "lastLoginDate", "startDate", "endDate", "date", "issuedOn"];
1626
+ private _dateFields = ["modified", "created", "updated", "birthDate", "lastLoginDate", "startDate", "endDate", "date", "issuedOn"];
1468
1627
 
1469
1628
  /**
1470
1629
  * Normalise récursivement un objet, un tableau ou une valeur simple.
@@ -1475,10 +1634,10 @@ export default class ApiClient extends EventEmitter {
1475
1634
  * - Transformation récursive des objets et tableaux.
1476
1635
  * - Normalisation des identifiants.
1477
1636
  *
1478
- * @param {any} data - La donnée à normaliser.
1479
- * @returns {any} La donnée normalisée.
1637
+ * @param data - La donnée à normaliser.
1638
+ * @returns La donnée normalisée.
1480
1639
  */
1481
- _normalizeRecursively(data) {
1640
+ private _normalizeRecursively(data: any): any {
1482
1641
  // Cas de base pour les chaînes
1483
1642
  if (typeof data === "string") {
1484
1643
  return this._normalizeString(data);
@@ -1486,13 +1645,13 @@ export default class ApiClient extends EventEmitter {
1486
1645
 
1487
1646
  // Traitement des tableaux
1488
1647
  if (Array.isArray(data)) {
1489
- return data.map(item => this._normalizeRecursively(item));
1648
+ return data.map((item: any) => this._normalizeRecursively(item));
1490
1649
  }
1491
1650
 
1492
1651
  // Traitement des objets non nuls
1493
1652
  if (data !== null && typeof data === "object") {
1494
1653
  // On crée une copie de l'objet pour éviter les effets de bord.
1495
- const normalizedData = {};
1654
+ const normalizedData: any = {};
1496
1655
 
1497
1656
  Object.keys(data).forEach(key => {
1498
1657
  // Appliquer récursivement la normalisation sur la valeur.
@@ -1524,11 +1683,12 @@ export default class ApiClient extends EventEmitter {
1524
1683
  return data;
1525
1684
  }
1526
1685
 
1527
- _startBeforeEndValidate = (schema, data) => {
1686
+ private _startBeforeEndValidate = (schema: any, data: any) => {
1528
1687
  if (!data.startDate || !data.endDate) return true;
1529
1688
 
1530
1689
  const isValid = new Date(data.startDate) < new Date(data.endDate);
1531
1690
  if (!isValid) {
1691
+ // @ts-expect-error - on sait que ValidateFunction expose .errors au runtime
1532
1692
  this._startBeforeEndValidate.errors = [
1533
1693
  {
1534
1694
  instancePath: "/startDate", // ou "." si tu veux le chemin actuel
@@ -1543,31 +1703,41 @@ export default class ApiClient extends EventEmitter {
1543
1703
  };
1544
1704
 
1545
1705
 
1546
- 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) {
1547
1712
  const endpoint = this._endpoints.find(e => e.constant === constant);
1548
1713
  return endpoint?.request || null;
1549
1714
  }
1550
1715
 
1551
- 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) {
1552
1722
  return this._endpoints.find(e => e.constant === constant)?.pathParams || null;
1553
1723
  }
1554
1724
 
1555
1725
  /**
1556
1726
  * Permet d'écouter facilement un ensemble d'événements importants émis par l'ApiClient.
1557
1727
  *
1558
- * @param {Object} handlers - Un objet avec des fonctions à appeler selon l'événement.
1559
- * @param {Function} [handlers.retryAttempt] - Lors d'une tentative de retry axios.
1560
- * @param {Function} [handlers.queuedOffline] - Lorsqu'une requête est mise en file.
1561
- * @param {Function} [handlers.circuitBreakerOpen] - Quand le breaker s'ouvre.
1562
- * @param {Function} [handlers.circuitBreakerReset] - Quand le breaker se referme.
1563
- * @param {Function} [handlers.refreshSuccess] - Quand le token est rafraîchi.
1564
- * @param {Function} [handlers.refreshFailed] - Quand le refresh échoue.
1565
- * @param {Function} [handlers.sessionReset] - Quand la session est réinitialisée.
1566
- * @param {Function} [handlers.validationError] - Quand une validation échoue.
1567
- * @param {Function} [handlers.offlineModeChanged] - Quand le mode offline change.
1568
- * @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.
1569
1739
  */
1570
- onEvent(handlers = {}) {
1740
+ onEvent(handlers: Record<string, (...args: any[]) => void> = {}) {
1571
1741
  const availableEvents = [
1572
1742
  "retryAttempt",
1573
1743
  "queuedOffline",
@@ -1583,7 +1753,7 @@ export default class ApiClient extends EventEmitter {
1583
1753
 
1584
1754
  for (const eventName of availableEvents) {
1585
1755
  if (typeof handlers[eventName] === "function") {
1586
- this.on(eventName, handlers[eventName]);
1756
+ this.on(eventName, handlers[eventName] as (...args: any[]) => void);
1587
1757
  }
1588
1758
  }
1589
1759
  }
@@ -1592,7 +1762,7 @@ export default class ApiClient extends EventEmitter {
1592
1762
  * Retourne la liste des noms d'événements personnalisés déclarés dans les endpoints.
1593
1763
  * Utile pour introspection ou documentation.
1594
1764
  *
1595
- * @returns {string[]} Liste des événements émis dynamiquement via postActions.
1765
+ * @returns Liste des événements émis dynamiquement via postActions.
1596
1766
  */
1597
1767
  getDeclaredEvents() {
1598
1768
  const events = new Set();
@@ -1609,46 +1779,65 @@ export default class ApiClient extends EventEmitter {
1609
1779
  }
1610
1780
 
1611
1781
  }
1782
+
1612
1783
  /**
1613
1784
  * @event ApiClient#retryAttempt
1614
1785
  * @type {Object}
1615
1786
  * @property {number} retryCount - Le numéro de tentative.
1616
1787
  * @property {string} url - L'URL de la requête ayant échoué.
1788
+ */
1617
1789
 
1790
+ /**
1618
1791
  * @event ApiClient#queuedOffline
1619
1792
  * @type {Object}
1620
1793
  * @property {string} constant - Le nom de l'endpoint.
1621
1794
  * @property {Object} data - Les données envoyées.
1622
1795
  * @property {"offlineMode"|"circuitBreaker"} reason - La raison de la mise en file.
1796
+ */
1623
1797
 
1798
+ /**
1624
1799
  * @event ApiClient#circuitBreakerOpen
1625
1800
  * @type {Object}
1626
1801
  * @property {number} timestamp - Date (ms) à laquelle le breaker s'est ouvert.
1802
+ */
1627
1803
 
1804
+ /**
1628
1805
  * @event ApiClient#circuitBreakerReset
1629
1806
  * @type {void}
1807
+ */
1630
1808
 
1809
+ /**
1631
1810
  * @event ApiClient#refreshSuccess
1632
1811
  * @type {Object}
1633
1812
  * @property {string} token - Le nouveau token d'accès.
1634
1813
  * @property {string} [refreshToken] - Le nouveau refreshToken (optionnel).
1814
+ */
1635
1815
 
1816
+ /**
1636
1817
  * @event ApiClient#refreshFailed
1637
1818
  * @type {Object}
1638
1819
  * @property {string} error - Le message d’erreur de refresh.
1820
+ */
1639
1821
 
1822
+ /**
1640
1823
  * @event ApiClient#sessionReset
1641
1824
  * @type {void}
1825
+ */
1642
1826
 
1827
+ /**
1643
1828
  * @event ApiClient#validationError
1644
1829
  * @type {Object}
1645
1830
  * @property {"pathParams"|"request"|"response"} stage - Étape de validation échouée.
1646
1831
  * @property {Array<Object>} errors - Erreurs AJV brutes.
1832
+ */
1647
1833
 
1834
+ /**
1648
1835
  * @event ApiClient#offlineModeChanged
1649
1836
  * @type {boolean} - true si le client est offline, false sinon.
1650
- *
1837
+ */
1838
+
1839
+ /**
1651
1840
  * @event ApiClient#userLoggedIn
1652
- * @param {Object} user - Les données utilisateur extraites de la réponse.
1841
+ * @param user - Les données utilisateur extraites de la réponse.
1653
1842
  */
1654
1843