@communecter/cocolight-api-client 1.0.31 → 1.0.33

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.
@@ -266,6 +266,109 @@ export class BaseEntity {
266
266
  */
267
267
  transforms = {};
268
268
 
269
+ /**
270
+ * ───────────────────────────────
271
+ * JSON
272
+ * ───────────────────────────────
273
+ */
274
+
275
+ /**
276
+ * Convertit l'instance en JSON pour l'envoi au serveur.
277
+ *
278
+ * @returns {Object} Représentation JSON de l'instance.
279
+ */
280
+ toJSON() {
281
+ const parentMeta = {};
282
+ if (this.parent?.id) parentMeta.id = this.parent.id;
283
+ if (typeof this.parent?.getEntityType === "function") parentMeta.type = this.parent.getEntityType();
284
+ if (this.parent?.__entityTag) parentMeta.__entityTag = this.parent.__entityTag;
285
+
286
+ return {
287
+ __entityTag: this.__entityTag,
288
+ _serverData: this._serialize(this._serverData),
289
+ parent: Object.keys(parentMeta).length > 0 ? parentMeta : null
290
+ };
291
+ }
292
+
293
+ _serialize(obj, seen = new WeakSet()) {
294
+ if (obj === null || typeof obj !== "object") return obj;
295
+
296
+ // Évite les boucles circulaires
297
+ if (seen.has(obj)) return null;
298
+ seen.add(obj);
299
+
300
+ // Si c'est une Date (normale ou Proxy)
301
+ if (obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]") {
302
+ return obj.toISOString();
303
+ }
304
+
305
+ // Si c'est un Proxy réactif de ton système maison
306
+ if (obj.__isReactive && typeof obj.__raw === "object") {
307
+ return this._serialize(obj.__raw, seen);
308
+ }
309
+
310
+ // Tableaux
311
+ if (Array.isArray(obj)) {
312
+ return obj.map((item) => this._serialize(item, seen));
313
+ }
314
+
315
+ // Objets simples
316
+ const result = {};
317
+ for (const key of Object.keys(obj)) {
318
+ const value = obj[key];
319
+
320
+ // Ignore les fonctions, symboles, ou undefined
321
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "undefined") {
322
+ continue;
323
+ }
324
+
325
+ try {
326
+ result[key] = this._serialize(value, seen);
327
+ // eslint-disable-next-line no-unused-vars
328
+ } catch (e) {
329
+ result[key] = null;
330
+ }
331
+ }
332
+
333
+ return result;
334
+ }
335
+
336
+ static _revive(obj) {
337
+ if (obj === null || typeof obj !== "object") return obj;
338
+
339
+ // Détecter et restaurer les ISO string en Date
340
+ if (typeof obj === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(obj)) {
341
+ return new Date(obj);
342
+ }
343
+
344
+ if (Array.isArray(obj)) {
345
+ return obj.map((item) => this._revive(item));
346
+ }
347
+
348
+ const result = {};
349
+ for (const key of Object.keys(obj)) {
350
+ result[key] = this._revive(obj[key]);
351
+ }
352
+
353
+ return result;
354
+ }
355
+
356
+ /**
357
+ * Crée une instance d'entité à partir de données JSON.
358
+ *
359
+ * @param {Object} json - Données JSON à utiliser.
360
+ * @param {Object} parent - Instance parente.
361
+ * @param {Object} deps - Dépendances injectées.
362
+ * @returns {object} Nouvelle instance d'entité.
363
+ */
364
+ static fromJSON(json, parent, deps) {
365
+
366
+ const { _serverData } = json;
367
+
368
+ const instance = this.fromServerData(this._revive(_serverData), parent, deps);
369
+
370
+ return instance;
371
+ }
269
372
 
270
373
  /**
271
374
  * ───────────────────────────────
@@ -1682,33 +1785,39 @@ export class BaseEntity {
1682
1785
  * @param {boolean} isNext - Indique si c'est une recherche suivante (pagination).
1683
1786
  * @returns {Promise<Object>} - Les données de réponse.
1684
1787
  */
