@colyseus/schema 4.0.23 → 4.0.25

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/index.js CHANGED
@@ -1235,12 +1235,26 @@
1235
1235
  }
1236
1236
  return;
1237
1237
  }
1238
- const changeSet = (this.filteredChanges !== undefined)
1238
+ // Mirror `change()`: a field's add/delete must target the same
1239
+ // changeset family (filtered vs non-filtered). Otherwise
1240
+ // `deleteOperationAtIndex` falls through to its "find last
1241
+ // operation" branch and evicts an unrelated sibling field —
1242
+ // surfaced as encodeAll() dropping a non-@view field after a
1243
+ // sibling @view field is set to undefined on a Schema with
1244
+ // mixed @view / non-@view fields (filteredChanges defined,
1245
+ // isFiltered false).
1246
+ const isFiltered = this.isFiltered || (this.metadata?.[index]?.tag !== undefined);
1247
+ const changeSet = (isFiltered)
1239
1248
  ? this.filteredChanges
1240
1249
  : this.changes;
1241
1250
  this.indexedOperations[index] = operation ?? exports.OPERATION.DELETE;
1242
1251
  setOperationAtIndex(changeSet, index);
1243
- deleteOperationAtIndex(this.allChanges, allChangesIndex);
1252
+ if (isFiltered) {
1253
+ deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
1254
+ }
1255
+ else {
1256
+ deleteOperationAtIndex(this.allChanges, allChangesIndex);
1257
+ }
1244
1258
  const previousValue = this.getValue(index);
1245
1259
  // remove `root` reference
1246
1260
  if (previousValue && previousValue[$changes]) {
@@ -1256,11 +1270,7 @@
1256
1270
  //
1257
1271
  this.root?.remove(previousValue[$changes]);
1258
1272
  }
1259
- //
1260
- // FIXME: this is looking a ugly and repeated
1261
- //
1262
- if (this.filteredChanges !== undefined) {
1263
- deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
1273
+ if (isFiltered) {
1264
1274
  this.root?.enqueueChangeTree(this, 'filteredChanges');
1265
1275
  }
1266
1276
  else {
@@ -1365,7 +1375,8 @@
1365
1375
  key += `-${this.root.types.schemas.get(parentConstructor)}`;
1366
1376
  }
1367
1377
  key += `-${parentIndex}`;
1368
- const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentConstructor?.[Symbol.metadata], parentIndex);
1378
+ const parentMetadata = parentConstructor?.[Symbol.metadata];
1379
+ const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentMetadata, parentIndex);
1369
1380
  this.isFiltered = parent[$changes].isFiltered // in case parent is already filtered
1370
1381
  || this.root.types.parentFiltered[key]
1371
1382
  || fieldHasViewTag;
@@ -1374,9 +1385,22 @@
1374
1385
  // when it's available, we need to enqueue the "changes" changeset into the "filteredChanges" changeset.
1375
1386
  //
1376
1387
  if (this.isFiltered) {
1388
+ //
1389
+ // Children of a `@view(N)` collection (non-default tag) inherit
1390
+ // visibility from their parent, so items pushed/set after the
1391
+ // initial `view.add(state, N)` show up automatically.
1392
+ //
1393
+ // Default-tag `@view()` collections deliberately keep per-item
1394
+ // gating — `view.add(item)` is required to opt each one in.
1395
+ //
1396
+ // The `parentMetadata[parentIndex].tag` access is safe inside
1397
+ // this branch: the OR's short-circuit means we only reach it
1398
+ // when `fieldHasViewTag` is true, which guarantees the metadata
1399
+ // entry and its `tag` property exist.
1400
+ //
1377
1401
  this.isVisibilitySharedWithParent = (parentChangeTree.isFiltered &&
1378
1402
  typeof (refType) !== "string" &&
1379
- !fieldHasViewTag);
1403
+ (!fieldHasViewTag || (parentIsCollection && parentMetadata[parentIndex].tag !== DEFAULT_VIEW_TAG)));
1380
1404
  if (!this.filteredChanges) {
1381
1405
  this.filteredChanges = createChangeSet();
1382
1406
  this.allFilteredChanges = createChangeSet();
package/build/index.mjs CHANGED
@@ -1229,12 +1229,26 @@ class ChangeTree {
1229
1229
  }
1230
1230
  return;
1231
1231
  }
