@legendapp/state 3.0.0-beta.16 → 3.0.0-beta.17

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": "@legendapp/state",
3
- "version": "3.0.0-beta.16",
3
+ "version": "3.0.0-beta.17",
4
4
  "description": "legend-state",
5
5
  "sideEffects": false,
6
6
  "private": false,
@@ -25,12 +25,7 @@ function computeLastSync(data, fieldUpdatedAt, fieldCreatedAt) {
25
25
  }
26
26
  return newLastSync;
27
27
  }
28
- var queuedRetries = {
29
- create: /* @__PURE__ */ new Map(),
30
- update: /* @__PURE__ */ new Map(),
31
- delete: /* @__PURE__ */ new Map()
32
- };
33
- function retrySet(params, retry, action, itemKey, itemValue, change, actionFn, saveResult) {
28
+ function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetries, actionFn, saveResult) {
34
29
  if (action === "delete") {
35
30
  if (queuedRetries.create.has(itemKey)) {
36
31
  queuedRetries.create.delete(itemKey);
@@ -84,6 +79,11 @@ function syncedCrud(props) {
84
79
  } = props;
85
80
  const fieldId = fieldIdProp || "id";
86
81
  const pendingCreates = /* @__PURE__ */ new Set();
82
+ const queuedRetries = {
83
+ create: /* @__PURE__ */ new Map(),
84
+ update: /* @__PURE__ */ new Map(),
85
+ delete: /* @__PURE__ */ new Map()
86
+ };
87
87
  let asType = props.as;
88
88
  if (!asType) {
89
89
  asType = getFn ? "value" : "object";
@@ -367,6 +367,7 @@ function syncedCrud(props) {
367
367
  itemKey,
368
368
  createObj,
369
369
  changesById.get(itemKey),
370
+ queuedRetries,
370
371
  createFn,
371
372
  saveResult
372
373
  ).then(() => {
@@ -387,6 +388,7 @@ function syncedCrud(props) {
387
388
  itemKey,
388
389
  changed,
389
390
  changesById.get(itemKey),
391
+ queuedRetries,
390
392
  updateFn,
391
393
  saveResult
392
394
  );
@@ -410,6 +412,7 @@ function syncedCrud(props) {
410
412
  itemKey,
411
413
  valuePrevious,
412
414
  changesById.get(itemKey),
415
+ queuedRetries,
413
416
  deleteFn,
414
417
  saveResult
415
418
  );
@@ -422,6 +425,7 @@ function syncedCrud(props) {
422
425
  itemKey,
423
426
  { [fieldId]: itemKey, [fieldDeleted]: true },
424
427
  changesById.get(itemKey),
428
+ queuedRetries,
425
429
  updateFn,
426
430
  saveResult
427
431
  );
@@ -23,12 +23,7 @@ function computeLastSync(data, fieldUpdatedAt, fieldCreatedAt) {
23
23
  }
24
24
  return newLastSync;
25
25
  }
26
- var queuedRetries = {
27
- create: /* @__PURE__ */ new Map(),
28
- update: /* @__PURE__ */ new Map(),
29
- delete: /* @__PURE__ */ new Map()
30
- };
31
- function retrySet(params, retry, action, itemKey, itemValue, change, actionFn, saveResult) {
26
+ function retrySet(params, retry, action, itemKey, itemValue, change, queuedRetries, actionFn, saveResult) {
32
27
  if (action === "delete") {
33
28
  if (queuedRetries.create.has(itemKey)) {
34
29
  queuedRetries.create.delete(itemKey);
@@ -82,6 +77,11 @@ function syncedCrud(props) {
82
77
  } = props;
83
78
  const fieldId = fieldIdProp || "id";
84
79
  const pendingCreates = /* @__PURE__ */ new Set();
80
+ const queuedRetries = {
81
+ create: /* @__PURE__ */ new Map(),
82
+ update: /* @__PURE__ */ new Map(),
83
+ delete: /* @__PURE__ */ new Map()
84
+ };
85
85
  let asType = props.as;
86
86
  if (!asType) {
87
87
  asType = getFn ? "value" : "object";
@@ -365,6 +365,7 @@ function syncedCrud(props) {
365
365
  itemKey,
366
366
  createObj,
367
367
  changesById.get(itemKey),
368
+ queuedRetries,
368
369
  createFn,
369
370
  saveResult
370
371
  ).then(() => {
@@ -385,6 +386,7 @@ function syncedCrud(props) {
385
386
  itemKey,
386
387
  changed,
387
388
  changesById.get(itemKey),
389
+ queuedRetries,
388
390
  updateFn,
389
391
  saveResult
390
392
  );
@@ -408,6 +410,7 @@ function syncedCrud(props) {
408
410
  itemKey,
409
411
  valuePrevious,
410
412
  changesById.get(itemKey),
413
+ queuedRetries,
411
414
  deleteFn,
412
415
  saveResult
413
416
  );
@@ -420,6 +423,7 @@ function syncedCrud(props) {
420
423
  itemKey,
421
424
  { [fieldId]: itemKey, [fieldDeleted]: true },
422
425
  changesById.get(itemKey),
426
+ queuedRetries,
423
427
  updateFn,
424
428
  saveResult
425
429
  );
@@ -13,7 +13,7 @@ type APIError = {
13
13
  type: string;
14
14
  message: string;
15
15
  requestId?: string;
16
- error?: Error;
16
+ error?: unknown;
17
17
  };
18
18
  type APIResult<T> = Result<T, APIError>;
19
19
  type Data<T> = {
@@ -13,7 +13,7 @@ type APIError = {
13
13
  type: string;
14
14
  message: string;
15
15
  requestId?: string;
16
- error?: Error;
16
+ error?: unknown;
17
17
  };
18
18
  type APIResult<T> = Result<T, APIError>;
19
19
  type Data<T> = {
package/sync.js CHANGED
@@ -36,12 +36,12 @@ function diffObjects(obj1, obj2, deep = false) {
36
36
  return diff;
37
37
  }
38
38
  function deepEqual(a, b, ignoreFields, nullVsUndefined) {
39
- if (a === b) {
39
+ if (a === b)
40
40
  return true;
41
- }
42
- if (state.isNullOrUndefined(a) !== state.isNullOrUndefined(b)) {
41
+ if (state.isNullOrUndefined(a) !== state.isNullOrUndefined(b))
43
42
  return false;
44
- }
43
+ if (!state.isObject(a) || !state.isObject(b))
44
+ return a === b;
45
45
  if (nullVsUndefined) {
46
46
  a = removeNullUndefined(
47
47
  a,
@@ -54,8 +54,18 @@ function deepEqual(a, b, ignoreFields, nullVsUndefined) {
54
54
  true
55
55
  );
56
56
  }
57
- const replacer = ignoreFields ? (key, value) => ignoreFields.includes(key) ? void 0 : value : void 0;
58
- return JSON.stringify(a, replacer) === JSON.stringify(b, replacer);
57
+ const keysA = Object.keys(a).filter((key) => !(ignoreFields == null ? void 0 : ignoreFields.includes(key)));
58
+ const keysB = Object.keys(b).filter((key) => !(ignoreFields == null ? void 0 : ignoreFields.includes(key)));
59
+ if (keysA.length !== keysB.length)
60
+ return false;
61
+ return keysA.every((key) => {
62
+ if (!Object.prototype.hasOwnProperty.call(b, key))
63
+ return false;
64
+ if (state.isDate(a[key]) && state.isDate(b[key])) {
65
+ return a[key].getTime() === b[key].getTime();
66
+ }
67
+ return deepEqual(a[key], b[key], ignoreFields, nullVsUndefined);
68
+ });
59
69
  }
60
70
  function combineTransforms(...transforms) {
61
71
  return {
@@ -309,8 +319,25 @@ function mergeChanges(changes) {
309
319
  existing.valueAtPath = change.valueAtPath;
310
320
  }
311
321
  } else {
312
- changesByPath.set(pathStr, change);
313
- changesOut.push(change);
322
+ let found = false;
323
+ for (let u = 0; u < change.path.length; u++) {
324
+ const path = change.path.slice(0, u).join("/");
325
+ if (changesByPath.has(path)) {
326
+ const remaining = change.path.slice(u);
327
+ state.setAtPath(
328
+ changesByPath.get(path).valueAtPath,
329
+ remaining,
330
+ change.pathTypes.slice(u),
331
+ change.valueAtPath
332
+ );
333
+ found = true;
334
+ break;
335
+ }
336
+ }
337
+ if (!found) {
338
+ changesByPath.set(pathStr, change);
339
+ changesOut.push(change);
340
+ }
314
341
  }
315
342
  }
316
343
  return changesOut;
@@ -860,27 +887,33 @@ function syncObservable(obs$, syncOptionsOrSynced) {
860
887
  syncState$.isLoaded.set(!syncState$.numPendingRemoteLoads.peek());
861
888
  let isSynced = false;
862
889
  let isSubscribed = false;
890
+ let isApplyingPendingAfterSync = false;
863
891
  let unsubscribe = void 0;
864
892
  const applyPending = (pending) => {
865
893
  if (pending && !state.isEmpty(pending)) {
866
- localState.isApplyingPending = true;
867
894
  const keys = Object.keys(pending);
895
+ const value = getNodeValue(node);
868
896
  const changes = [];
869
897
  for (let i = 0; i < keys.length; i++) {
870
898
  const key = keys[i];
871
899
  const path = key.split("/").filter((p2) => p2 !== "");
872
- const { p, v, t } = pending[key];
873
- changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
900
+ const { p, t, v } = pending[key];
901
+ const valueAtPath = getValueAtPath(value, path);
902
+ if (isApplyingPendingAfterSync || !deepEqual(valueAtPath, v)) {
903
+ changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
904
+ }
905
+ }
906
+ if (changes.length > 0) {
907
+ localState.isApplyingPending = true;
908
+ onObsChange(obs$, syncState$, localState, syncOptions, {
909
+ value,
910
+ isFromPersist: false,
911
+ isFromSync: false,
912
+ getPrevious: createPreviousHandler(value, changes),
913
+ changes
914
+ });
915
+ localState.isApplyingPending = false;
874
916
  }
875
- const value = getNodeValue(node);
876
- onObsChange(obs$, syncState$, localState, syncOptions, {
877
- value,
878
- isFromPersist: false,
879
- isFromSync: false,
880
- getPrevious: createPreviousHandler(value, changes),
881
- changes
882
- });
883
- localState.isApplyingPending = false;
884
917
  }
885
918
  };
886
919
  const { get, subscribe } = syncOptions;
@@ -917,10 +950,10 @@ function syncObservable(obs$, syncOptionsOrSynced) {
917
950
  const { v, t } = pending2[key];
918
951
  if (t.length === 0 || !value) {
919
952
  const oldValue = clone2(value);
920
- pending2[key].p = oldValue;
953
+ pending2[key].p = key ? oldValue[key] : oldValue;
921
954
  if (state.isObject(value) && state.isObject(v)) {
922
- Object.assign(value, v);
923
- } else {
955
+ Object.assign(value, key ? { [key]: v } : v);
956
+ } else if (!key) {
924
957
  value = v;
925
958
  }
926
959
  } else if (value[p[0]] !== void 0) {
@@ -932,6 +965,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
932
965
  } else {
933
966
  const oldValue = clone2(value);
934
967
  pending2[key].p = getValueAtPath(oldValue, p);
968
+ didChangeMetadata = true;
935
969
  value = state.setAtPath(
936
970
  value,
937
971
  p,
@@ -952,7 +986,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
952
986
  }
953
987
  });
954
988
  if (didChangeMetadata && syncOptions.persist) {
955
- updateMetadata(obs$, localState, syncState$, syncOptions, {
989
+ updateMetadataImmediate(obs$, localState, syncState$, syncOptions, {
956
990
  pending: pending2
957
991
  });
958
992
  }
@@ -1129,14 +1163,17 @@ function syncObservable(obs$, syncOptionsOrSynced) {
1129
1163
  }
1130
1164
  if (!isSynced) {
1131
1165
  isSynced = true;
1132
- await state.when(syncState$.isLoaded);
1166
+ isApplyingPendingAfterSync = true;
1133
1167
  applyPending(pending);
1168
+ isApplyingPendingAfterSync = false;
1134
1169
  }
1135
1170
  };
1136
1171
  syncStateValue.sync = sync;
1137
1172
  } else {
1138
1173
  if (!isSynced) {
1174
+ isApplyingPendingAfterSync = true;
1139
1175
  applyPending(localState.pendingChanges);
1176
+ isApplyingPendingAfterSync = false;
1140
1177
  }
1141
1178
  }
1142
1179
  syncStateValue.reset = async () => {
package/sync.mjs CHANGED
@@ -34,12 +34,12 @@ function diffObjects(obj1, obj2, deep = false) {
34
34
  return diff;
35
35
  }
36
36
  function deepEqual(a, b, ignoreFields, nullVsUndefined) {
37
- if (a === b) {
37
+ if (a === b)
38
38
  return true;
39
- }
40
- if (isNullOrUndefined(a) !== isNullOrUndefined(b)) {
39
+ if (isNullOrUndefined(a) !== isNullOrUndefined(b))
41
40
  return false;
42
- }
41
+ if (!isObject(a) || !isObject(b))
42
+ return a === b;
43
43
  if (nullVsUndefined) {
44
44
  a = removeNullUndefined(
45
45
  a,
@@ -52,8 +52,18 @@ function deepEqual(a, b, ignoreFields, nullVsUndefined) {
52
52
  true
53
53
  );
54
54
  }
55
- const replacer = ignoreFields ? (key, value) => ignoreFields.includes(key) ? void 0 : value : void 0;
56
- return JSON.stringify(a, replacer) === JSON.stringify(b, replacer);
55
+ const keysA = Object.keys(a).filter((key) => !(ignoreFields == null ? void 0 : ignoreFields.includes(key)));
56
+ const keysB = Object.keys(b).filter((key) => !(ignoreFields == null ? void 0 : ignoreFields.includes(key)));
57
+ if (keysA.length !== keysB.length)
58
+ return false;
59
+ return keysA.every((key) => {
60
+ if (!Object.prototype.hasOwnProperty.call(b, key))
61
+ return false;
62
+ if (isDate(a[key]) && isDate(b[key])) {
63
+ return a[key].getTime() === b[key].getTime();
64
+ }
65
+ return deepEqual(a[key], b[key], ignoreFields, nullVsUndefined);
66
+ });
57
67
  }
58
68
  function combineTransforms(...transforms) {
59
69
  return {
@@ -307,8 +317,25 @@ function mergeChanges(changes) {
307
317
  existing.valueAtPath = change.valueAtPath;
308
318
  }
309
319
  } else {
310
- changesByPath.set(pathStr, change);
311
- changesOut.push(change);
320
+ let found = false;
321
+ for (let u = 0; u < change.path.length; u++) {
322
+ const path = change.path.slice(0, u).join("/");
323
+ if (changesByPath.has(path)) {
324
+ const remaining = change.path.slice(u);
325
+ setAtPath(
326
+ changesByPath.get(path).valueAtPath,
327
+ remaining,
328
+ change.pathTypes.slice(u),
329
+ change.valueAtPath
330
+ );
331
+ found = true;
332
+ break;
333
+ }
334
+ }
335
+ if (!found) {
336
+ changesByPath.set(pathStr, change);
337
+ changesOut.push(change);
338
+ }
312
339
  }
313
340
  }
314
341
  return changesOut;
@@ -858,27 +885,33 @@ function syncObservable(obs$, syncOptionsOrSynced) {
858
885
  syncState$.isLoaded.set(!syncState$.numPendingRemoteLoads.peek());
859
886
  let isSynced = false;
860
887
  let isSubscribed = false;
888
+ let isApplyingPendingAfterSync = false;
861
889
  let unsubscribe = void 0;
862
890
  const applyPending = (pending) => {
863
891
  if (pending && !isEmpty(pending)) {
864
- localState.isApplyingPending = true;
865
892
  const keys = Object.keys(pending);
893
+ const value = getNodeValue(node);
866
894
  const changes = [];
867
895
  for (let i = 0; i < keys.length; i++) {
868
896
  const key = keys[i];
869
897
  const path = key.split("/").filter((p2) => p2 !== "");
870
- const { p, v, t } = pending[key];
871
- changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
898
+ const { p, t, v } = pending[key];
899
+ const valueAtPath = getValueAtPath(value, path);
900
+ if (isApplyingPendingAfterSync || !deepEqual(valueAtPath, v)) {
901
+ changes.push({ path, valueAtPath: v, prevAtPath: p, pathTypes: t });
902
+ }
903
+ }
904
+ if (changes.length > 0) {
905
+ localState.isApplyingPending = true;
906
+ onObsChange(obs$, syncState$, localState, syncOptions, {
907
+ value,
908
+ isFromPersist: false,
909
+ isFromSync: false,
910
+ getPrevious: createPreviousHandler(value, changes),
911
+ changes
912
+ });
913
+ localState.isApplyingPending = false;
872
914
  }
873
- const value = getNodeValue(node);
874
- onObsChange(obs$, syncState$, localState, syncOptions, {
875
- value,
876
- isFromPersist: false,
877
- isFromSync: false,
878
- getPrevious: createPreviousHandler(value, changes),
879
- changes
880
- });
881
- localState.isApplyingPending = false;
882
915
  }
883
916
  };
884
917
  const { get, subscribe } = syncOptions;
@@ -915,10 +948,10 @@ function syncObservable(obs$, syncOptionsOrSynced) {
915
948
  const { v, t } = pending2[key];
916
949
  if (t.length === 0 || !value) {
917
950
  const oldValue = clone2(value);
918
- pending2[key].p = oldValue;
951
+ pending2[key].p = key ? oldValue[key] : oldValue;
919
952
  if (isObject(value) && isObject(v)) {
920
- Object.assign(value, v);
921
- } else {
953
+ Object.assign(value, key ? { [key]: v } : v);
954
+ } else if (!key) {
922
955
  value = v;
923
956
  }
924
957
  } else if (value[p[0]] !== void 0) {
@@ -930,6 +963,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
930
963
  } else {
931
964
  const oldValue = clone2(value);
932
965
  pending2[key].p = getValueAtPath(oldValue, p);
966
+ didChangeMetadata = true;
933
967
  value = setAtPath(
934
968
  value,
935
969
  p,
@@ -950,7 +984,7 @@ function syncObservable(obs$, syncOptionsOrSynced) {
950
984
  }
951
985
  });
952
986
  if (didChangeMetadata && syncOptions.persist) {
953
- updateMetadata(obs$, localState, syncState$, syncOptions, {
987
+ updateMetadataImmediate(obs$, localState, syncState$, syncOptions, {
954
988
  pending: pending2
955
989
  });
956
990
  }
@@ -1127,14 +1161,17 @@ function syncObservable(obs$, syncOptionsOrSynced) {
1127
1161
  }
1128
1162
  if (!isSynced) {
1129
1163
  isSynced = true;
1130
- await when(syncState$.isLoaded);
1164
+ isApplyingPendingAfterSync = true;
1131
1165
  applyPending(pending);
1166
+ isApplyingPendingAfterSync = false;
1132
1167
  }
1133
1168
  };
1134
1169
  syncStateValue.sync = sync;
1135
1170
  } else {
1136
1171
  if (!isSynced) {
1172
+ isApplyingPendingAfterSync = true;
1137
1173
  applyPending(localState.pendingChanges);
1174
+ isApplyingPendingAfterSync = false;
1138
1175
  }
1139
1176
  }
1140
1177
  syncStateValue.reset = async () => {