1685
- async getOrganizations(data = {}, isNext = false) {
1788
+ async getOrganizations(data = {}) {
1686
1789
  data.searchType = this._getDefaultFromEndpoint("GET_ORGANIZATIONS_NO_ADMIN", "searchType");
1687
1790
  // data.searchBy = "ALL";
1688
- return this._paginateWith(data, isNext, async (finalData) => {
1689
- if(this.isMe){
1690
- finalData.pathParams = { type: this.getEntityType(), id: this.id };
1791
+
1792
+ const paginator = this._createPaginatorEngine({
1793
+ initialData: data,
1794
+ finalizer: async (finalData) => {
1795
+ if(this.isMe){
1796
+ finalData.pathParams = { type: this.getEntityType(), id: this.id };
1691
1797
  // NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
1692
1798
  // finalData.filters = {
1693
1799
  // [`links.members.${this.id}`]: { "$exists": true },
1694
1800
  // [`links.members.${this.id}.toBeValidated`]: { "$exists": false },
1695
1801
  // [`links.members.${this.id}.isInviting`]: { "$exists": false }
1696
1802
  // };
1697
- } else {
1698
- delete finalData?.pathParams;
1699
- finalData.filters = {
1700
- [`links.members.${this.id}`]: { "$exists": true },
1701
- [`links.members.${this.id}.toBeValidated`]: { "$exists": false },
1702
- [`links.members.${this.id}.isInviting`]: { "$exists": false }
1703
- };
1704
- }
1803
+ } else {
1804
+ delete finalData?.pathParams;
1805
+ finalData.filters = {
1806
+ [`links.members.${this.id}`]: { "$exists": true },
1807
+ [`links.members.${this.id}.toBeValidated`]: { "$exists": false },
1808
+ [`links.members.${this.id}.isInviting`]: { "$exists": false }
1809
+ };
1810
+ }
1705
1811
 
1706
- const fetchFn = this.isMe
1707
- ? () => this.callIsMe(() => this.endpointApi.getOrganizationsAdmin(finalData))
1708
- : () => this.endpointApi.getOrganizationsNoAdmin(finalData);
1812
+ const fetchFn = this.isMe
1813
+ ? () => this.callIsMe(() => this.endpointApi.getOrganizationsAdmin(finalData))
1814
+ : () => this.endpointApi.getOrganizationsNoAdmin(finalData);
1709
1815
 
1710
- return fetchFn();
1816
+ return fetchFn();
1817
+ }
1711
1818
  });
1819
+
1820
+ return paginator.next();
1712
1821
  }
1713
1822
 
1714
1823
  /**
@@ -1718,37 +1827,43 @@ export class BaseEntity {
1718
1827
  * @param {boolean} isNext - Indique si c'est une recherche suivante (pagination).
1719
1828
  * @returns {Promise<Object>} - Les données de réponse.
1720
1829
  */
1721
- async getProjects(data = {}, isNext = false) {
1830
+ async getProjects(data = {}) {
1722
1831
  data.searchType = this._getDefaultFromEndpoint("GET_PROJECTS_ADMIN", "searchType");
1723
1832
  // data.searchBy = "ALL";
1724
- return this._paginateWith(data, isNext, async (finalData) => {
1725
- if(this.isMe){
1726
- finalData.pathParams = { type: this.getEntityType(), id: this.id };
1727
- // NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
1728
- // finalData.filters = {
1729
- // "$or": {
1730
- // [`links.contributors.${this.id}`]: { "$exists": true },
1731
- // [`parent.${this.id}`]: { "$exists": true }
1732
- // },
1733
- // [`links.contributors.${this.id}`]: { "$exists": true }
1734
- // };
1735
- } else {
1736
- delete finalData?.pathParams;
1737
- finalData.filters = {
1738
- "$or": {
1739
- [`links.contributors.${this.id}`]: { "$exists": true },
1740
- [`parent.${this.id}`]: { "$exists": true }
1741
- },
1742
- [`links.contributors.${this.id}`]: { "$exists": true }
1743
- };
1744
- }
1833
+
1834
+ const paginator = this._createPaginatorEngine({
1835
+ initialData: data,
1836
+ finalizer: async (finalData) => {
1837
+ if(this.isMe){
1838
+ finalData.pathParams = { type: this.getEntityType(), id: this.id };
1839
+ // NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
1840
+ // finalData.filters = {
1841
+ // "$or": {
1842
+ // [`links.contributors.${this.id}`]: { "$exists": true },
1843
+ // [`parent.${this.id}`]: { "$exists": true }
1844
+ // },
1845
+ // [`links.contributors.${this.id}`]: { "$exists": true }
1846
+ // };
1847
+ } else {
1848
+ delete finalData?.pathParams;
1849
+ finalData.filters = {
1850
+ "$or": {
1851
+ [`links.contributors.${this.id}`]: { "$exists": true },
1852
+ [`parent.${this.id}`]: { "$exists": true }
1853
+ },
1854
+ [`links.contributors.${this.id}`]: { "$exists": true }
1855
+ };
1856
+ }
1745
1857
 
1746
- const fetchFn = this.isMe
1747
- ? () => this.callIsMe(() => this.endpointApi.getProjectsAdmin(finalData))
1748
- : () => this.endpointApi.getProjectsNoAdmin(finalData);
1858
+ const fetchFn = this.isMe
1859
+ ? () => this.callIsMe(() => this.endpointApi.getProjectsAdmin(finalData))
1860
+ : () => this.endpointApi.getProjectsNoAdmin(finalData);
1749
1861
 
1750
- return fetchFn();
1862
+ return fetchFn();
1863
+ }
1751
1864
  });
1865
+
1866
+ return paginator.next();
1752
1867
  }
1753
1868
 
1754
1869
  /**
@@ -1758,29 +1873,35 @@ export class BaseEntity {
1758
1873
  * @param {boolean} isNext - Indique si c'est une recherche suivante (pagination).
1759
1874
  * @returns {Promise<Object>} - Les données de réponse.
1760
1875
  */
1761
- async getPois(data = {}, isNext = false) {
1876
+ async getPois(data = {}) {
1762
1877
  data.searchType = this._getDefaultFromEndpoint("GET_POIS_ADMIN", "searchType");
1763
1878
  // data.searchBy = "ALL";
1764
- return this._paginateWith(data, isNext, async (finalData) => {
1765
- if(this.isMe){
1766
- finalData.pathParams = { type: this.getEntityType(), id: this.id };
1767
- // NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
1768
- // finalData.filters = {
1769
- // [`parent.${this.id}`]: { "$exists": true },
1770
- // };
1771
- } else {
1772
- delete finalData?.pathParams;
1773
- finalData.filters = {
1774
- [`parent.${this.id}`]: { "$exists": true },
1775
- };
1776
- }
1879
+
1880
+ const paginator = this._createPaginatorEngine({
1881
+ initialData: data,
1882
+ finalizer: async (finalData) => {
1883
+ if(this.isMe){
1884
+ finalData.pathParams = { type: this.getEntityType(), id: this.id };
1885
+ // NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
1886
+ // finalData.filters = {
1887
+ // [`parent.${this.id}`]: { "$exists": true },
1888
+ // };
1889
+ } else {
1890
+ delete finalData?.pathParams;
1891
+ finalData.filters = {
1892
+ [`parent.${this.id}`]: { "$exists": true },
1893
+ };
1894
+ }
1777
1895
 
1778
- const fetchFn = this.isMe
1779
- ? () => this.callIsMe(() => this.endpointApi.getPoisAdmin(finalData))
1780
- : () => this.endpointApi.getPoisNoAdmin(finalData);
1896
+ const fetchFn = this.isMe
1897
+ ? () => this.callIsMe(() => this.endpointApi.getPoisAdmin(finalData))
1898
+ : () => this.endpointApi.getPoisNoAdmin(finalData);
1781
1899
 
1782
- return fetchFn();
1900
+ return fetchFn();
1901
+ }
1783
1902
  });
1903
+
1904
+ return paginator.next();
1784
1905
  }
1785
1906
 
1786
1907
  /**
@@ -1790,20 +1911,26 @@ export class BaseEntity {
1790
1911
  * @param {boolean} isNext - Indique si c'est une recherche suivante (pagination).
1791
1912
  * @returns {Promise<Object>} - Les données de réponse.
1792
1913
  */
1793
- async getSubscribers(data = {}, isNext = false) {
1914
+ async getSubscribers(data = {}) {
1794
1915
  data.searchType = this._getDefaultFromEndpoint("GET_SUBSCRIBERS", "searchType");
1795
1916
  // data.searchBy = "ALL";
1796
- return this._paginateWith(data, isNext, async (finalData) => {
1797
- delete finalData?.pathParams;
1798
1917
 
1799
- finalData.filters = {
1800
- [`links.follows.${this.id}`]: { "$exists": true },
1801
- [`links.follows.${this.id}.toBeValidated`]: { "$exists": false },
1802
- [`links.follows.${this.id}.isInviting`]: { "$exists": false }
1803
- };
1918
+ const paginator = this._createPaginatorEngine({
1919
+ initialData: data,
1920
+ finalizer: async (finalData) => {
1921
+ delete finalData?.pathParams;
1922
+
1923
+ finalData.filters = {
1924
+ [`links.follows.${this.id}`]: { "$exists": true },
1925
+ [`links.follows.${this.id}.toBeValidated`]: { "$exists": false },
1926
+ [`links.follows.${this.id}.isInviting`]: { "$exists": false }
1927
+ };
1804
1928
 
1805
- return this.endpointApi.getSubscribers(finalData);
1929
+ return this.endpointApi.getSubscribers(finalData);
1930
+ }
1806
1931
  });
1932
+
1933
+ return paginator.next();
1807
1934
  }
1808
1935
 
1809
1936
  /**
@@ -1813,18 +1940,24 @@ export class BaseEntity {
1813
1940
  * @param {boolean} isNext - Indique si c'est une recherche suivante (pagination).
1814
1941
  * @returns {Promise<Object>} - Les données de réponse.
1815
1942
  */
1816
- async getBadgesIssuer(data = {}, isNext = false) {
1943
+ async getBadgesIssuer(data = {}) {
1817
1944
  data.searchType = this._getDefaultFromEndpoint("GET_BADGES", "searchType");
1818
1945
  // data.searchBy = "ALL";
1819
- return this._paginateWith(data, isNext, async (finalData) => {
1820
- delete finalData?.pathParams;
1946
+
1947
+ const paginator = this._createPaginatorEngine({
1948
+ initialData: data,
1949
+ finalizer: async (finalData) => {
1950
+ delete finalData?.pathParams;
1821
1951
 
1822
- finalData.filters = finalData.filters || {};
1823
- finalData.filters["$or"] = {};
1824
- finalData.filters["$or"][`issuer.${this.id}`] = { "$exists": true };
1952
+ finalData.filters = finalData.filters || {};
1953
+ finalData.filters["$or"] = {};
1954
+ finalData.filters["$or"][`issuer.${this.id}`] = { "$exists": true };
1825
1955
 
1826
- return this.endpointApi.getBadges(finalData);
1956
+ return this.endpointApi.getBadges(finalData);
1957
+ }
1827
1958
  });
1959
+
1960
+ return paginator.next();
1828
1961
  }
1829
1962
 
1830
1963
  /**
@@ -2187,161 +2320,163 @@ export class BaseEntity {
2187
2320
 
2188
2321
  return count;
2189
2322
  }
2190
-
2323
+
2191
2324
  /**
2192
- * Méthode générique de pagination contextuelle avec support next / prev.
2325
+ * Récupère une valeur par défaut depuis un endpoint donné.
2193
2326
  *
2194
- * @param {Object} data - Données d'entrée
2195
- * @param {boolean} isNext - true si appel de page suivante
2196
- * @param {Function} finalizer - fonction qui transforme et envoie les données (exécute la requête)
2197
- * @returns {Promise<Object>} { count, results, next, prev }
2198
- * @throws {ApiError} - Si le slug ou l'id de l'entité n'est pas défini.
2199
- * @throws {ApiResponseError} - Si les résultats ne sont pas un tableau.
2200
- * @private
2327
+ * @param {string} constant - Le nom unique de l’endpoint (ex: "GET_ORGANIZATIONS_NO_ADMIN")
2328
+ * @param {string} path - Le chemin vers la propriété (ex: "searchType")
2329
+ * @returns {*} La valeur par défaut, ou undefined si non trouvée
2201
2330
  */
2202
- async _paginateWith(data = {}, isNext = false, finalizer) {
2203
- if (!this.serverData.slug) throw new ApiError("slug de l'entité non défini");
2204
- if (!this.serverData.id) throw new ApiError("id de l'entité non défini");
2331
+ _getDefaultFromEndpoint(constant, path) {
2332
+ const endpoint = this.apiClient._endpoints.find((ep) => ep.constant === constant);
2333
+ if (!endpoint?.request?.properties?.[path]) return undefined;
2334
+ return endpoint.request.properties[path].default;
2335
+ }
2205
2336
 
2206
- const hasStep = (d) => d?.indexStep && d.indexStep > 0;
2207
2337
 
2208
- if (!this._paginationCursor || (!isNext && this._paginationHistory.length === 0)) {
2209
- this._paginationCount = 0;
2210
- this._paginationPageIndex = 0;
2211
- this._paginationHistory = [];
2212
- this._paginationPageSizes = [];
2213
- data.countType = data.searchType;
2214
-
2215
- if (!data?.searchBy && hasStep(data)) {
2216
- data.ranges = this._generateRanges(data.searchType, data.indexStep);
2217
- data.indexMin = data.indexMin ?? 0;
2218
- data.indexMax = data.indexMax ?? data.indexStep;
2219
- } else if (data?.searchBy === "ALL" && hasStep(data)) {
2220
- data.indexMin = data.indexMin ?? 0;
2221
- data.indexMax = data.indexMax ?? data.indexStep;
2222
- }
2338
+ /**
2339
+ * Recherche liée à l'entité, version stateless.
2340
+ *
2341
+ * @param {Object} data - Données initiales de recherche.
2342
+ * @returns {Object} - Un paginateur avec .next(), .prev(), etc.
2343
+ */
2344
+ async searchCostum(data = {}) {
2223
2345
 
2224
- this._paginationCursor = { ...data };
2225
- }
2346
+ const paginator = this._createPaginatorEngine({
2347
+ initialData: data,
2348
+ finalizer: this._withCostumContext((finalData) => this.endpointApi.globalAutocompleteCostum(finalData)),
2349
+ });
2226
2350
 
2227
- const cursor = this._paginationCursor;
2351
+ return paginator.next();
2352
+ }
2228
2353
 
2229
- if (isNext) {
2230
- this._paginationHistory.push({ ...cursor });
2354
+ /**
2355
+ * Coeur de pagination stateless et réutilisable, sans logique métier.
2356
+ *
2357
+ * @param {Object} config
2358
+ * @param {Object} config.initialData - Paramètres de départ
2359
+ * @param {Function} config.finalizer - Fonction async qui retourne { results, count }
2360
+ *
2361
+ * @returns {Object} paginator avec .next() et .prev()
2362
+ */
2363
+ _createPaginatorEngine({ initialData, finalizer }) {
2364
+ const Entity = this;
2231
2365
 
2232
- if (!cursor.searchBy && hasStep(cursor)) {
2233
- cursor.ranges = this._generateRanges(cursor.searchType, cursor.indexStep, cursor.ranges);
2234
- cursor.indexMin = cursor.indexMax ?? 0;
2235
- cursor.indexMax = (cursor.indexMax ?? 0) + cursor.indexStep;
2236
- } else if (cursor.searchBy === "ALL" && hasStep(cursor)) {
2237
- cursor.indexMin = cursor.indexMax ?? 0;
2238
- cursor.indexMax = (cursor.indexMax ?? 0) + cursor.indexStep;
2239
- }
2366
+ const state = {
2367
+ cursor: undefined,
2368
+ count: 0,
2369
+ index: 0,
2370
+ history: [],
2371
+ sizes: []
2372
+ };
2240
2373
 
2241
- this._paginationCursor = { ...cursor };
2242
- }
2374
+ const hasStep = (d) => d?.indexStep && d.indexStep > 0;
2243
2375
 
2244
- data = { ...this._paginationCursor };
2376
+ async function getPage(isNext = false) {
2377
+ let data = { ...initialData };
2245
2378
 
2246
- if (!isNext && (!data?.searchType || !Array.isArray(data.searchType) || data.searchType.length === 0)) {
2247
- throw new ApiError("searchType non défini");
2248
- }
2379
+ if (!state.cursor || (!isNext && state.history.length === 0)) {
2380
+ state.count = 0;
2381
+ state.index = 0;
2382
+ state.history = [];
2383
+ state.sizes = [];
2249
2384
 
2250
- const result = await finalizer(data);
2251
- if (!Array.isArray(result.results)) {
2252
- throw new ApiResponseError("Erreur lors de la récupération des résultats", 500, result.results);
2253
- }
2385
+ data.countType = data.searchType;
2254
2386
 
2255
- this._paginationCount += result.results.length;
2256
- this._paginationPageSizes.push(result.results.length);
2387
+ if (!data?.searchBy && hasStep(data)) {
2388
+ data.ranges = Entity._generateRanges(data.searchType, data.indexStep);
2389
+ data.indexMin = data.indexMin ?? 0;
2390
+ data.indexMax = data.indexMax ?? data.indexStep;
2391
+ } else if (data?.searchBy === "ALL" && hasStep(data)) {
2392
+ data.indexMin = data.indexMin ?? 0;
2393
+ data.indexMax = data.indexMax ?? data.indexStep;
2394
+ }
2257
2395
 
2258
- if (isNext) {
2259
- this._paginationPageIndex++;
2260
- }
2261
-
2262
- const count = this._normalizeCount(result.count);
2263
- const rawList = this._linkEntities(result.results);
2264
- const hasNext = hasStep(data) && this._paginationCount < count.total;
2265
- const hasPrev = this._paginationHistory?.length > 0;
2266
-
2267
- const response = {
2268
- count,
2269
- results: rawList,
2270
- pageIndex: this._paginationPageIndex,
2271
- pageNumber: this._paginationPageIndex + 1,
2272
- hasNext,
2273
- hasPrev
2274
- };
2396
+ state.cursor = { ...data };
2397
+ }
2275
2398
 
2276
- if (hasNext) {
2277
- response.next = async () => this._paginateWith({}, true, finalizer);
2278
- }
2399
+ const cursor = state.cursor;
2279
2400
 
2280
- if (this._paginationHistory?.length > 0) {
2281
- response.prev = async () => {
2282
- const previous = this._paginationHistory.pop();
2283
- const lastPageSize = this._paginationPageSizes.pop() ?? 0;
2284
- this._paginationCount -= lastPageSize;
2285
- this._paginationPageIndex = Math.max(0, this._paginationPageIndex - 1);
2286
- this._paginationCursor = { ...previous };
2287
- return this._paginateWith({}, false, finalizer);
2288
- };
2289
- }
2401
+ if (isNext) {
2402
+ state.history.push({ ...cursor });
2290
2403
 
2291
- return response;
2292
- }
2404
+ if (!cursor.searchBy && hasStep(cursor)) {
2405
+ cursor.ranges = Entity._generateRanges(cursor.searchType, cursor.indexStep, cursor.ranges);
2406
+ cursor.indexMin = cursor.indexMax ?? 0;
2407
+ cursor.indexMax = (cursor.indexMax ?? 0) + cursor.indexStep;
2408
+ } else if (cursor.searchBy === "ALL" && hasStep(cursor)) {
2409
+ cursor.indexMin = cursor.indexMax ?? 0;
2410
+ cursor.indexMax = (cursor.indexMax ?? 0) + cursor.indexStep;
2411
+ }
2293
2412
 
2413
+ state.cursor = { ...cursor };
2414
+ }
2294
2415
 
2295
- /**
2296
- * Réinitialise l'état de pagination.
2297
- */
2298
- resetPagination() {
2299
- this._paginationCursor = undefined;
2300
- this._paginationCount = 0;
2301
- this._paginationPageIndex = 0;
2302
- this._paginationHistory = [];
2303
- this._paginationPageSizes = [];
2304
- }
2416
+ data = { ...state.cursor };
2305
2417
 
2418
+ if (!isNext && (!data?.searchType || !Array.isArray(data.searchType) || data.searchType.length === 0)) {
2419
+ throw new Error("searchType non défini");
2420
+ }
2306
2421
 
2307
- /**
2308
- * Récupère une valeur par défaut depuis un endpoint donné.
2309
- *
2310
- * @param {string} constant - Le nom unique de l’endpoint (ex: "GET_ORGANIZATIONS_NO_ADMIN")
2311
- * @param {string} path - Le chemin vers la propriété (ex: "searchType")
2312
- * @returns {*} La valeur par défaut, ou undefined si non trouvée
2313
- */
2314
- _getDefaultFromEndpoint(constant, path) {
2315
- const endpoint = this.apiClient._endpoints.find((ep) => ep.constant === constant);
2316
- if (!endpoint?.request?.properties?.[path]) return undefined;
2317
- return endpoint.request.properties[path].default;
2422
+ const result = await finalizer(data);
2423
+ if (!Array.isArray(result.results)) {
2424
+ throw new Error("Les résultats doivent être un tableau");
2425
+ }
2426
+
2427
+ state.count += result.results.length;
2428
+ state.sizes.push(result.results.length);
2429
+ if (isNext) state.index++;
2430
+
2431
+ const count = Entity._normalizeCount(result.count);
2432
+ const rawList = Entity._linkEntities(result.results);
2433
+
2434
+ const hasNext = hasStep(data) && state.count < count.total;
2435
+ const hasPrev = state.history.length > 0;
2436
+
2437
+ return {
2438
+ count,
2439
+ results: rawList,
2440
+ pageIndex: state.index,
2441
+ pageNumber: state.index + 1,
2442
+ hasNext,
2443
+ hasPrev,
2444
+ next: hasNext ? () => getPage(true) : undefined,
2445
+ prev: hasPrev
2446
+ ? async () => {
2447
+ const previous = state.history.pop();
2448
+ const lastPageSize = state.sizes.pop() ?? 0;
2449
+ state.count -= lastPageSize;
2450
+ state.index = Math.max(0, state.index - 1);
2451
+ state.cursor = { ...previous };
2452
+ return getPage(false);
2453
+ }
2454
+ : undefined
2455
+ };
2456
+ }
2457
+
2458
+ return {
2459
+ next: () => getPage(false)
2460
+ };
2318
2461
  }
2319
2462
 
2320
2463
  /**
2321
- * recherche lié à l'entité.
2322
- *
2323
- * @param {Object} data - Les données de recherche.
2324
- * @param {boolean} isNext - Indique si c'est une recherche suivante (pagination).
2325
- * @returns {Promise<Object>} - Résultat de la recherche.
2326
- * @throws {ApiError} - Si le slug ou l'id de entité n'est pas défini.
2327
- */
2328
- async searchCostum(data = {}, isNext = false) {
2329
- return this._paginateWith(data, isNext, async (finalData) => {
2330
- finalData = {
2331
- ...finalData,
2464
+ * Injection de contexte Communecter dans une requête finalizer.
2465
+ *
2466
+ * @param {Function} baseFinalizer - fonction async(data) => { results, count }
2467
+ * @returns {Function} fonction enrichie
2468
+ */
2469
+ _withCostumContext(baseFinalizer) {
2470
+ return async (data) => {
2471
+ const finalData = {
2472
+ ...data,
2332
2473
  costumSlug: this.serverData.slug,
2333
2474
  contextId: this.serverData.id,
2334
- contextType: this.getEntityType()
2475
+ contextType: this.getEntityType(),
2476
+ sourceKey: [...(data.sourceKey || []), this.serverData.slug]
2335
2477
  };
2336
-
2337
- if (finalData.sourceKey?.length) {
2338
- finalData.sourceKey = [...finalData.sourceKey, this.serverData.slug];
2339
- } else {
2340
- finalData.sourceKey = [this.serverData.slug];
2341
- }
2342
-
2343
- return this.endpointApi.globalAutocompleteCostum(finalData);
2344
- });
2478
+ return baseFinalizer(finalData);
2479
+ };
2345
2480
  }
2346
2481
 
2347
2482