@jbrowse/mobx-state-tree 5.10.8 → 5.11.0

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.
@@ -4903,7 +4903,7 @@ class ModelType extends ComplexType {
4903
4903
  }
4904
4904
  getSnapshot(node, applyPostProcess = true) {
4905
4905
  const res = {};
4906
- this.forAllProps((name, _type) => {
4906
+ this.forAllProps((name, type) => {
4907
4907
  try {
4908
4908
  // TODO: FIXME, make sure the observable ref is used!
4909
4909
  const atom = getAtom(node.storedValue, name);
@@ -4912,7 +4912,11 @@ class ModelType extends ComplexType {
4912
4912
  catch (_e) {
4913
4913
  throw fail(`${name} property is declared twice`);
4914
4914
  }
4915
- res[name] = this.getChildNode(node, name).snapshot;
4915
+ const snapshot = this.getChildNode(node, name).snapshot;
4916
+ // strip-default optionals omit their key when equal to the default
4917
+ if (!shouldStripChildFromSnapshot(type, snapshot)) {
4918
+ res[name] = snapshot;
4919
+ }
4916
4920
  });
4917
4921
  if (applyPostProcess) {
4918
4922
  return this.applySnapshotPostProcessor(res);
@@ -4922,7 +4926,13 @@ class ModelType extends ComplexType {
4922
4926
  processInitialSnapshot(childNodes) {
4923
4927
  const processed = {};
4924
4928
  Object.keys(childNodes).forEach(key => {
4925
- processed[key] = childNodes[key].getSnapshot();
4929
+ const snapshot = childNodes[key].getSnapshot();
4930
+ // strip-default optionals omit their key when equal to the default; this
4931
+ // mirrors getSnapshot for nodes serialized before they become observable
4932
+ // instances (e.g. array/map children that were never accessed)
4933
+ if (!shouldStripChildFromSnapshot(this.properties[key], snapshot)) {
4934
+ processed[key] = snapshot;
4935
+ }
4926
4936
  });
4927
4937
  return this.applySnapshotPostProcessor(processed);
4928
4938
  }
@@ -5876,6 +5886,72 @@ const undefinedAsOptionalValues = [undefined];
5876
5886
  function isOptionalType(type) {
5877
5887
  return isType(type) && (type.flags & TypeFlags.Optional) > 0;
5878
5888
  }
5889
+ /**
5890
+ * Compare a child snapshot to a stripped-default's reference snapshot. Mirrors
5891
+ * the legacy hand-rolled comparison: identity for primitives, structural for
5892
+ * objects/arrays.
5893
+ */
5894
+ function defaultSnapshotEquals(a, b) {
5895
+ if (a === b) {
5896
+ return true;
5897
+ }
5898
+ if (typeof a === "object" &&
5899
+ a !== null &&
5900
+ typeof b === "object" &&
5901
+ b !== null) {
5902
+ return JSON.stringify(a) === JSON.stringify(b);
5903
+ }
5904
+ return false;
5905
+ }
5906
+ /**
5907
+ * An optional type that additionally omits its key from a parent model's
5908
+ * snapshot when the value equals the (snapshotted) default. The comparison is
5909
+ * against the default *snapshot* — i.e. the subtype is instantiated with the
5910
+ * default once and its post-processed snapshot is cached — so a default whose
5911
+ * normalized form gains fields (e.g. a fileLocation gaining `locationType`)
5912
+ * still strips correctly.
5913
+ *
5914
+ * @hidden
5915
+ * @internal
5916
+ */
5917
+ class StripDefaultValue extends OptionalValue {
5918
+ _defaultSnapshot;
5919
+ shouldStripFromSnapshot(snapshot) {
5920
+ if (!this._defaultSnapshot) {
5921
+ // instantiate the subtype detached with the default and read the node's
5922
+ // snapshot, which normalizes (fills model defaults, applies the subtype's
5923
+ // own postProcess). Cached on the (singleton) type after first use.
5924
+ const node = this.getSubTypes().instantiate(null, "", undefined, this.getDefaultInstanceOrSnapshot());
5925
+ this._defaultSnapshot = { value: node.snapshot };
5926
+ }
5927
+ return defaultSnapshotEquals(snapshot, this._defaultSnapshot.value);
5928
+ }
5929
+ }
5930
+ /**
5931
+ * Whether `type` is a strip-default optional whose current child `snapshot`
5932
+ * equals its default and should therefore be omitted from the parent model's
5933
+ * snapshot. Used by `ModelType.getSnapshot`.
5934
+ *
5935
+ * @hidden
5936
+ * @internal
5937
+ */
5938
+ function shouldStripChildFromSnapshot(type, snapshot) {
5939
+ return (type instanceof StripDefaultValue && type.shouldStripFromSnapshot(snapshot));
5940
+ }
5941
+ /**
5942
+ * `types.stripDefault` - Like `types.optional`, but the property is omitted from
5943
+ * a parent model's snapshot entirely when its value equals the default (instead
5944
+ * of being serialized with the default value). Lets a model produce minimal
5945
+ * snapshots without a bespoke `postProcessSnapshot`.
5946
+ *
5947
+ * @param type
5948
+ * @param defaultValueOrFunction
5949
+ * @returns
5950
+ */
5951
+ function stripDefault(type, defaultValueOrFunction) {
5952
+ checkOptionalPreconditions(type, defaultValueOrFunction);
5953
+ return new StripDefaultValue(type, defaultValueOrFunction, undefinedAsOptionalValues);
5954
+ }
5879
5955
 
5880
5956
  const optionalUndefinedType = optional(undefinedType, undefined);
5881
5957
  const optionalNullType = optional(nullType, null);
@@ -6893,6 +6969,7 @@ const types = {
6893
6969
  safeReference,
6894
6970
  union,
6895
6971
  optional,
6972
+ stripDefault,
6896
6973
  literal,
6897
6974
  maybe,
6898
6975
  maybeNull,