@legendapp/state 3.0.0-beta.46 → 3.0.0-beta.48

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.
@@ -7,6 +7,11 @@ var { waitForSet, runWithRetry } = internal$1;
7
7
  function transformOut(data, transform) {
8
8
  return transform ? transform(clone(data)) : data;
9
9
  }
10
+ function warnMissingId(value) {
11
+ if (process.env.NODE_ENV === "development") {
12
+ console.warn("[legend-state] syncedCrud received an item without an id", value);
13
+ }
14
+ }
10
15
  function ensureId(obj, fieldId, generateId, node) {
11
16
  if (!obj[fieldId]) {
12
17
  obj[fieldId] = generateId();
@@ -41,40 +46,89 @@ function arrayToRecord(arr, keyField) {
41
46
  for (let i = 0; i < arr.length; i++) {
42
47
  const v = arr[i];
43
48
  const key = v[keyField];
44
- record[key] = v;
49
+ if (isNullOrUndefined(key)) {
50
+ warnMissingId(v);
51
+ } else {
52
+ record[key] = v;
53
+ }
45
54
  }
46
55
  }
47
56
  return record;
48
57
  }
49
- function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetries, itemValueFull, actionFn, saveResult) {
50
- if (action === "delete") {
51
- if (queuedRetries.create.has(itemKey)) {
52
- queuedRetries.create.delete(itemKey);
53
- }
54
- if (queuedRetries.update.has(itemKey)) {
55
- queuedRetries.update.delete(itemKey);
56
- }
57
- } else {
58
- if (queuedRetries.delete.has(itemKey)) {
59
- queuedRetries.delete.delete(itemKey);
60
- }
58
+ function mergeQueuedRetryValue(currentValue, nextValue) {
59
+ if (currentValue && nextValue && typeof currentValue === "object" && typeof nextValue === "object") {
60
+ Object.assign(currentValue, nextValue);
61
+ return currentValue;
62
+ }
63
+ return nextValue;
64
+ }
65
+ function queueRetryValue(queuedRetries, itemKey, itemValue, itemValueFull) {
66
+ const queuedRetry = queuedRetries.get(itemKey);
67
+ if (queuedRetry) {
68
+ queuedRetry.value = mergeQueuedRetryValue(queuedRetry.value, itemValue);
69
+ queuedRetry.fullValue = mergeQueuedRetryValue(queuedRetry.fullValue, itemValueFull);
70
+ return queuedRetry;
61
71
  }
62
- const queuedRetry = queuedRetries[action].get(itemKey);
72
+ const nextQueuedRetry = {
73
+ value: itemValue,
74
+ fullValue: itemValueFull
75
+ };
76
+ queuedRetries.set(itemKey, nextQueuedRetry);
77
+ return nextQueuedRetry;
78
+ }
79
+ function cancelQueuedRetry(queuedRetries, itemKey) {
80
+ const queuedRetry = queuedRetries.get(itemKey);
63
81
  if (queuedRetry) {
64
- itemValue = Object.assign(queuedRetry, itemValue);
82
+ queuedRetry.cancelled = true;
83
+ queuedRetries.delete(itemKey);
84
+ }
85
+ }
86
+ function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetries, itemValueFull, actionFn, saveResult, options) {
87
+ if (action === "delete") {
88
+ cancelQueuedRetry(queuedRetries.create, itemKey);
89
+ cancelQueuedRetry(queuedRetries.update, itemKey);
90
+ } else {
91
+ cancelQueuedRetry(queuedRetries.delete, itemKey);
65
92
  }
66
- queuedRetries[action].set(itemKey, itemValue);
67
- const clonedValue = clone(itemValueFull);
93
+ const queuedRetry = queueRetryValue(queuedRetries[action], itemKey, itemValue, itemValueFull);
68
94
  const paramsWithChanges = { ...params, changes: [change] };
69
- return runWithRetry(
70
- paramsWithChanges,
71
- retry,
72
- "create_" + itemKey,
73
- () => actionFn(itemValue, paramsWithChanges).then((result) => {
74
- queuedRetries[action].delete(itemKey);
75
- return saveResult(itemKey, clonedValue, result, true, change);
76
- })
77
- );
95
+ const runAttempt = () => {
96
+ var _a;
97
+ if (queuedRetry.cancelled) {
98
+ return Promise.resolve(void 0);
99
+ }
100
+ const refreshed = (_a = options == null ? void 0 : options.refreshValue) == null ? void 0 : _a.call(options);
101
+ const runWithRefreshedValue = (retryValue) => {
102
+ if (retryValue === false || queuedRetry.cancelled) {
103
+ queuedRetry.cancelled = true;
104
+ queuedRetries[action].delete(itemKey);
105
+ return Promise.resolve(void 0);
106
+ }
107
+ if (retryValue) {
108
+ queuedRetry.value = retryValue.value;
109
+ queuedRetry.fullValue = retryValue.fullValue;
110
+ }
111
+ const attemptValue = queuedRetry.value;
112
+ const attemptFullValue = clone(queuedRetry.fullValue);
113
+ return actionFn(attemptValue, paramsWithChanges).catch((error) => {
114
+ var _a2;
115
+ (_a2 = options == null ? void 0 : options.onAttemptFailure) == null ? void 0 : _a2.call(options);
116
+ throw error;
117
+ }).then(async (result) => {
118
+ queuedRetries[action].delete(itemKey);
119
+ if (queuedRetry.cancelled) {
120
+ return result;
121
+ }
122
+ await saveResult(itemKey, attemptFullValue, result, true, change);
123
+ return result;
124
+ });
125
+ };
126
+ return isPromise(refreshed) ? refreshed.then(runWithRefreshedValue) : runWithRefreshedValue(refreshed);
127
+ };
128
+ return runWithRetry(paramsWithChanges, retry, action + "_" + itemKey, runAttempt).catch((error) => {
129
+ params.update({ value: {}, failedChanges: [change] });
130
+ throw error;
131
+ });
78
132
  }
79
133
  function syncedCrud(props) {
80
134
  const {
@@ -100,12 +154,53 @@ function syncedCrud(props) {
100
154
  ...rest
101
155
  } = props;
102
156
  const fieldId = fieldIdProp || "id";
103
- const pendingCreates = /* @__PURE__ */ new Set();
157
+ const pendingCreates = /* @__PURE__ */ new Map();
104
158
  const queuedRetries = {
105
159
  create: /* @__PURE__ */ new Map(),
106
160
  update: /* @__PURE__ */ new Map(),
107
161
  delete: /* @__PURE__ */ new Map()
108
162
  };
163
+ const beginPendingCreate = (id, change) => {
164
+ const pendingCreate = pendingCreates.get(id) || { hasFailed: false };
165
+ pendingCreate.hasFailed = false;
166
+ pendingCreate.change || (pendingCreate.change = change);
167
+ pendingCreates.set(id, pendingCreate);
168
+ return pendingCreate;
169
+ };
170
+ const clearPendingCreate = (id) => {
171
+ if (!isNullOrUndefined(id)) {
172
+ pendingCreates.delete(id);
173
+ cancelQueuedRetry(queuedRetries.create, id);
174
+ }
175
+ };
176
+ const cancelFailedPendingCreate = (id) => {
177
+ var _a;
178
+ if (!isNullOrUndefined(id) && ((_a = pendingCreates.get(id)) == null ? void 0 : _a.hasFailed)) {
179
+ clearPendingCreate(id);
180
+ return true;
181
+ }
182
+ return false;
183
+ };
184
+ const clearPendingCreateForValue = (value) => {
185
+ if (value && typeof value === "object") {
186
+ clearPendingCreate(value[fieldId]);
187
+ }
188
+ };
189
+ const clearPendingCreatesForValues = (values) => {
190
+ if (values == null ? void 0 : values.length) {
191
+ for (let i = 0; i < values.length; i++) {
192
+ clearPendingCreateForValue(values[i]);
193
+ }
194
+ }
195
+ };
196
+ const markPendingCreateFailed = (itemKey) => {
197
+ const pendingCreate = pendingCreates.get(itemKey);
198
+ if (pendingCreate && !pendingCreate.hasFailed) {
199
+ pendingCreate.hasFailed = true;
200
+ cancelQueuedRetry(queuedRetries.update, itemKey);
201
+ cancelQueuedRetry(queuedRetries.delete, itemKey);
202
+ }
203
+ };
109
204
  let asType = props.as;
110
205
  if (!asType) {
111
206
  asType = getFn ? "value" : "object";
@@ -125,9 +220,19 @@ function syncedCrud(props) {
125
220
  if (asArray) {
126
221
  out.push(result);
127
222
  } else if (asMap) {
128
- out.set(value[fieldId], result);
223
+ const key = value[fieldId];
224
+ if (isNullOrUndefined(key)) {
225
+ warnMissingId(value);
226
+ } else {
227
+ out.set(key, result);
228
+ }
129
229
  } else {
130
- out[value[fieldId]] = result;
230
+ const key = value[fieldId];
231
+ if (isNullOrUndefined(key)) {
232
+ warnMissingId(value);
233
+ } else {
234
+ out[key] = result;
235
+ }
131
236
  }
132
237
  }
133
238
  }
@@ -165,6 +270,7 @@ function syncedCrud(props) {
165
270
  };
166
271
  const processResults = (data) => {
167
272
  data || (data = []);
273
+ clearPendingCreatesForValues(data);
168
274
  if (fieldUpdatedAt) {
169
275
  const newLastSync = computeLastSync(data, fieldUpdatedAt, fieldCreatedAt);
170
276
  if (newLastSync && newLastSync !== lastSync) {
@@ -181,6 +287,7 @@ function syncedCrud(props) {
181
287
  } else if (getFn) {
182
288
  const dataPromise = getFn(getParams);
183
289
  const processData = (data) => {
290
+ clearPendingCreateForValue(data);
184
291
  let transformed = data;
185
292
  if (data) {
186
293
  const newLastSync = data[fieldUpdatedAt] || data[fieldCreatedAt];
@@ -215,6 +322,32 @@ function syncedCrud(props) {
215
322
  !isNullOrUndefined(itemValue[fieldId]) ? { [fieldId]: itemValue[fieldId] } : {}
216
323
  ) : itemValue;
217
324
  };
325
+ const getCurrentValueAtKey = (itemKey) => {
326
+ const currentPeeked = getNodeValue(node);
327
+ if (asType === "value") {
328
+ return currentPeeked;
329
+ }
330
+ if (asType === "array") {
331
+ return isArray(currentPeeked) ? currentPeeked.find((value2) => (value2 == null ? void 0 : value2[fieldId]) === itemKey) : void 0;
332
+ }
333
+ if (asType === "Map") {
334
+ return currentPeeked == null ? void 0 : currentPeeked.get(itemKey);
335
+ }
336
+ return currentPeeked == null ? void 0 : currentPeeked[itemKey];
337
+ };
338
+ const isFailedCreateReadyToRetry = (itemKey) => {
339
+ const pendingCreate = pendingCreates.get(itemKey);
340
+ return !!(pendingCreate == null ? void 0 : pendingCreate.hasFailed) && !pendingCreate.promise;
341
+ };
342
+ const addCreate = (id, itemValue, change) => {
343
+ const existingPendingCreate = pendingCreates.get(id);
344
+ if ((existingPendingCreate == null ? void 0 : existingPendingCreate.hasFailed) && !existingPendingCreate.promise && change.path.length === 0) {
345
+ existingPendingCreate.change = change;
346
+ }
347
+ const pendingCreate = beginPendingCreate(id, change);
348
+ changesById.set(id, pendingCreate.change);
349
+ creates.set(id, itemValue);
350
+ };
218
351
  changes.forEach((change) => {
219
352
  var _a, _b;
220
353
  const { path, prevAtPath, valueAtPath, pathTypes } = change;
@@ -227,12 +360,14 @@ function syncedCrud(props) {
227
360
  const id = value[fieldId];
228
361
  if (!isNullOrUndefined(id)) {
229
362
  changesById.set(id, change);
230
- if (pendingCreates.has(id)) {
363
+ if (isFailedCreateReadyToRetry(id)) {
364
+ isCreate = true;
365
+ } else if (pendingCreates.has(id)) {
231
366
  isCreate = false;
232
367
  }
233
368
  if (isCreate || retryAsCreate) {
234
369
  if (createFn) {
235
- creates.set(id, value);
370
+ addCreate(id, value, change);
236
371
  } else {
237
372
  console.warn("[legend-state] missing create function");
238
373
  }
@@ -257,8 +392,11 @@ function syncedCrud(props) {
257
392
  console.error("[legend-state]: added synced item without an id");
258
393
  }
259
394
  } else if (path.length === 0) {
260
- deletes.add(prevAtPath);
261
- changesById.set(prevAtPath[fieldId], change);
395
+ const id = prevAtPath == null ? void 0 : prevAtPath[fieldId];
396
+ if (!cancelFailedPendingCreate(id)) {
397
+ deletes.add(prevAtPath);
398
+ changesById.set(prevAtPath[fieldId], change);
399
+ }
262
400
  }
263
401
  } else {
264
402
  let itemsChanged = [];
@@ -281,7 +419,11 @@ function syncedCrud(props) {
281
419
  for (let i = 0; i < lengthPrev; i++) {
282
420
  const key = keysPrev[i];
283
421
  if (!keysSet.has(key)) {
284
- deletes.add(prevAsObject[key]);
422
+ const prevValue = prevAsObject[key];
423
+ const id = prevValue == null ? void 0 : prevValue[fieldId];
424
+ if (!cancelFailedPendingCreate(id)) {
425
+ deletes.add(prevValue);
426
+ }
285
427
  }
286
428
  }
287
429
  for (let i = 0; i < length; i++) {
@@ -293,7 +435,8 @@ function syncedCrud(props) {
293
435
  return false;
294
436
  } else {
295
437
  const isDiff = !prevAsObject || !deepEqual(value2, prev);
296
- if (isDiff) {
438
+ const isFailedCreateRetry = isFailedCreateReadyToRetry(value2 == null ? void 0 : value2[fieldId]);
439
+ if (isDiff || isFailedCreateRetry) {
297
440
  itemsChanged.push([getUpdateValue(value2, prev), prev, value2]);
298
441
  }
299
442
  }
@@ -303,8 +446,11 @@ function syncedCrud(props) {
303
446
  const itemValue = asMap ? value.get(itemKey) : value[itemKey];
304
447
  if (!itemValue) {
305
448
  if (path.length === 1 && prevAtPath) {
306
- deletes.add(prevAtPath);
307
- changesById.set(prevAtPath[fieldId], change);
449
+ const id = prevAtPath[fieldId];
450
+ if (!cancelFailedPendingCreate(id)) {
451
+ deletes.add(prevAtPath);
452
+ changesById.set(id, change);
453
+ }
308
454
  }
309
455
  } else {
310
456
  if (generateId) {
@@ -320,27 +466,26 @@ function syncedCrud(props) {
320
466
  }
321
467
  }
322
468
  itemsChanged == null ? void 0 : itemsChanged.forEach(([item, prev, fullValue]) => {
323
- const isCreate = !pendingCreates.has(item[fieldId]) && (fieldCreatedAt ? !item[fieldCreatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : fieldUpdatedAt ? !item[fieldUpdatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : isNullOrUndefined(prev));
469
+ const id = item[fieldId];
470
+ const isFailedCreateRetry = isFailedCreateReadyToRetry(id);
471
+ const isCreate = isFailedCreateRetry || !pendingCreates.has(id) && (fieldCreatedAt ? !item[fieldCreatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : fieldUpdatedAt ? !item[fieldUpdatedAt] && !(prev == null ? void 0 : prev[fieldCreatedAt]) : isNullOrUndefined(prev));
324
472
  if (isCreate) {
325
- if (!item[fieldId]) {
473
+ if (!id) {
326
474
  console.error("[legend-state]: added item without an id");
327
475
  }
328
476
  if (createFn) {
329
- const id = item[fieldId];
330
- changesById.set(id, change);
331
- pendingCreates.add(id);
332
- creates.set(id, item);
477
+ addCreate(id, isFailedCreateRetry ? fullValue : item, change);
333
478
  } else {
334
479
  console.warn("[legend-state] missing create function");
335
480
  }
336
481
  } else {
337
482
  if (updateFn) {
338
- const id = item[fieldId];
339
- changesById.set(id, change);
340
- updates.set(id, updates.has(id) ? Object.assign(updates.get(id), item) : item);
483
+ const id2 = item[fieldId];
484
+ changesById.set(id2, change);
485
+ updates.set(id2, updates.has(id2) ? Object.assign(updates.get(id2), item) : item);
341
486
  updateFullValues.set(
342
- id,
343
- updateFullValues.has(id) ? Object.assign(updateFullValues.get(id), fullValue) : fullValue
487
+ id2,
488
+ updateFullValues.has(id2) ? Object.assign(updateFullValues.get(id2), fullValue) : fullValue
344
489
  );
345
490
  } else {
346
491
  console.warn("[legend-state] missing update function");
@@ -411,7 +556,28 @@ function syncedCrud(props) {
411
556
  await waitForSet(waitForSetParam, changes, itemValue, { type: "create" });
412
557
  }
413
558
  const createObj = await transformOut(itemValue, transform == null ? void 0 : transform.save);
414
- return retrySet(
559
+ const pendingCreate = pendingCreates.get(itemKey) || { hasFailed: false };
560
+ pendingCreates.set(itemKey, pendingCreate);
561
+ const refreshCreateValue = async () => {
562
+ const currentPendingCreate = pendingCreates.get(itemKey);
563
+ if (!currentPendingCreate) {
564
+ return false;
565
+ }
566
+ if (!currentPendingCreate.hasFailed) {
567
+ return void 0;
568
+ }
569
+ const currentValue = getCurrentValueAtKey(itemKey);
570
+ if (isNullOrUndefined(currentValue)) {
571
+ clearPendingCreate(itemKey);
572
+ return false;
573
+ }
574
+ const nextCreateObj = await transformOut(
575
+ clone(currentValue),
576
+ transform == null ? void 0 : transform.save
577
+ );
578
+ return { value: nextCreateObj, fullValue: nextCreateObj };
579
+ };
580
+ const createPromise = retrySet(
415
581
  params,
416
582
  retry,
417
583
  "create",
@@ -421,13 +587,31 @@ function syncedCrud(props) {
421
587
  queuedRetries,
422
588
  createObj,
423
589
  createFn,
424
- saveResult
425
- ).then(() => {
426
- pendingCreates.delete(itemKey);
590
+ saveResult,
591
+ {
592
+ onAttemptFailure: () => markPendingCreateFailed(itemKey),
593
+ refreshValue: refreshCreateValue
594
+ }
595
+ );
596
+ pendingCreate.promise = createPromise;
597
+ return createPromise.then((result) => {
598
+ if (pendingCreates.get(itemKey) === pendingCreate) {
599
+ pendingCreates.delete(itemKey);
600
+ }
601
+ return result;
602
+ }).catch((error) => {
603
+ if (pendingCreates.get(itemKey) === pendingCreate) {
604
+ pendingCreate.promise = void 0;
605
+ }
606
+ throw error;
427
607
  });
428
608
  }),
429
609
  // Handle updates
430
610
  ...Array.from(updates).map(async ([itemKey, itemValue]) => {
611
+ const pendingCreate = pendingCreates.get(itemKey);
612
+ if ((pendingCreate == null ? void 0 : pendingCreate.hasFailed) && pendingCreate.promise) {
613
+ await pendingCreate.promise;
614
+ }
431
615
  const fullValue = updateFullValues.get(itemKey);
432
616
  if (waitForSetParam) {
433
617
  await waitForSet(waitForSetParam, changes, fullValue, { type: "update" });
@@ -510,6 +694,7 @@ function syncedCrud(props) {
510
694
  paramsForUpdate.lastSync = newLastSync;
511
695
  }
512
696
  const rowsTransformed = (transform == null ? void 0 : transform.load) ? await transformRows(rows) : rows;
697
+ clearPendingCreatesForValues(rows);
513
698
  paramsForUpdate.value = resultsToOutType(rowsTransformed);
514
699
  params.update(paramsForUpdate);
515
700
  }
package/sync.js CHANGED
@@ -249,6 +249,12 @@ function createRevertChanges(obs$, changes) {
249
249
  };
250
250
  }
251
251
 
252
+ // src/onChange.ts
253
+ function markNodeAsSynced(node) {
254
+ const activationState = node.activationState || (node.activationState = {});
255
+ activationState.synced = true;
256
+ }
257
+
252
258
  // src/sync/syncObservable.ts
253
259
  var {
254
260
  clone: clone2,
@@ -715,11 +721,12 @@ async function doChangeRemote(changeInfo) {
715
721
  onError: onSetError,
716
722
  update: (params) => {
717
723
  if (updateResult) {
718
- const { value: value2, mode, changes } = params;
724
+ const { value: value2, mode, changes, failedChanges } = params;
719
725
  updateResult = {
720
726
  value: deepMerge(updateResult.value, value2),
721
727
  mode,
722
- changes: changes ? [...updateResult.changes || [], ...changes] : updateResult.changes
728
+ changes: changes ? [...updateResult.changes || [], ...changes] : updateResult.changes,
729
+ failedChanges: failedChanges ? [...updateResult.failedChanges || [], ...failedChanges] : updateResult.failedChanges
723
730
  };
724
731
  } else {
725
732
  updateResult = params;
@@ -742,7 +749,11 @@ async function doChangeRemote(changeInfo) {
742
749
  });
743
750
  }
744
751
  if (!didError || (updateResult == null ? void 0 : updateResult.changes)) {
745
- const { value: updateValue, changes: updateChanges = changesRemote } = updateResult || {};
752
+ const {
753
+ value: updateValue,
754
+ changes: updateChanges = changesRemote,
755
+ failedChanges = []
756
+ } = updateResult || {};
746
757
  const changesWithPath = updateChanges;
747
758
  const pathStrs = Array.from(new Set(changesWithPath.map((change) => change.pathStr)));
748
759
  if (pathStrs.length > 0) {
@@ -750,6 +761,7 @@ async function doChangeRemote(changeInfo) {
750
761
  const metadata = {};
751
762
  const pending = localState.pendingChanges;
752
763
  const pendingToKeep = /* @__PURE__ */ new Set();
764
+ const failedPathStrs = new Set(failedChanges.map((change) => change.pathStr));
753
765
  if (pending) {
754
766
  const changesByPath = /* @__PURE__ */ new Map();
755
767
  for (let i = 0; i < changesWithPath.length; i++) {
@@ -762,7 +774,7 @@ async function doChangeRemote(changeInfo) {
762
774
  continue;
763
775
  }
764
776
  const change = changesByPath.get(key);
765
- if (!change || !deepEqual(pendingEntry.v, change.valueAtPath)) {
777
+ if (failedPathStrs.has(key) || !change || !deepEqual(pendingEntry.v, change.valueAtPath)) {
766
778
  pendingToKeep.add(key);
767
779
  }
768
780
  }
@@ -996,6 +1008,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
996
1008
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && (!obs$ || !node)) {
997
1009
  throw new Error("[legend-state] syncObservable called with undefined observable");
998
1010
  }
1011
+ markNodeAsSynced(node);
999
1012
  syncOptions = deepMerge(
1000
1013
  {
1001
1014
  syncMode: "auto"
package/sync.mjs CHANGED
@@ -247,6 +247,12 @@ function createRevertChanges(obs$, changes) {
247
247
  };
248
248
  }
249
249
 
250
+ // src/onChange.ts
251
+ function markNodeAsSynced(node) {
252
+ const activationState = node.activationState || (node.activationState = {});
253
+ activationState.synced = true;
254
+ }
255
+
250
256
  // src/sync/syncObservable.ts
251
257
  var {
252
258
  clone: clone2,
@@ -713,11 +719,12 @@ async function doChangeRemote(changeInfo) {
713
719
  onError: onSetError,
714
720
  update: (params) => {
715
721
  if (updateResult) {
716
- const { value: value2, mode, changes } = params;
722
+ const { value: value2, mode, changes, failedChanges } = params;
717
723
  updateResult = {
718
724
  value: deepMerge(updateResult.value, value2),
719
725
  mode,
720
- changes: changes ? [...updateResult.changes || [], ...changes] : updateResult.changes
726
+ changes: changes ? [...updateResult.changes || [], ...changes] : updateResult.changes,
727
+ failedChanges: failedChanges ? [...updateResult.failedChanges || [], ...failedChanges] : updateResult.failedChanges
721
728
  };
722
729
  } else {
723
730
  updateResult = params;
@@ -740,7 +747,11 @@ async function doChangeRemote(changeInfo) {
740
747
  });
741
748
  }
742
749
  if (!didError || (updateResult == null ? void 0 : updateResult.changes)) {
743
- const { value: updateValue, changes: updateChanges = changesRemote } = updateResult || {};
750
+ const {
751
+ value: updateValue,
752
+ changes: updateChanges = changesRemote,
753
+ failedChanges = []
754
+ } = updateResult || {};
744
755
  const changesWithPath = updateChanges;
745
756
  const pathStrs = Array.from(new Set(changesWithPath.map((change) => change.pathStr)));
746
757
  if (pathStrs.length > 0) {
@@ -748,6 +759,7 @@ async function doChangeRemote(changeInfo) {
748
759
  const metadata = {};
749
760
  const pending = localState.pendingChanges;
750
761
  const pendingToKeep = /* @__PURE__ */ new Set();
762
+ const failedPathStrs = new Set(failedChanges.map((change) => change.pathStr));
751
763
  if (pending) {
752
764
  const changesByPath = /* @__PURE__ */ new Map();
753
765
  for (let i = 0; i < changesWithPath.length; i++) {
@@ -760,7 +772,7 @@ async function doChangeRemote(changeInfo) {
760
772
  continue;
761
773
  }
762
774
  const change = changesByPath.get(key);
763
- if (!change || !deepEqual(pendingEntry.v, change.valueAtPath)) {
775
+ if (failedPathStrs.has(key) || !change || !deepEqual(pendingEntry.v, change.valueAtPath)) {
764
776
  pendingToKeep.add(key);
765
777
  }
766
778
  }
@@ -994,6 +1006,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
994
1006
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && (!obs$ || !node)) {
995
1007
  throw new Error("[legend-state] syncObservable called with undefined observable");
996
1008
  }
1009
+ markNodeAsSynced(node);
997
1010
  syncOptions = deepMerge(
998
1011
  {
999
1012
  syncMode: "auto"
package/.DS_Store DELETED
Binary file