@colyseus/schema 4.0.24 → 4.0.26
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/build/encoder/StateView.d.ts +1 -1
- package/build/index.cjs +80 -42
- package/build/index.cjs.map +1 -1
- package/build/index.js +80 -42
- package/build/index.mjs +80 -42
- package/build/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Metadata.ts +17 -4
- package/src/Schema.ts +3 -2
- package/src/decoder/DecodeOperation.ts +13 -4
- package/src/encoder/ChangeTree.ts +17 -3
- package/src/encoder/StateView.ts +32 -32
package/build/index.js
CHANGED
|
@@ -821,10 +821,25 @@
|
|
|
821
821
|
});
|
|
822
822
|
}
|
|
823
823
|
metadata[$viewFieldIndexes].push(index);
|
|
824
|
-
|
|
825
|
-
|
|
824
|
+
// Populate $fieldIndexesByViewTag: for a bitmask tag, register the field
|
|
825
|
+
// index under each individual set bit so that view.add(obj, Tag.ONE) finds
|
|
826
|
+
// fields tagged @view(Tag.ONE|Tag.TWO).
|
|
827
|
+
// Negative tags (i.e. DEFAULT_VIEW_TAG = -1) are stored as-is.
|
|
828
|
+
if (tag < 0) {
|
|
829
|
+
if (!metadata[$fieldIndexesByViewTag][tag]) {
|
|
830
|
+
metadata[$fieldIndexesByViewTag][tag] = [];
|
|
831
|
+
}
|
|
832
|
+
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
for (let bits = tag; bits > 0; bits &= bits - 1) {
|
|
836
|
+
const bit = bits & (-bits); // isolate lowest set bit
|
|
837
|
+
if (!metadata[$fieldIndexesByViewTag][bit]) {
|
|
838
|
+
metadata[$fieldIndexesByViewTag][bit] = [];
|
|
839
|
+
}
|
|
840
|
+
metadata[$fieldIndexesByViewTag][bit].push(index);
|
|
841
|
+
}
|
|
826
842
|
}
|
|
827
|
-
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
828
843
|
},
|
|
829
844
|
setFields(target, fields) {
|
|
830
845
|
// for inheritance support
|
|
@@ -1375,7 +1390,8 @@
|
|
|
1375
1390
|
key += `-${this.root.types.schemas.get(parentConstructor)}`;
|
|
1376
1391
|
}
|
|
1377
1392
|
key += `-${parentIndex}`;
|
|
1378
|
-
const
|
|
1393
|
+
const parentMetadata = parentConstructor?.[Symbol.metadata];
|
|
1394
|
+
const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentMetadata, parentIndex);
|
|
1379
1395
|
this.isFiltered = parent[$changes].isFiltered // in case parent is already filtered
|
|
1380
1396
|
|| this.root.types.parentFiltered[key]
|
|
1381
1397
|
|| fieldHasViewTag;
|
|
@@ -1384,9 +1400,22 @@
|
|
|
1384
1400
|
// when it's available, we need to enqueue the "changes" changeset into the "filteredChanges" changeset.
|
|
1385
1401
|
//
|
|
1386
1402
|
if (this.isFiltered) {
|
|
1403
|
+
//
|
|
1404
|
+
// Children of a `@view(N)` collection (non-default tag) inherit
|
|
1405
|
+
// visibility from their parent, so items pushed/set after the
|
|
1406
|
+
// initial `view.add(state, N)` show up automatically.
|
|
1407
|
+
//
|
|
1408
|
+
// Default-tag `@view()` collections deliberately keep per-item
|
|
1409
|
+
// gating — `view.add(item)` is required to opt each one in.
|
|
1410
|
+
//
|
|
1411
|
+
// The `parentMetadata[parentIndex].tag` access is safe inside
|
|
1412
|
+
// this branch: the OR's short-circuit means we only reach it
|
|
1413
|
+
// when `fieldHasViewTag` is true, which guarantees the metadata
|
|
1414
|
+
// entry and its `tag` property exist.
|
|
1415
|
+
//
|
|
1387
1416
|
this.isVisibilitySharedWithParent = (parentChangeTree.isFiltered &&
|
|
1388
1417
|
typeof (refType) !== "string" &&
|
|
1389
|
-
!fieldHasViewTag);
|
|
1418
|
+
(!fieldHasViewTag || (parentIsCollection && parentMetadata[parentIndex].tag !== DEFAULT_VIEW_TAG)));
|
|
1390
1419
|
if (!this.filteredChanges) {
|
|
1391
1420
|
this.filteredChanges = createChangeSet();
|
|
1392
1421
|
this.allFilteredChanges = createChangeSet();
|
|
@@ -1666,17 +1695,25 @@
|
|
|
1666
1695
|
if (previousValue) {
|
|
1667
1696
|
let previousRefId = previousValue[$refId];
|
|
1668
1697
|
if (previousRefId !== undefined && refId !== previousRefId) {
|
|
1698
|
+
// Collection field replaced by a different instance.
|
|
1669
1699
|
//
|
|
1670
|
-
//
|
|
1671
|
-
//
|
|
1700
|
+
// Don't decrement children here: GC (`garbageCollectDeletedRefs`)
|
|
1701
|
+
// removes them once the previous collection's refId hits zero.
|
|
1702
|
+
// Doing it here too would double-decrement a *shared* child and
|
|
1703
|
+
// drop it while still referenced ("refId not found").
|
|
1704
|
+
if ((operation & exports.OPERATION.DELETE) !== exports.OPERATION.DELETE) {
|
|
1705
|
+
// Replacement not tagged DELETE (e.g. pending ADD not upgraded
|
|
1706
|
+
// to DELETE_AND_ADD), so the previous refId wasn't decremented
|
|
1707
|
+
// above. Release it here, else it never gets GC'd (leak).
|
|
1708
|
+
$root.removeRef(previousRefId);
|
|
1709
|
+
}
|
|
1710
|
+
// enqueue onRemove callbacks for the previous collection's children.
|
|
1672
1711
|
const entries = previousValue.entries();
|
|
1673
1712
|
let iter;
|
|
1674
1713
|
while ((iter = entries.next()) && !iter.done) {
|
|
1675
1714
|
const [key, value] = iter.value;
|
|
1676
|
-
// if value is a schema, remove its reference
|
|
1677
1715
|
if (typeof (value) === "object") {
|
|
1678
1716
|
previousRefId = value[$refId];
|
|
1679
|
-
$root.removeRef(previousRefId);
|
|
1680
1717
|
}
|
|
1681
1718
|
allChanges.push({
|
|
1682
1719
|
ref: previousValue,
|
|
@@ -3754,9 +3791,10 @@
|
|
|
3754
3791
|
return view.isChangeTreeVisible(ref[$changes]);
|
|
3755
3792
|
}
|
|
3756
3793
|
else {
|
|
3757
|
-
// view pass: custom tag
|
|
3794
|
+
// view pass: custom tag (bitmask)
|
|
3795
|
+
// tag is the field's stored bitmask; view.tags stores the accumulated bitmask of tags used in view.add().
|
|
3758
3796
|
const tags = view.tags?.get(ref[$changes]);
|
|
3759
|
-
return tags &&
|
|
3797
|
+
return tags != null && (tag & tags) !== 0;
|
|
3760
3798
|
}
|
|
3761
3799
|
}
|
|
3762
3800
|
// allow inherited classes to have a constructor
|
|
@@ -5570,7 +5608,7 @@
|
|
|
5570
5608
|
* List of ChangeTree's that are invisible to this view
|
|
5571
5609
|
*/
|
|
5572
5610
|
invisible = new WeakSet();
|
|
5573
|
-
tags; //
|
|
5611
|
+
tags; // bitmask of tags used to add each ChangeTree
|
|
5574
5612
|
/**
|
|
5575
5613
|
* Manual "ADD" operations for changes per ChangeTree, specific to this view.
|
|
5576
5614
|
* (This is used to force encoding a property, even if it was not changed)
|
|
@@ -5658,9 +5696,16 @@
|
|
|
5658
5696
|
changeTree.forEachChild((change, index) => {
|
|
5659
5697
|
// Do not ADD children that don't have the same tag
|
|
5660
5698
|
if (metadata &&
|
|
5661
|
-
metadata[index].tag !== undefined
|
|
5662
|
-
metadata[index].tag
|
|
5663
|
-
|
|
5699
|
+
metadata[index].tag !== undefined) {
|
|
5700
|
+
const fieldTag = metadata[index].tag;
|
|
5701
|
+
// DEFAULT_VIEW_TAG fields are visible to all clients.
|
|
5702
|
+
// Custom-tagged fields are only visible when bits overlap,
|
|
5703
|
+
// and never to default-tag clients.
|
|
5704
|
+
const tagMatch = fieldTag === DEFAULT_VIEW_TAG ||
|
|
5705
|
+
(tag !== DEFAULT_VIEW_TAG && (fieldTag & tag) !== 0);
|
|
5706
|
+
if (!tagMatch) {
|
|
5707
|
+
return;
|
|
5708
|
+
}
|
|
5664
5709
|
}
|
|
5665
5710
|
if (this.add(change.ref, tag, false)) {
|
|
5666
5711
|
isChildAdded = true;
|
|
@@ -5671,15 +5716,9 @@
|
|
|
5671
5716
|
if (!this.tags) {
|
|
5672
5717
|
this.tags = new WeakMap();
|
|
5673
5718
|
}
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
this.tags.set(changeTree, tags);
|
|
5678
|
-
}
|
|
5679
|
-
else {
|
|
5680
|
-
tags = this.tags.get(changeTree);
|
|
5681
|
-
}
|
|
5682
|
-
tags.add(tag);
|
|
5719
|
+
// Add tag bits into the bitmask stored for this ChangeTree.
|
|
5720
|
+
const currentMask = this.tags.get(changeTree) ?? 0;
|
|
5721
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5683
5722
|
// Ref: add tagged properties
|
|
5684
5723
|
metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
|
|
5685
5724
|
if (changeTree.getChange(index) !== exports.OPERATION.DELETE) {
|
|
@@ -5703,7 +5742,7 @@
|
|
|
5703
5742
|
if (op !== exports.OPERATION.DELETE &&
|
|
5704
5743
|
(isInvisible || // if "invisible", include all
|
|
5705
5744
|
tagAtIndex === undefined || // "all change" with no tag
|
|
5706
|
-
tagAtIndex === tag // tagged property
|
|
5745
|
+
(tagAtIndex === DEFAULT_VIEW_TAG || (tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0)) // tagged property
|
|
5707
5746
|
)) {
|
|
5708
5747
|
changes[index] = op;
|
|
5709
5748
|
isChildAdded = true; // FIXME: assign only once
|
|
@@ -5729,18 +5768,16 @@
|
|
|
5729
5768
|
// add parent's tag properties
|
|
5730
5769
|
if (changeTree.getChange(parentIndex) !== exports.OPERATION.DELETE) {
|
|
5731
5770
|
const changes = this.touchChanges(changeTree.ref[$refId]);
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
tags = this.tags.get(changeTree);
|
|
5771
|
+
// Only accumulate positive (custom) tags in the bitmask.
|
|
5772
|
+
// DEFAULT_VIEW_TAG = -1 has all bits set and must not be OR'd in,
|
|
5773
|
+
// as it would make every custom-tagged field appear visible.
|
|
5774
|
+
if (tag !== DEFAULT_VIEW_TAG) {
|
|
5775
|
+
if (!this.tags) {
|
|
5776
|
+
this.tags = new WeakMap();
|
|
5777
|
+
}
|
|
5778
|
+
const currentMask = this.tags.has(changeTree) ? this.tags.get(changeTree) : 0;
|
|
5779
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5742
5780
|
}
|
|
5743
|
-
tags.add(tag);
|
|
5744
5781
|
changes[parentIndex] = exports.OPERATION.ADD;
|
|
5745
5782
|
}
|
|
5746
5783
|
}
|
|
@@ -5810,18 +5847,19 @@
|
|
|
5810
5847
|
}
|
|
5811
5848
|
// remove tag
|
|
5812
5849
|
if (this.tags && this.tags.has(changeTree)) {
|
|
5813
|
-
const tags = this.tags.get(changeTree);
|
|
5814
5850
|
if (tag === undefined) {
|
|
5815
5851
|
// delete all tags
|
|
5816
5852
|
this.tags.delete(changeTree);
|
|
5817
5853
|
}
|
|
5818
5854
|
else {
|
|
5819
|
-
//
|
|
5820
|
-
tags.
|
|
5821
|
-
|
|
5822
|
-
if (tags.size === 0) {
|
|
5855
|
+
// clear the tag's bits from the bitmask
|
|
5856
|
+
const newMask = this.tags.get(changeTree) & ~tag;
|
|
5857
|
+
if (newMask === 0) {
|
|
5823
5858
|
this.tags.delete(changeTree);
|
|
5824
5859
|
}
|
|
5860
|
+
else {
|
|
5861
|
+
this.tags.set(changeTree, newMask);
|
|
5862
|
+
}
|
|
5825
5863
|
}
|
|
5826
5864
|
}
|
|
5827
5865
|
return this;
|
|
@@ -5831,7 +5869,7 @@
|
|
|
5831
5869
|
}
|
|
5832
5870
|
hasTag(ob, tag = DEFAULT_VIEW_TAG) {
|
|
5833
5871
|
const tags = this.tags?.get(ob[$changes]);
|
|
5834
|
-
return tags
|
|
5872
|
+
return tags != null && (tags & tag) !== 0;
|
|
5835
5873
|
}
|
|
5836
5874
|
clear() {
|
|
5837
5875
|
if (!this.iterable) {
|
package/build/index.mjs
CHANGED
|
@@ -815,10 +815,25 @@ const Metadata = {
|
|
|
815
815
|
});
|
|
816
816
|
}
|
|
817
817
|
metadata[$viewFieldIndexes].push(index);
|
|
818
|
-
|
|
819
|
-
|
|
818
|
+
// Populate $fieldIndexesByViewTag: for a bitmask tag, register the field
|
|
819
|
+
// index under each individual set bit so that view.add(obj, Tag.ONE) finds
|
|
820
|
+
// fields tagged @view(Tag.ONE|Tag.TWO).
|
|
821
|
+
// Negative tags (i.e. DEFAULT_VIEW_TAG = -1) are stored as-is.
|
|
822
|
+
if (tag < 0) {
|
|
823
|
+
if (!metadata[$fieldIndexesByViewTag][tag]) {
|
|
824
|
+
metadata[$fieldIndexesByViewTag][tag] = [];
|
|
825
|
+
}
|
|
826
|
+
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
for (let bits = tag; bits > 0; bits &= bits - 1) {
|
|
830
|
+
const bit = bits & (-bits); // isolate lowest set bit
|
|
831
|
+
if (!metadata[$fieldIndexesByViewTag][bit]) {
|
|
832
|
+
metadata[$fieldIndexesByViewTag][bit] = [];
|
|
833
|
+
}
|
|
834
|
+
metadata[$fieldIndexesByViewTag][bit].push(index);
|
|
835
|
+
}
|
|
820
836
|
}
|
|
821
|
-
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
822
837
|
},
|
|
823
838
|
setFields(target, fields) {
|
|
824
839
|
// for inheritance support
|
|
@@ -1369,7 +1384,8 @@ class ChangeTree {
|
|
|
1369
1384
|
key += `-${this.root.types.schemas.get(parentConstructor)}`;
|
|
1370
1385
|
}
|
|
1371
1386
|
key += `-${parentIndex}`;
|
|
1372
|
-
const
|
|
1387
|
+
const parentMetadata = parentConstructor?.[Symbol.metadata];
|
|
1388
|
+
const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentMetadata, parentIndex);
|
|
1373
1389
|
this.isFiltered = parent[$changes].isFiltered // in case parent is already filtered
|
|
1374
1390
|
|| this.root.types.parentFiltered[key]
|
|
1375
1391
|
|| fieldHasViewTag;
|
|
@@ -1378,9 +1394,22 @@ class ChangeTree {
|
|
|
1378
1394
|
// when it's available, we need to enqueue the "changes" changeset into the "filteredChanges" changeset.
|
|
1379
1395
|
//
|
|
1380
1396
|
if (this.isFiltered) {
|
|
1397
|
+
//
|
|
1398
|
+
// Children of a `@view(N)` collection (non-default tag) inherit
|
|
1399
|
+
// visibility from their parent, so items pushed/set after the
|
|
1400
|
+
// initial `view.add(state, N)` show up automatically.
|
|
1401
|
+
//
|
|
1402
|
+
// Default-tag `@view()` collections deliberately keep per-item
|
|
1403
|
+
// gating — `view.add(item)` is required to opt each one in.
|
|
1404
|
+
//
|
|
1405
|
+
// The `parentMetadata[parentIndex].tag` access is safe inside
|
|
1406
|
+
// this branch: the OR's short-circuit means we only reach it
|
|
1407
|
+
// when `fieldHasViewTag` is true, which guarantees the metadata
|
|
1408
|
+
// entry and its `tag` property exist.
|
|
1409
|
+
//
|
|
1381
1410
|
this.isVisibilitySharedWithParent = (parentChangeTree.isFiltered &&
|
|
1382
1411
|
typeof (refType) !== "string" &&
|
|
1383
|
-
!fieldHasViewTag);
|
|
1412
|
+
(!fieldHasViewTag || (parentIsCollection && parentMetadata[parentIndex].tag !== DEFAULT_VIEW_TAG)));
|
|
1384
1413
|
if (!this.filteredChanges) {
|
|
1385
1414
|
this.filteredChanges = createChangeSet();
|
|
1386
1415
|
this.allFilteredChanges = createChangeSet();
|
|
@@ -1660,17 +1689,25 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1660
1689
|
if (previousValue) {
|
|
1661
1690
|
let previousRefId = previousValue[$refId];
|
|
1662
1691
|
if (previousRefId !== undefined && refId !== previousRefId) {
|
|
1692
|
+
// Collection field replaced by a different instance.
|
|
1663
1693
|
//
|
|
1664
|
-
//
|
|
1665
|
-
//
|
|
1694
|
+
// Don't decrement children here: GC (`garbageCollectDeletedRefs`)
|
|
1695
|
+
// removes them once the previous collection's refId hits zero.
|
|
1696
|
+
// Doing it here too would double-decrement a *shared* child and
|
|
1697
|
+
// drop it while still referenced ("refId not found").
|
|
1698
|
+
if ((operation & OPERATION.DELETE) !== OPERATION.DELETE) {
|
|
1699
|
+
// Replacement not tagged DELETE (e.g. pending ADD not upgraded
|
|
1700
|
+
// to DELETE_AND_ADD), so the previous refId wasn't decremented
|
|
1701
|
+
// above. Release it here, else it never gets GC'd (leak).
|
|
1702
|
+
$root.removeRef(previousRefId);
|
|
1703
|
+
}
|
|
1704
|
+
// enqueue onRemove callbacks for the previous collection's children.
|
|
1666
1705
|
const entries = previousValue.entries();
|
|
1667
1706
|
let iter;
|
|
1668
1707
|
while ((iter = entries.next()) && !iter.done) {
|
|
1669
1708
|
const [key, value] = iter.value;
|
|
1670
|
-
// if value is a schema, remove its reference
|
|
1671
1709
|
if (typeof (value) === "object") {
|
|
1672
1710
|
previousRefId = value[$refId];
|
|
1673
|
-
$root.removeRef(previousRefId);
|
|
1674
1711
|
}
|
|
1675
1712
|
allChanges.push({
|
|
1676
1713
|
ref: previousValue,
|
|
@@ -3748,9 +3785,10 @@ class Schema {
|
|
|
3748
3785
|
return view.isChangeTreeVisible(ref[$changes]);
|
|
3749
3786
|
}
|
|
3750
3787
|
else {
|
|
3751
|
-
// view pass: custom tag
|
|
3788
|
+
// view pass: custom tag (bitmask)
|
|
3789
|
+
// tag is the field's stored bitmask; view.tags stores the accumulated bitmask of tags used in view.add().
|
|
3752
3790
|
const tags = view.tags?.get(ref[$changes]);
|
|
3753
|
-
return tags &&
|
|
3791
|
+
return tags != null && (tag & tags) !== 0;
|
|
3754
3792
|
}
|
|
3755
3793
|
}
|
|
3756
3794
|
// allow inherited classes to have a constructor
|
|
@@ -5564,7 +5602,7 @@ class StateView {
|
|
|
5564
5602
|
* List of ChangeTree's that are invisible to this view
|
|
5565
5603
|
*/
|
|
5566
5604
|
invisible = new WeakSet();
|
|
5567
|
-
tags; //
|
|
5605
|
+
tags; // bitmask of tags used to add each ChangeTree
|
|
5568
5606
|
/**
|
|
5569
5607
|
* Manual "ADD" operations for changes per ChangeTree, specific to this view.
|
|
5570
5608
|
* (This is used to force encoding a property, even if it was not changed)
|
|
@@ -5652,9 +5690,16 @@ class StateView {
|
|
|
5652
5690
|
changeTree.forEachChild((change, index) => {
|
|
5653
5691
|
// Do not ADD children that don't have the same tag
|
|
5654
5692
|
if (metadata &&
|
|
5655
|
-
metadata[index].tag !== undefined
|
|
5656
|
-
metadata[index].tag
|
|
5657
|
-
|
|
5693
|
+
metadata[index].tag !== undefined) {
|
|
5694
|
+
const fieldTag = metadata[index].tag;
|
|
5695
|
+
// DEFAULT_VIEW_TAG fields are visible to all clients.
|
|
5696
|
+
// Custom-tagged fields are only visible when bits overlap,
|
|
5697
|
+
// and never to default-tag clients.
|
|
5698
|
+
const tagMatch = fieldTag === DEFAULT_VIEW_TAG ||
|
|
5699
|
+
(tag !== DEFAULT_VIEW_TAG && (fieldTag & tag) !== 0);
|
|
5700
|
+
if (!tagMatch) {
|
|
5701
|
+
return;
|
|
5702
|
+
}
|
|
5658
5703
|
}
|
|
5659
5704
|
if (this.add(change.ref, tag, false)) {
|
|
5660
5705
|
isChildAdded = true;
|
|
@@ -5665,15 +5710,9 @@ class StateView {
|
|
|
5665
5710
|
if (!this.tags) {
|
|
5666
5711
|
this.tags = new WeakMap();
|
|
5667
5712
|
}
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
this.tags.set(changeTree, tags);
|
|
5672
|
-
}
|
|
5673
|
-
else {
|
|
5674
|
-
tags = this.tags.get(changeTree);
|
|
5675
|
-
}
|
|
5676
|
-
tags.add(tag);
|
|
5713
|
+
// Add tag bits into the bitmask stored for this ChangeTree.
|
|
5714
|
+
const currentMask = this.tags.get(changeTree) ?? 0;
|
|
5715
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5677
5716
|
// Ref: add tagged properties
|
|
5678
5717
|
metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
|
|
5679
5718
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
@@ -5697,7 +5736,7 @@ class StateView {
|
|
|
5697
5736
|
if (op !== OPERATION.DELETE &&
|
|
5698
5737
|
(isInvisible || // if "invisible", include all
|
|
5699
5738
|
tagAtIndex === undefined || // "all change" with no tag
|
|
5700
|
-
tagAtIndex === tag // tagged property
|
|
5739
|
+
(tagAtIndex === DEFAULT_VIEW_TAG || (tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0)) // tagged property
|
|
5701
5740
|
)) {
|
|
5702
5741
|
changes[index] = op;
|
|
5703
5742
|
isChildAdded = true; // FIXME: assign only once
|
|
@@ -5723,18 +5762,16 @@ class StateView {
|
|
|
5723
5762
|
// add parent's tag properties
|
|
5724
5763
|
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
5725
5764
|
const changes = this.touchChanges(changeTree.ref[$refId]);
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
tags = this.tags.get(changeTree);
|
|
5765
|
+
// Only accumulate positive (custom) tags in the bitmask.
|
|
5766
|
+
// DEFAULT_VIEW_TAG = -1 has all bits set and must not be OR'd in,
|
|
5767
|
+
// as it would make every custom-tagged field appear visible.
|
|
5768
|
+
if (tag !== DEFAULT_VIEW_TAG) {
|
|
5769
|
+
if (!this.tags) {
|
|
5770
|
+
this.tags = new WeakMap();
|
|
5771
|
+
}
|
|
5772
|
+
const currentMask = this.tags.has(changeTree) ? this.tags.get(changeTree) : 0;
|
|
5773
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5736
5774
|
}
|
|
5737
|
-
tags.add(tag);
|
|
5738
5775
|
changes[parentIndex] = OPERATION.ADD;
|
|
5739
5776
|
}
|
|
5740
5777
|
}
|
|
@@ -5804,18 +5841,19 @@ class StateView {
|
|
|
5804
5841
|
}
|
|
5805
5842
|
// remove tag
|
|
5806
5843
|
if (this.tags && this.tags.has(changeTree)) {
|
|
5807
|
-
const tags = this.tags.get(changeTree);
|
|
5808
5844
|
if (tag === undefined) {
|
|
5809
5845
|
// delete all tags
|
|
5810
5846
|
this.tags.delete(changeTree);
|
|
5811
5847
|
}
|
|
5812
5848
|
else {
|
|
5813
|
-
//
|
|
5814
|
-
tags.
|
|
5815
|
-
|
|
5816
|
-
if (tags.size === 0) {
|
|
5849
|
+
// clear the tag's bits from the bitmask
|
|
5850
|
+
const newMask = this.tags.get(changeTree) & ~tag;
|
|
5851
|
+
if (newMask === 0) {
|
|
5817
5852
|
this.tags.delete(changeTree);
|
|
5818
5853
|
}
|
|
5854
|
+
else {
|
|
5855
|
+
this.tags.set(changeTree, newMask);
|
|
5856
|
+
}
|
|
5819
5857
|
}
|
|
5820
5858
|
}
|
|
5821
5859
|
return this;
|
|
@@ -5825,7 +5863,7 @@ class StateView {
|
|
|
5825
5863
|
}
|
|
5826
5864
|
hasTag(ob, tag = DEFAULT_VIEW_TAG) {
|
|
5827
5865
|
const tags = this.tags?.get(ob[$changes]);
|
|
5828
|
-
return tags
|
|
5866
|
+
return tags != null && (tags & tag) !== 0;
|
|
5829
5867
|
}
|
|
5830
5868
|
clear() {
|
|
5831
5869
|
if (!this.iterable) {
|