1232
- const changeSet = (this.filteredChanges !== undefined)
1232
+ // Mirror `change()`: a field's add/delete must target the same
1233
+ // changeset family (filtered vs non-filtered). Otherwise
1234
+ // `deleteOperationAtIndex` falls through to its "find last
1235
+ // operation" branch and evicts an unrelated sibling field —
1236
+ // surfaced as encodeAll() dropping a non-@view field after a
1237
+ // sibling @view field is set to undefined on a Schema with
1238
+ // mixed @view / non-@view fields (filteredChanges defined,
1239
+ // isFiltered false).
1240
+ const isFiltered = this.isFiltered || (this.metadata?.[index]?.tag !== undefined);
1241
+ const changeSet = (isFiltered)
1233
1242
  ? this.filteredChanges
1234
1243
  : this.changes;
1235
1244
  this.indexedOperations[index] = operation ?? OPERATION.DELETE;
1236
1245
  setOperationAtIndex(changeSet, index);
1237
- deleteOperationAtIndex(this.allChanges, allChangesIndex);
1246
+ if (isFiltered) {
1247
+ deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
1248
+ }
1249
+ else {
1250
+ deleteOperationAtIndex(this.allChanges, allChangesIndex);
1251
+ }
1238
1252
  const previousValue = this.getValue(index);
1239
1253
  // remove `root` reference
1240
1254
  if (previousValue && previousValue[$changes]) {
@@ -1250,11 +1264,7 @@ class ChangeTree {
1250
1264
  //
1251
1265
  this.root?.remove(previousValue[$changes]);
1252
1266
  }
1253
- //
1254
- // FIXME: this is looking a ugly and repeated
1255
- //
1256
- if (this.filteredChanges !== undefined) {
1257
- deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
1267
+ if (isFiltered) {
1258
1268
  this.root?.enqueueChangeTree(this, 'filteredChanges');
1259
1269
  }
1260
1270
  else {
@@ -1359,7 +1369,8 @@ class ChangeTree {
1359
1369
  key += `-${this.root.types.schemas.get(parentConstructor)}`;
1360
1370
  }
1361
1371
  key += `-${parentIndex}`;
1362
- const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentConstructor?.[Symbol.metadata], parentIndex);
1372
+ const parentMetadata = parentConstructor?.[Symbol.metadata];
1373
+ const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentMetadata, parentIndex);
1363
1374
  this.isFiltered = parent[$changes].isFiltered // in case parent is already filtered
1364
1375
  || this.root.types.parentFiltered[key]
1365
1376
  || fieldHasViewTag;
@@ -1368,9 +1379,22 @@ class ChangeTree {
1368
1379
  // when it's available, we need to enqueue the "changes" changeset into the "filteredChanges" changeset.
1369
1380
  //
1370
1381
  if (this.isFiltered) {
1382
+ //
1383
+ // Children of a `@view(N)` collection (non-default tag) inherit
1384
+ // visibility from their parent, so items pushed/set after the
1385
+ // initial `view.add(state, N)` show up automatically.
1386
+ //
1387
+ // Default-tag `@view()` collections deliberately keep per-item
1388
+ // gating — `view.add(item)` is required to opt each one in.
1389
+ //
1390
+ // The `parentMetadata[parentIndex].tag` access is safe inside
1391
+ // this branch: the OR's short-circuit means we only reach it
1392
+ // when `fieldHasViewTag` is true, which guarantees the metadata
1393
+ // entry and its `tag` property exist.
1394
+ //
1371
1395
  this.isVisibilitySharedWithParent = (parentChangeTree.isFiltered &&
1372
1396
  typeof (refType) !== "string" &&
1373
- !fieldHasViewTag);
1397
+ (!fieldHasViewTag || (parentIsCollection && parentMetadata[parentIndex].tag !== DEFAULT_VIEW_TAG)));
1374
1398
  if (!this.filteredChanges) {
1375
1399
  this.filteredChanges = createChangeSet();
1376
1400
  this.allFilteredChanges = createChangeSet();