@communecter/cocolight-api-client 1.0.60 → 1.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@communecter/cocolight-api-client",
3
- "version": "1.0.60",
3
+ "version": "1.0.61",
4
4
  "description": "Client Axios simplifié pour l'API cocolight",
5
5
  "repository": {
6
6
  "type": "git",
@@ -4,6 +4,7 @@ import objectId from "bson-objectid";
4
4
  import * as pkg from "file-type";
5
5
 
6
6
  import { ApiAuthenticationError, ApiError, ApiResponseError, ApiValidationError } from "../error.js";
7
+ import { fromEntityJSON } from "./EntityRegistry.js";
7
8
  import { EJSON } from "../utils/compat.js";
8
9
  import { isReactive, isSignal, reactive } from "../utils/reactive.js";
9
10
 
@@ -734,19 +735,8 @@ export class BaseEntity<TServerData = any> {
734
735
  private _removeUnserializables(obj: any, seen = new WeakSet<object>()): any {
735
736
  if (obj === null || typeof obj !== "object") return obj;
736
737
 
737
- if (seen.has(obj)) return null;
738
- seen.add(obj);
739
-
740
- // Ignore les proxys réactifs
741
- if (obj.__isReactive && typeof obj.__raw === "object") {
742
- return this._removeUnserializables(obj.__raw, seen);
743
- }
744
-
745
- if (Array.isArray(obj)) {
746
- return obj.map((el) => this._removeUnserializables(el, seen));
747
- }
748
-
749
- // Preserve native types that EJSON handles natively
738
+ // Check for native types BEFORE adding to WeakSet and checking for reactive proxies
739
+ // This prevents Date objects from being processed as regular objects
750
740
  if (obj instanceof Date) {
751
741
  return obj;
752
742
  }
@@ -765,6 +755,25 @@ export class BaseEntity<TServerData = any> {
765
755
  return obj;
766
756
  }
767
757
 
758
+ // If the object is a BaseEntity instance, serialize it via toJSON()
759
+ // This ensures nested entities (like author, target in News) are properly serialized
760
+ if (obj instanceof BaseEntity) {
761
+ return obj.toJSON();
762
+ }
763
+
764
+ // Now check for circular references
765
+ if (seen.has(obj)) return null;
766
+ seen.add(obj);
767
+
768
+ // Ignore les proxys réactifs
769
+ if (obj.__isReactive && typeof obj.__raw === "object") {
770
+ return this._removeUnserializables(obj.__raw, seen);
771
+ }
772
+
773
+ if (Array.isArray(obj)) {
774
+ return obj.map((el) => this._removeUnserializables(el, seen));
775
+ }
776
+
768
777
  const clean: Record<string, any> = {};
769
778
  for (const key of Object.keys(obj)) {
770
779
  const val = obj[key];
@@ -790,12 +799,57 @@ export class BaseEntity<TServerData = any> {
790
799
 
791
800
  /**
792
801
  * Restaure les données sérialisées en un objet d'origine.
802
+ * Désérialise récursivement les entités imbriquées qui ont le marqueur __isSerializedEntity.
793
803
  *
794
804
  * @param obj - L'objet à restaurer.
805
+ * @param parent - Instance parente optionnelle pour désérialiser les entités imbriquées.
795
806
  * @returns L'objet restauré.
796
807
  */
797
- static _revive(obj: object): any {
798
- return EJSON.fromJSONValue(obj);
808
+ static _revive(obj: object, parent?: ApiClient | BaseEntity<any> | null): any {
809
+ // D'abord, restaurer les dates EJSON
810
+ const revived = EJSON.fromJSONValue(obj);
811
+
812
+ // Ensuite, parcourir récursivement pour désérialiser les entités imbriquées
813
+ return this._deserializeNestedEntities(revived, parent);
814
+ }
815
+
816
+ /**
817
+ * Parcourt récursivement un objet et désérialise les entités imbriquées.
818
+ *
819
+ * @param obj - L'objet à parcourir.
820
+ * @param parent - Instance parente pour les entités.
821
+ * @returns L'objet avec les entités désérialisées.
822
+ * @private
823
+ */
824
+ private static _deserializeNestedEntities(obj: any, parent?: ApiClient | BaseEntity<any> | null): any {
825
+ if (obj === null || typeof obj !== "object") {
826
+ return obj;
827
+ }
828
+
829
+ // Si c'est un objet Date, RegExp, ou autre type natif, le retourner tel quel
830
+ if (obj instanceof Date || obj instanceof RegExp) {
831
+ return obj;
832
+ }
833
+
834
+ // Si c'est un array, parcourir chaque élément
835
+ if (Array.isArray(obj)) {
836
+ return obj.map(item => this._deserializeNestedEntities(item, parent));
837
+ }
838
+
839
+ // Si c'est une entité sérialisée, la désérialiser
840
+ // On vérifie les marqueurs d'une entité sérialisée
841
+ if (obj.__isSerializedEntity && obj.__entityTag && obj.serverData) {
842
+ // Désérialiser l'entité imbriquée via EntityRegistry
843
+ return fromEntityJSON(obj, parent);
844
+ }
845
+
846
+ // Sinon, parcourir les propriétés de l'objet et désérialiser récursivement
847
+ const result: any = {};
848
+ for (const [key, value] of Object.entries(obj)) {
849
+ result[key] = this._deserializeNestedEntities(value, parent);
850
+ }
851
+
852
+ return result;
799
853
  }
800
854
 
801
855
  /**
@@ -816,7 +870,7 @@ export class BaseEntity<TServerData = any> {
816
870
  }
817
871
  const { serverData } = json as { serverData: object };
818
872
 
819
- const instance = this.fromServerData(this._revive(serverData), parent as ApiClient | ParentLike, deps || {});
873
+ const instance = this.fromServerData(this._revive(serverData, parent), parent as ApiClient | ParentLike, deps || {});
820
874
 
821
875
  return instance;
822
876
  }
@@ -57,10 +57,18 @@ export function fromEntityJSON(json: unknown, parent: ApiClient | BaseEntity | n
57
57
  // On gère null / non-objet / pas une entité → on renvoie tel quel (ou null si tu préfères)
58
58
  if (!isEntityJSON(json)) return json;
59
59
 
60
- // Ici, TS sait que json.__entityTag existe
61
- if (!json.serverData?.collection) return json;
60
+ // Récupérer la collection depuis serverData, ou la dériver depuis __entityTag
61
+ let collection: CollectionKey | null | undefined = json.serverData?.collection;
62
62
 
63
- const meta = _getEntityMeta(json.serverData.collection, json.__entityTag);
63
+ // Si collection est absente, essayer de la dériver depuis __entityTag
64
+ if (!collection) {
65
+ collection = _getCollectionFromTag(json.__entityTag);
66
+ }
67
+
68
+ // Si on n'a toujours pas de collection, on ne peut pas désérialiser
69
+ if (!collection) return json;
70
+
71
+ const meta = _getEntityMeta(collection, json.__entityTag);
64
72
  if (!meta) return json;
65
73
 
66
74
  if (!meta.entityClass?.fromJSON) {
@@ -69,6 +77,24 @@ export function fromEntityJSON(json: unknown, parent: ApiClient | BaseEntity | n
69
77
  return meta.entityClass.fromJSON(json, parent, meta.deps);
70
78
  }
71
79
 
80
+ /**
81
+ * Mappe un EntityTag vers son CollectionKey correspondant.
82
+ */
83
+ function _getCollectionFromTag(tag: EntityTag): CollectionKey | null {
84
+ const map: Record<EntityTag, CollectionKey> = {
85
+ "User": "citoyens",
86
+ "Organization": "organizations",
87
+ "Project": "projects",
88
+ "Event": "events",
89
+ "Poi": "poi",
90
+ "News": "news",
91
+ "Badge": "badges",
92
+ "Comment": "comments",
93
+ "Answer": "answers"
94
+ };
95
+ return map[tag] || null;
96
+ }
97
+
72
98
  /**
73
99
  * Récupère la classe d'entité et ses dépendances à partir du type d'entité.
74
100
  */
@@ -328,11 +328,22 @@ export declare class BaseEntity<TServerData = any> {
328
328
  private _removeUnserializables;
329
329
  /**
330
330
  * Restaure les données sérialisées en un objet d'origine.
331
+ * Désérialise récursivement les entités imbriquées qui ont le marqueur __isSerializedEntity.
331
332
  *
332
333
  * @param obj - L'objet à restaurer.
334
+ * @param parent - Instance parente optionnelle pour désérialiser les entités imbriquées.
333
335
  * @returns L'objet restauré.
334
336
  */
335
- static _revive(obj: object): any;
337
+ static _revive(obj: object, parent?: ApiClient | BaseEntity<any> | null): any;
338
+ /**
339
+ * Parcourt récursivement un objet et désérialise les entités imbriquées.
340
+ *
341
+ * @param obj - L'objet à parcourir.
342
+ * @param parent - Instance parente pour les entités.
343
+ * @returns L'objet avec les entités désérialisées.
344
+ * @private
345
+ */
346
+ private static _deserializeNestedEntities;
336
347
  /**
337
348
  * Crée une instance d'entité à partir de données JSON.
338
349
  *