@legendapp/state 2.0.0-next.3 → 2.0.0-next.4

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/index.js CHANGED
@@ -163,7 +163,7 @@ function setNodeValue(node, newValue) {
163
163
  if (parentNode.root.locked && parentNode.root.set) {
164
164
  parentNode.root.set(parentNode.root._);
165
165
  }
166
- return { prevValue, newValue };
166
+ return { prevValue, newValue, parentValue };
167
167
  }
168
168
  const arrNodeKeys = [];
169
169
  function getNodeValue(node) {
@@ -573,55 +573,50 @@ function setInObservableAtPath(obs, path, value, mode) {
573
573
  function mergeIntoObservable(target, ...sources) {
574
574
  beginBatch();
575
575
  globalState.isMerging = true;
576
- const value = _mergeIntoObservable(target, ...sources);
576
+ for (let i = 0; i < sources.length; i++) {
577
+ target = _mergeIntoObservable(target, sources[i]);
578
+ }
577
579
  globalState.isMerging = false;
578
580
  endBatch();
579
- return value;
581
+ return target;
580
582
  }
581
- function _mergeIntoObservable(target, ...sources) {
583
+ function _mergeIntoObservable(target, source) {
582
584
  var _a;
583
- if (!sources.length)
584
- return target;
585
- for (let u = 0; u < sources.length; u++) {
586
- const source = sources[u];
587
- const needsSet = isObservable(target);
588
- const targetValue = needsSet ? target.peek() : target;
589
- const isTargetArr = isArray(targetValue);
590
- const isTargetObj = !isTargetArr && isObject(targetValue);
591
- if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
592
- (isTargetArr && isArray(source) && targetValue.length > 0)) {
593
- for (const key in source) {
594
- const sourceValue = source[key];
595
- if (sourceValue === symbolDelete) {
596
- needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete)
597
- ? target[key].delete()
598
- : delete target[key];
599
- }
600
- else {
601
- const isObj = isObject(sourceValue);
602
- const isArr = !isObj && isArray(sourceValue);
603
- const targetChild = target[key];
604
- if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
605
- if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
606
- target[key] = sourceValue;
607
- }
608
- else {
609
- _mergeIntoObservable(targetChild, sourceValue);
610
- }
585
+ const needsSet = isObservable(target);
586
+ const targetValue = needsSet ? target.peek() : target;
587
+ const isTargetArr = isArray(targetValue);
588
+ const isTargetObj = !isTargetArr && isObject(targetValue);
589
+ if ((isTargetObj && isObject(source) && !isEmpty(targetValue)) ||
590
+ (isTargetArr && isArray(source) && targetValue.length > 0)) {
591
+ const keys = Object.keys(source);
592
+ for (let i = 0; i < keys.length; i++) {
593
+ const key = keys[i];
594
+ const sourceValue = source[key];
595
+ if (sourceValue === symbolDelete) {
596
+ needsSet && ((_a = target[key]) === null || _a === void 0 ? void 0 : _a.delete) ? target[key].delete() : delete target[key];
597
+ }
598
+ else {
599
+ const isObj = isObject(sourceValue);
600
+ const isArr = !isObj && isArray(sourceValue);
601
+ const targetChild = target[key];
602
+ if ((isObj || isArr) && targetChild && (needsSet || !isEmpty(targetChild))) {
603
+ if (!needsSet && (!targetChild || (isObj ? !isObject(targetChild) : !isArray(targetChild)))) {
604
+ target[key] = sourceValue;
611
605
  }
612
606
  else {
613
- // TODO: It should not be possible for targetChild to be a non-observable, but it was happening in Legend.
614
- // It would be good to track down why that was happening and remove this check.
615
- needsSet && isFunction(targetChild.set)
616
- ? targetChild.set(sourceValue)
617
- : (target[key] = sourceValue);
607
+ _mergeIntoObservable(targetChild, sourceValue);
618
608
  }
619
609
  }
610
+ else {
611
+ needsSet
612
+ ? targetChild.set(sourceValue)
613
+ : (target[key] = sourceValue);
614
+ }
620
615
  }
621
616
  }
622
- else if (source !== undefined) {
623
- needsSet ? target.set(source) : (target = source);
624
- }
617
+ }
618
+ else if (source !== undefined) {
619
+ needsSet ? target.set(source) : (target = source);
625
620
  }
626
621
  return target;
627
622
  }
@@ -684,11 +679,6 @@ function onChange(node, callback, options = {}) {
684
679
  noArgs,
685
680
  };
686
681
  listeners.add(listener);
687
- let parent = node.parent;
688
- while (parent && !parent.descendantHasListener) {
689
- parent.descendantHasListener = true;
690
- parent = parent.parent;
691
- }
692
682
  if (initial) {
693
683
  const value = getNodeValue(node);
694
684
  callback({
@@ -917,75 +907,74 @@ function updateNodes(parent, obj, prevValue) {
917
907
  hasADiff = hasADiff || (keys === null || keys === void 0 ? void 0 : keys.length) !== (keysPrev === null || keysPrev === void 0 ? void 0 : keysPrev.length);
918
908
  const isArrDiff = hasADiff;
919
909
  let didMove = false;
920
- if (parent.descendantHasListener || !hasADiff) {
921
- for (let i = 0; i < length; i++) {
922
- const key = keys[i];
923
- const value = isMap ? obj.get(key) : obj[key];
924
- const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
925
- let isDiff = value !== prev;
926
- if (isDiff) {
927
- const id = idField && value
928
- ? isIdFieldFunction
929
- ? idField(value)
930
- : value[idField]
931
- : undefined;
932
- let child = getChildNode(parent, key);
933
- // Detect moves within an array. Need to move the original proxy to the new position to keep
934
- // the proxy stable, so that listeners to this node will be unaffected by the array shift.
935
- if (isArr && id !== undefined) {
936
- // Find the previous position of this element in the array
937
- const prevChild = id !== undefined ? prevChildrenById === null || prevChildrenById === void 0 ? void 0 : prevChildrenById.get(id) : undefined;
938
- if (!prevChild) {
939
- // This id was not in the array before so it does not need to notify children
940
- isDiff = false;
941
- hasADiff = true;
942
- }
943
- else if (prevChild !== undefined && prevChild.key !== key) {
944
- const valuePrevChild = prevValue[prevChild.key];
945
- // If array length changed then move the original node to the current position.
946
- // That should be faster than notifying every single element that
947
- // it's in a new position.
948
- if (isArrDiff) {
949
- child = prevChild;
950
- parent.children.delete(child.key);
951
- child.key = key;
952
- moved.push([key, child]);
953
- }
954
- didMove = true;
955
- // And check for diff against the previous value in the previous position
956
- isDiff = valuePrevChild !== value;
957
- }
910
+ for (let i = 0; i < length; i++) {
911
+ const key = keys[i];
912
+ const value = isMap ? obj.get(key) : obj[key];
913
+ const prev = isMap ? prevValue === null || prevValue === void 0 ? void 0 : prevValue.get(key) : prevValue === null || prevValue === void 0 ? void 0 : prevValue[key];
914
+ let isDiff = value !== prev;
915
+ if (isDiff) {
916
+ extractFunctionOrComputed(parent, obj, key, value);
917
+ const id = idField && value
918
+ ? isIdFieldFunction
919
+ ? idField(value)
920
+ : value[idField]
921
+ : undefined;
922
+ let child = getChildNode(parent, key);
923
+ // Detect moves within an array. Need to move the original proxy to the new position to keep
924
+ // the proxy stable, so that listeners to this node will be unaffected by the array shift.
925
+ if (isArr && id !== undefined) {
926
+ // Find the previous position of this element in the array
927
+ const prevChild = id !== undefined ? prevChildrenById === null || prevChildrenById === void 0 ? void 0 : prevChildrenById.get(id) : undefined;
928
+ if (!prevChild) {
929
+ // This id was not in the array before so it does not need to notify children
930
+ isDiff = false;
931
+ hasADiff = true;
958
932
  }
959
- if (isDiff) {
960
- // Array has a new / modified element
961
- // If object iterate through its children
962
- if (isPrimitive(value)) {
963
- hasADiff = true;
964
- }
965
- else {
966
- // Always need to updateNodes so we notify through all children
967
- const updatedNodes = (!hasADiff || !!child.descendantHasListener) && updateNodes(child, value, prev);
968
- hasADiff = hasADiff || updatedNodes;
933
+ else if (prevChild !== undefined && prevChild.key !== key) {
934
+ const valuePrevChild = prevValue[prevChild.key];
935
+ // If array length changed then move the original node to the current position.
936
+ // That should be faster than notifying every single element that
937
+ // it's in a new position.
938
+ if (isArrDiff) {
939
+ child = prevChild;
940
+ parent.children.delete(child.key);
941
+ child.key = key;
942
+ moved.push([key, child]);
969
943
  }
944
+ didMove = true;
945
+ // And check for diff against the previous value in the previous position
946
+ isDiff = valuePrevChild !== value;
970
947
  }
971
- if (isDiff || !isArrDiff) {
972
- // Notify for this child if this element is different and it has listeners
973
- // Or if the position changed in an array whose length did not change
974
- // But do not notify child if the parent is an array with changing length -
975
- // the array's listener will cover it
976
- if (child.listeners || child.listenersImmediate) {
977
- notify(child, value, prev, 0, !isArrDiff);
978
- }
948
+ }
949
+ if (isDiff) {
950
+ // Array has a new / modified element
951
+ // If object iterate through its children
952
+ if (isPrimitive(value)) {
953
+ hasADiff = true;
954
+ }
955
+ else {
956
+ // Always need to updateNodes so we notify through all children
957
+ const updatedNodes = updateNodes(child, value, prev);
958
+ hasADiff = hasADiff || updatedNodes;
979
959
  }
980
960
  }
981
- }
982
- if (moved) {
983
- for (let i = 0; i < moved.length; i++) {
984
- const [key, child] = moved[i];
985
- parent.children.set(key, child);
961
+ if (isDiff || !isArrDiff) {
962
+ // Notify for this child if this element is different and it has listeners
963
+ // Or if the position changed in an array whose length did not change
964
+ // But do not notify child if the parent is an array with changing length -
965
+ // the array's listener will cover it
966
+ if (child.listeners || child.listenersImmediate) {
967
+ notify(child, value, prev, 0, !isArrDiff);
968
+ }
986
969
  }
987
970
  }
988
971
  }
972
+ if (moved) {
973
+ for (let i = 0; i < moved.length; i++) {
974
+ const [key, child] = moved[i];
975
+ parent.children.set(key, child);
976
+ }
977
+ }
989
978
  // The full array does not need to re-render if the length is the same
990
979
  // So don't notify shallow listeners
991
980
  retValue = hasADiff || didMove;
@@ -1198,10 +1187,7 @@ const proxyHandler = {
1198
1187
  },
1199
1188
  };
1200
1189
  function set(node, newValue) {
1201
- if (isPromise(newValue)) {
1202
- newValue.then((v) => set(node, v)).catch((error) => set(node, { error }));
1203
- }
1204
- else if (node.parent) {
1190
+ if (node.parent) {
1205
1191
  return setKey(node.parent, node.key, newValue);
1206
1192
  }
1207
1193
  else {
@@ -1241,12 +1227,13 @@ function setKey(node, key, newValue, level) {
1241
1227
  // Get the child node for updating and notifying
1242
1228
  const childNode = isRoot ? node : getChildNode(node, key);
1243
1229
  // Set the raw value on the parent object
1244
- const { newValue: savedValue, prevValue } = setNodeValue(childNode, newValue);
1245
- const isFunc = isFunction(newValue);
1230
+ const { newValue: savedValue, prevValue, parentValue } = setNodeValue(childNode, newValue);
1231
+ const isFunc = isFunction(savedValue);
1246
1232
  const isPrim = isPrimitive(savedValue) || savedValue instanceof Date;
1247
1233
  if (savedValue !== prevValue) {
1248
1234
  updateNodesAndNotify(node, savedValue, prevValue, childNode, isPrim, isRoot, level);
1249
1235
  }
1236
+ extractFunctionOrComputed(node, parentValue, key, newValue);
1250
1237
  return isFunc ? savedValue : isRoot ? getProxy(node) : getProxy(node, key);
1251
1238
  }
1252
1239
  function assign(node, value) {
@@ -1372,13 +1359,48 @@ function updateNodesAndNotify(node, newValue, prevValue, childNode, isPrim, isRo
1372
1359
  }
1373
1360
  function extractPromise(node, value) {
1374
1361
  value.status = 'pending';
1375
- value.catch((error) => {
1376
- set(node, { error, status: 'rejected' });
1377
- });
1378
- value.then((value) => {
1362
+ value
1363
+ .then((value) => {
1379
1364
  set(node, value);
1365
+ })
1366
+ .catch((error) => {
1367
+ set(node, { error, status: 'rejected' });
1380
1368
  });
1381
1369
  }
1370
+ const __devExtractFunctionsAndComputedsNodes = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ? new Set() : undefined;
1371
+ function extractFunctionOrComputed(node, obj, k, v) {
1372
+ if (isPromise(v)) {
1373
+ extractPromise(getChildNode(node, k), v);
1374
+ }
1375
+ else if (typeof v === 'function') {
1376
+ extractFunction(node, k, v);
1377
+ }
1378
+ else if (typeof v == 'object' && v !== null && v !== undefined) {
1379
+ const childNode = getNode(v);
1380
+ if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
1381
+ extractFunction(node, k, v, childNode);
1382
+ delete obj[k];
1383
+ }
1384
+ else {
1385
+ return true;
1386
+ }
1387
+ }
1388
+ }
1389
+ function extractFunctionsAndComputeds(node, obj) {
1390
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
1391
+ if (__devExtractFunctionsAndComputedsNodes.has(obj)) {
1392
+ console.error('[legend-state] Circular reference detected in object. You may want to use opaqueObject to stop traversing child nodes.', obj);
1393
+ return false;
1394
+ }
1395
+ __devExtractFunctionsAndComputedsNodes.add(obj);
1396
+ }
1397
+ for (const k in obj) {
1398
+ const v = obj[k];
1399
+ if (v && extractFunctionOrComputed(node, obj, k, v) && !v[symbolOpaque]) {
1400
+ extractFunctionsAndComputeds(getChildNode(node, k), v);
1401
+ }
1402
+ }
1403
+ }
1382
1404
 
1383
1405
  const fns = ['get', 'set', 'peek', 'onChange', 'toggle'];
1384
1406
  function ObservablePrimitiveClass(node) {
@@ -1421,35 +1443,6 @@ ObservablePrimitiveClass.prototype.delete = function () {
1421
1443
  return this;
1422
1444
  };
1423
1445
 
1424
- const __devExtractFunctionsAndComputedsNodes = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ? new Set() : undefined;
1425
- function extractFunctionsAndComputeds(node, obj) {
1426
- if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
1427
- if (__devExtractFunctionsAndComputedsNodes.has(obj)) {
1428
- console.error('[legend-state] Circular reference detected in object. You may want to use opaqueObject to stop traversing child nodes.', obj);
1429
- return false;
1430
- }
1431
- __devExtractFunctionsAndComputedsNodes.add(obj);
1432
- }
1433
- for (const k in obj) {
1434
- const v = obj[k];
1435
- if (isPromise(v)) {
1436
- extractPromise(getChildNode(node, k), v);
1437
- }
1438
- else if (typeof v === 'function') {
1439
- extractFunction(node, k, v);
1440
- }
1441
- else if (typeof v == 'object' && v !== null && v !== undefined) {
1442
- const childNode = getNode(v);
1443
- if (childNode === null || childNode === void 0 ? void 0 : childNode.isComputed) {
1444
- extractFunction(node, k, v, childNode);
1445
- delete obj[k];
1446
- }
1447
- else if (!v[symbolOpaque]) {
1448
- extractFunctionsAndComputeds(getChildNode(node, k), obj[k]);
1449
- }
1450
- }
1451
- }
1452
- }
1453
1446
  function createObservable(value, makePrimitive) {
1454
1447
  const valueIsPromise = isPromise(value);
1455
1448
  const root = {