@communecter/cocolight-api-client 1.0.41 → 1.0.43

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.41",
3
+ "version": "1.0.43",
4
4
  "description": "Client Axios simplifié pour l'API cocolight",
5
5
  "repository": {
6
6
  "type": "git",
package/src/ApiClient.js CHANGED
@@ -102,6 +102,8 @@ export default class ApiClient extends EventEmitter {
102
102
  this._ajv = new Ajv({ strict: false, useDefaults: true, allErrors: true, verbose: true });
103
103
  addFormats(this._ajv);
104
104
 
105
+ this._ajv.addFormat("textarea", true);
106
+
105
107
  this._ajv.addKeyword({
106
108
  keyword: "startBeforeEnd",
107
109
  type: "object",
@@ -5,7 +5,7 @@ import EJSON from "ejson";
5
5
  import pkg from "file-type";
6
6
 
7
7
  import { ApiAuthenticationError, ApiError, ApiResponseError, ApiValidationError } from "../error.js";
8
- import { autoSyncDraftFromSchema, reactive } from "../utils/reactive.js";
8
+ import { isReactive, isSignal, reactive } from "../utils/reactive.js";
9
9
  const { fromBuffer } = pkg;
10
10
 
11
11
  /**
@@ -89,9 +89,9 @@ export class BaseEntity {
89
89
  throw new ApiError("deps.EndpointApi doit être une classe ou une instance valide.");
90
90
  }
91
91
 
92
- this._serverData = null;
92
+ this._serverData = reactive({});
93
93
 
94
- const { draft, proxy } = this._buildDraftAndProxy({
94
+ const { draft, proxy, initial } = this._buildDraftAndProxy({
95
95
  data: { ...data, ...this.defaultFields },
96
96
  serverData: this._serverData,
97
97
  constant: this.constructor.SCHEMA_CONSTANTS,
@@ -100,7 +100,7 @@ export class BaseEntity {
100
100
  removeFields: this.removeFields
101
101
  });
102
102
 
103
- this._initialDraftData = JSON.parse(JSON.stringify(draft));
103
+ this._initialDraftData = initial;
104
104
  this._draftData = draft;
105
105
  this.data = proxy;
106
106
  }
@@ -162,7 +162,7 @@ export class BaseEntity {
162
162
  * @returns {boolean}
163
163
  */
164
164
  hasChanges() {
165
- return JSON.stringify(this._draftData) !== JSON.stringify(this._initialDraftData);
165
+ return this._serialize(this._toRawDeep(this._draftData)) !== this._serialize(this._initialDraftData);
166
166
  }
167
167
 
168
168
  /**
@@ -186,6 +186,7 @@ export class BaseEntity {
186
186
 
187
187
  if (!this.id && typeof this._add === "function") {
188
188
  await this._add(payload);
189
+ this._resetInitialDraftData();
189
190
  // on refresh le contexte utilisateur si besoin
190
191
  if(this.userContext) {
191
192
  await this.userContext.refresh();
@@ -193,6 +194,7 @@ export class BaseEntity {
193
194
  return await this.refresh();
194
195
  } else if (typeof this._update === "function") {
195
196
  const hasChanged = await this._update(payload);
197
+ this._resetInitialDraftData();
196
198
  if (hasChanged) return await this.refresh();
197
199
  }
198
200
 
@@ -223,29 +225,94 @@ export class BaseEntity {
223
225
  * @returns {void}
224
226
  * @private
225
227
  */
226
- _setData(newData) {
228
+ _setData(newData, { forceInitialDraftReset = false } = {}) {
227
229
  if (this.userContext && this.userContext !== this) {
228
230
  this.apiClient._logger?.info?.(`[${this.__entityTag}] Mise à jour liée à userContext : ${this.userContext.id}`);
229
231
  }
230
- this._serverData = reactive({ ...newData });
231
232
 
232
- const { draft, proxy } = this._buildDraftAndProxy({
233
- data: { ...newData, ...this.defaultFields },
233
+ if (isReactive(this._serverData)) {
234
+ Object.assign(this._serverData, newData);
235
+ } else {
236
+ this._serverData = reactive({ ...newData });
237
+ }
238
+
239
+ const clientDraft = this._draftData ? this._toRawDeep(this._draftData) : {};
240
+
241
+ const mergedData = {
242
+ ...newData,
243
+ ...this.defaultFields,
244
+ ...clientDraft
245
+ };
246
+
247
+ const { draft, proxy, initial } = this._buildDraftAndProxy({
248
+ data: mergedData,
234
249
  serverData: this._serverData,
250
+ previousDraft: this._draftData,
235
251
  constant: this.constructor.SCHEMA_CONSTANTS,
236
252
  apiClient: this.apiClient,
237
253
  transforms: this.transforms,
238
254
  removeFields: this.removeFields
239
255
  });
240
- this._initialDraftData = JSON.parse(JSON.stringify(draft));
241
- this._draftData = draft;
242
- this.data = proxy;
243
256
 
244
- if (this._syncReactiveDraft) {
245
- this.setupReactiveSync();
257
+ if (forceInitialDraftReset) {
258
+ this._initialDraftData = structuredClone(this._toRawDeep(draft));
259
+ } else if (!this._initialDraftData) {
260
+ this._initialDraftData = initial;
246
261
  }
262
+
263
+ if (isReactive(this._draftData)) {
264
+ this._updateDraftPreservingUserChanges(draft);
265
+ } else {
266
+ this._draftData = reactive(draft);
267
+ }
268
+
269
+ if (!this.data) {
270
+ this.data = proxy;
271
+ }
272
+
247
273
  }
248
274
 
275
+ _updateDraftPreservingUserChanges(draft) {
276
+ for (const key of Object.keys(draft)) {
277
+ const current = this._draftData?.[key];
278
+ const initialValue = this._initialDraftData?.[key];
279
+
280
+ const isModified =
281
+ current !== undefined &&
282
+ initialValue !== undefined &&
283
+ this._serialize(this._toRawDeep(current)) !== this._serialize(initialValue);
284
+
285
+ if (!isModified) {
286
+ this._draftData[key] = draft[key];
287
+ }
288
+ }
289
+ }
290
+
291
+ _resetInitialDraftData() {
292
+ const raw = this._toRawDeep(this._draftData);
293
+ this._initialDraftData = structuredClone(raw);
294
+ }
295
+
296
+
297
+ _toRawDeep(obj) {
298
+ if (isSignal(obj)) {
299
+ return this._toRawDeep(obj.value);
300
+ }
301
+
302
+ if (Array.isArray(obj)) {
303
+ return obj.map(this._toRawDeep);
304
+ }
305
+
306
+ if (typeof obj === "object" && obj !== null) {
307
+ const result = {};
308
+ for (const key of Object.keys(obj)) {
309
+ result[key] = this._toRawDeep(obj[key]);
310
+ }
311
+ return result;
312
+ }
313
+
314
+ return obj; // valeur primitive
315
+ }
249
316
 
250
317
  /**
251
318
  * Champs à ajouter automatiquement à chaque draft (ex: `typeElement`).
@@ -768,16 +835,24 @@ export class BaseEntity {
768
835
  _createDraftProxy(apiClient, server = {}, draft = {}, allowedFields = [], transforms = {}, options = {}) {
769
836
  return new Proxy({}, {
770
837
  get: (_, prop) => {
771
- let val;
838
+ // Ne pas tenter d’accéder à des propriétés système
839
+ if (typeof prop !== "string" && typeof prop !== "symbol") return undefined;
840
+
841
+ // Lecture explicite — déclenche signal si draft est réactif
842
+ if (prop in draft) {
843
+ const value = draft[prop];
844
+ const transformer = transforms[prop];
845
+ return typeof transformer === "function" ? transformer(value) : value;
846
+ }
772
847
 
773
- try {
774
- val = prop in draft ? draft[prop] : (server && typeof server === "object" ? server[prop] : undefined);
775
- } catch {
776
- val = undefined;
848
+ // Fallback vers serverData — aussi potentiellement réactif
849
+ if (server && typeof server === "object" && prop in server) {
850
+ const value = server[prop];
851
+ const transformer = transforms[prop];
852
+ return typeof transformer === "function" ? transformer(value) : value;
777
853
  }
778
854
 
779
- const transformer = transforms[prop];
780
- return typeof transformer === "function" ? transformer(val) : val;
855
+ return undefined;
781
856
  },
782
857
 
783
858
  set: (_, prop, value) => {
@@ -793,7 +868,13 @@ export class BaseEntity {
793
868
  apiClient._logger.warn(message);
794
869
  return false;
795
870
  }
796
- draft[prop] = value;
871
+ const current = draft[prop];
872
+
873
+ if (current && typeof current === "object" && current.__isSignal === true) {
874
+ current.value = value; // ✅ met à jour le signal
875
+ } else {
876
+ draft[prop] = value; // fallback classique
877
+ }
797
878
  return true;
798
879
  },
799
880
 
@@ -804,8 +885,16 @@ export class BaseEntity {
804
885
  },
805
886
 
806
887
  has: (_, prop) => prop in draft || prop in server,
807
- ownKeys: () => [...new Set([...Object.keys(server), ...Object.keys(draft)])],
808
- getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true })
888
+
889
+ ownKeys: () => [...new Set([
890
+ ...Object.keys(server || {}),
891
+ ...Object.keys(draft || {})
892
+ ])],
893
+
894
+ getOwnPropertyDescriptor: () => ({
895
+ enumerable: true,
896
+ configurable: true
897
+ })
809
898
  });
810
899
  }
811
900
 
@@ -881,7 +970,7 @@ export class BaseEntity {
881
970
  * @returns {Object} - Objet contenant le brouillon et le proxy.
882
971
  * @private
883
972
  */
884
- _buildDraftAndProxy({ data = {}, serverData = null, constant, apiClient, transforms = {}, throwOnError = true, removeFields = [] }) {
973
+ _buildDraftAndProxy({ data = {}, serverData = null, previousDraft = null, constant, apiClient, transforms = {}, throwOnError = true, removeFields = [] }) {
885
974
  const constants = Array.isArray(constant) ? constant : [constant];
886
975
  const combinedSchema = {
887
976
  allOf: [],
@@ -905,7 +994,7 @@ export class BaseEntity {
905
994
 
906
995
  combinedSchema.allOf.push(sch);
907
996
  }
908
- const draft = {};
997
+
909
998
  let allowed = this._extractWritableFields(combinedSchema, data);
910
999
 
911
1000
  if (data.id && allowed.indexOf("id") === -1) {
@@ -917,15 +1006,32 @@ export class BaseEntity {
917
1006
 
918
1007
  allowed = allowed.filter(k => !removeFields.includes(k));
919
1008
 
920
- for (const key of allowed) {
921
- const raw = data[key];
922
- const transformed = typeof transforms[key] === "function" ? transforms[key](raw, data) : raw;
923
- if (transformed !== undefined) draft[key] = transformed;
924
- }
1009
+ // Transformation des champs autorisés
1010
+ const rawDraft = Object.fromEntries(
1011
+ allowed.map((key) => {
1012
+ const raw = data[key];
1013
+ const transformed = typeof transforms[key] === "function"
1014
+ ? transforms[key](raw, data)
1015
+ : raw;
1016
+ return [key, transformed];
1017
+ // eslint-disable-next-line no-unused-vars
1018
+ }).filter(([_, v]) => v !== undefined)
1019
+ );
1020
+
1021
+ const initial = structuredClone ? structuredClone(rawDraft) : JSON.parse(JSON.stringify(rawDraft));
925
1022
 
926
- const proxy = this._createDraftProxy(apiClient, serverData, draft, allowed, transforms, { throwOnError });
1023
+ const draft = isReactive(previousDraft)
1024
+ ? Object.assign(previousDraft, rawDraft)
1025
+ : reactive(rawDraft);
927
1026
 
928
- return { draft, proxy };
1027
+ // Assure que serverData est réactif si c'est un objet
1028
+ const reactiveServer = isReactive(serverData)
1029
+ ? serverData
1030
+ : (serverData && typeof serverData === "object" ? reactive(serverData) : serverData);
1031
+
1032
+ const proxy = this._createDraftProxy(apiClient, reactiveServer, draft, allowed, transforms, { throwOnError });
1033
+
1034
+ return { draft, proxy, initial};
929
1035
  }
930
1036
 
931
1037
  /**
@@ -969,25 +1075,6 @@ export class BaseEntity {
969
1075
  return Object.keys(changed).length > 0 ? changed : null;
970
1076
  }
971
1077
 
972
- /**
973
- * ───────────────────────────────
974
- * ReactiveMixin
975
- * ───────────────────────────────
976
- */
977
-
978
- /**
979
- * Active la synchronisation réactive entre le brouillon et le schéma.
980
- *
981
- * @returns {void}
982
- * @public
983
- */
984
- setupReactiveSync() {
985
- if (!this._syncReactiveDraft) {
986
- this._syncReactiveDraft = true;
987
- }
988
- autoSyncDraftFromSchema(this);
989
- }
990
-
991
1078
  /**
992
1079
  * ───────────────────────────────
993
1080
  * MutualEntityMixin
@@ -1601,7 +1688,7 @@ export class BaseEntity {
1601
1688
  async get() {
1602
1689
  return this.apiClient.safeCall(async () => {
1603
1690
  const data = await this._getPublicProfile();
1604
- this._setData(data);
1691
+ this._setData(data, { forceInitialDraftReset: true });
1605
1692
  return data;
1606
1693
  });
1607
1694
  }
@@ -3025,7 +3025,7 @@ export interface AddEventData {
3025
3025
  };
3026
3026
  };
3027
3027
  /**
3028
- * Nom du poi
3028
+ * Nom de l'événement
3029
3029
  */
3030
3030
  name: string;
3031
3031
  /**