@ifc-lite/mutations 1.23.0 → 1.24.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.
@@ -1 +1 @@
1
- {"version":3,"file":"mutable-property-view.d.ts","sourceRoot":"","sources":["../src/mutable-property-view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mutable-property-view.d.ts","sourceRoot":"","sources":["../src/mutable-property-view.ts"],"names":[],"mappings":"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"}
@@ -3,10 +3,12 @@
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
4
  import { PropertyValueType, QuantityType } from '@ifc-lite/data';
5
5
  import { propertyKey, quantityKey, attributeKey, generateMutationId } from './types.js';
6
+ import { collectEffectiveChanges } from './effective-changes.js';
6
7
  export class MutablePropertyView {
7
8
  baseTable;
8
9
  onDemandExtractor = null;
9
10
  quantityExtractor = null;
11
+ attributeExtractor = null;
10
12
  propertyMutations = new Map();
11
13
  quantityMutations = new Map();
12
14
  /**
@@ -28,6 +30,39 @@ export class MutablePropertyView {
28
30
  typeMutations = new Map(); // entityId -> retype intent
29
31
  newEntities = new Map();
30
32
  tombstones = new Set();
33
+ /**
34
+ * Ids `createEntity` allocated and `deleteEntity` then forgot (removed from
35
+ * `newEntities`, per that method's "existing entities are tombstoned; new
36
+ * entities are simply forgotten" contract). Tracked separately so
37
+ * `getEffectiveChanges()` / `collectEffectiveChanges` can tell "overlay-created
38
+ * then forgotten" apart from "an ordinary source-buffer entity" — both are
39
+ * otherwise indistinguishable, being simply absent from `newEntities`.
40
+ * `restoreNewEntity` (the undo-of-delete counterpart) clears the id back out.
41
+ */
42
+ forgottenCreatedEntities = new Set();
43
+ /**
44
+ * Snapshot of a forgotten-created entity's overlay rows, stashed by
45
+ * `deleteEntity` and restored by `restoreNewEntity`.
46
+ *
47
+ * `deleteEntity` on an overlay-created entity does more than drop it from
48
+ * `newEntities` — it also PURGES every other overlay entry the entity left
49
+ * behind (property/quantity/attribute/positional/type mutations, its
50
+ * `newPsets`/`newQsets` entries, and its own `mutationHistory` records).
51
+ * Without that purge, an entity that was created, edited, then deleted
52
+ * before export left a dangling reference: `StepExporter` derives its
53
+ * property/quantity work list from `getMutations()` (the append-only
54
+ * history) and reads `getForEntity()` / `getQuantitiesForEntity()` straight
55
+ * off `newPsets` / `newQsets` — neither of which the review-side
56
+ * `forgottenCreatedEntities` filter in `effective-changes.ts` touches. The
57
+ * review dialog looked clean while the exported file still contained an
58
+ * `IFCPROPERTYSET` + `IFCRELDEFINESBYPROPERTIES` pointing at an expressId
59
+ * that was never actually created (maintainer finding on #1967).
60
+ *
61
+ * The purged data is captured here, not discarded, because `restoreNewEntity`
62
+ * (undo of the delete) must bring it all back — rows AND count AND what the
63
+ * exporter would see — not just re-add the bare `NewEntity` record.
64
+ */
65
+ forgottenEntityOverlay = new Map();
31
66
  /**
32
67
  * Overlay-entity → source-entity aliases for property/quantity reads.
33
68
  *
@@ -143,6 +178,16 @@ export class MutablePropertyView {
143
178
  setQuantityExtractor(extractor) {
144
179
  this.quantityExtractor = extractor;
145
180
  }
181
+ /**
182
+ * Set the base entity-attribute extractor (Name, Description, ObjectType,
183
+ * Tag, ...), used only to resolve `previousValue` in `getEffectiveChanges()`.
184
+ * Without one, attribute `previousValue` falls back to whatever `oldValue`
185
+ * the overlay entry itself carries — which undo can leave stale/absent (see
186
+ * `getEffectiveChanges()` doc).
187
+ */
188
+ setAttributeExtractor(extractor) {
189
+ this.attributeExtractor = extractor;
190
+ }
146
191
  /**
147
192
  * Get base properties for an entity (before mutations)
148
193
  * Uses on-demand extraction if available, otherwise falls back to base table.
@@ -385,7 +430,23 @@ export class MutablePropertyView {
385
430
  if (oldValue === null && !inNewPset) {
386
431
  return null; // Property doesn't exist
387
432
  }
388
- this.setPropertyMutation(entityId, key, { operation: 'DELETE' });
433
+ // A DELETE marker in `propertyMutations` only earns its keep when it is
434
+ // masking a value that genuinely exists in the base data — that's what
435
+ // `getForEntity`'s base-pset walk (and `collectPropertyChanges`) needs to
436
+ // skip. A purely in-session property (added via `setProperty`/
437
+ // `createPropertySet`, never in base) has nothing to mask: leaving a
438
+ // DELETE marker for it kept `collectModifiedEntityIds()` counting this
439
+ // entity as modified with zero effective rows to show for it (the same
440
+ // class of bug as the `newPsets` empty-map leak above, #1967 finding
441
+ // 2(b)) — so drop the mutation entry outright instead.
442
+ const basePsets = this.getBasePropertiesForEntity(entityId);
443
+ const propExistsInBase = basePsets.some(p => p.name === psetName && p.properties.some(prop => prop.name === propName));
444
+ if (propExistsInBase) {
445
+ this.setPropertyMutation(entityId, key, { operation: 'DELETE' });
446
+ }
447
+ else {
448
+ this.deletePropertyMutation(entityId, key);
449
+ }
389
450
  // Keep the verbatim newPsets read path (getForEntity / STEP export)
390
451
  // consistent with getPropertyValue when the prop lives in an in-session
391
452
  // pset: splice it out, and drop the pset if it becomes empty.
@@ -395,6 +456,14 @@ export class MutablePropertyView {
395
456
  newPset.properties = newPset.properties.filter(p => p.name !== propName);
396
457
  if (newPset.properties.length === 0) {
397
458
  entityPsets.delete(psetName);
459
+ // An empty Map is still truthy, so leaving it in `newPsets` would keep
460
+ // `collectModifiedEntityIds()` / `hasChanges(entityId)` reporting this
461
+ // entity as modified with zero rows to show for it (maintainer finding
462
+ // 2(b) on #1967 — deleting the last property of an auto-created pset
463
+ // never cleared the entity out of `newPsets`).
464
+ if (entityPsets.size === 0) {
465
+ this.newPsets.delete(entityId);
466
+ }
398
467
  }
399
468
  }
400
469
  const mutation = {
@@ -459,16 +528,41 @@ export class MutablePropertyView {
459
528
  * Delete an entire property set
460
529
  */
461
530
  deletePropertySet(entityId, psetName) {
462
- this.deletedPsets.add(`${entityId}:${psetName}`);
463
531
  // Also remove from new psets if it was created in this session
464
532
  const entityPsets = this.newPsets.get(entityId);
465
- if (entityPsets) {
533
+ const inSessionPset = entityPsets?.get(psetName);
534
+ if (entityPsets && inSessionPset) {
466
535
  entityPsets.delete(psetName);
536
+ // An empty Map is still truthy, so leaving it in `newPsets` would keep
537
+ // `collectModifiedEntityIds()` / `hasChanges(entityId)` reporting this
538
+ // entity as modified with zero rows to show for it (maintainer finding
539
+ // 2(b) on #1967 — the `newPsets` empty-map leak that also affects
540
+ // `deleteProperty`).
541
+ if (entityPsets.size === 0) {
542
+ this.newPsets.delete(entityId);
543
+ }
544
+ // The individual SET mutations `createPropertySet` recorded for this
545
+ // pset's properties have nothing to mask either — same argument as
546
+ // `deleteProperty`'s in-session branch below, applied to every
547
+ // property this in-session pset carried, so drop each entry outright
548
+ // instead of leaving it orphaned in `propertyMutations`.
549
+ for (const prop of inSessionPset.properties) {
550
+ const key = propertyKey(entityId, psetName, prop.name);
551
+ this.deletePropertyMutation(entityId, key);
552
+ }
467
553
  }
468
- // Mark all properties as deleted
554
+ // A DELETE marker in `deletedPsets` only earns its keep when it is
555
+ // masking a pset that genuinely exists in the base data — same argument
556
+ // as `deleteProperty` one level down (see the comment above its own
557
+ // base-existence check): a purely in-session pset (added via
558
+ // `createPropertySet`, never in the base file) has nothing to mask, so
559
+ // dropping the pset above already nets to nothing and there is no
560
+ // deletion to report. Recording it as deleted here told the export
561
+ // review a pset would be removed when the net change was zero.
469
562
  const existingPsets = this.getBasePropertiesForEntity(entityId);
470
563
  const pset = existingPsets.find(p => p.name === psetName);
471
564
  if (pset) {
565
+ this.deletedPsets.add(`${entityId}:${psetName}`);
472
566
  for (const prop of pset.properties) {
473
567
  const key = propertyKey(entityId, psetName, prop.name);
474
568
  this.setPropertyMutation(entityId, key, { operation: 'DELETE' });
@@ -892,7 +986,21 @@ export class MutablePropertyView {
892
986
  deleteEntity(expressId) {
893
987
  if (this.newEntities.has(expressId)) {
894
988
  this.newEntities.delete(expressId);
989
+ // Both sets are needed: `tombstones` is what the unified isDeleted() /
990
+ // getEffectiveEntityIndex() answer from (#2036), while
991
+ // `forgottenCreatedEntities` is what collectEffectiveChanges()'s row
992
+ // filter uses to drop ALL rows for a created-then-deleted entity
993
+ // (create and delete cancel out) rather than keeping an entity-deleted
994
+ // row the way a tombstoned source entity does.
895
995
  this.tombstones.add(expressId);
996
+ this.forgottenCreatedEntities.add(expressId);
997
+ // Purge every other overlay trace of this entity — property/quantity/
998
+ // attribute/positional/type mutations, `newPsets`/`newQsets`, and this
999
+ // entity's own mutation-history records — BEFORE pushing the
1000
+ // DELETE_ENTITY record below, so that record is the only history entry
1001
+ // left for this id. See `forgottenEntityOverlay`'s doc for why this is
1002
+ // a stash-and-remove rather than an outright discard.
1003
+ this.stashAndPurgeEntityOverlay(expressId);
896
1004
  this.mutationHistory.push({
897
1005
  id: generateMutationId(),
898
1006
  type: 'DELETE_ENTITY',
@@ -974,16 +1082,162 @@ export class MutablePropertyView {
974
1082
  */
975
1083
  restoreNewEntity(entity) {
976
1084
  this.newEntities.set(entity.expressId, entity);
977
- // `deleteEntity` tombstones an overlay-created entity as well as forgetting
978
- // it, so the inverse has to lift the tombstone — otherwise the restored
979
- // record is live in `newEntities` and still deleted according to
980
- // `isDeleted`, and the export drops it again.
1085
+ // `deleteEntity` both tombstones an overlay-created entity (for the
1086
+ // unified isDeleted() / getEffectiveEntityIndex() answer) and forgets it
1087
+ // (for collectEffectiveChanges()'s row filter), so the inverse has to
1088
+ // clear both — otherwise the restored record is either still "deleted"
1089
+ // per isDeleted() (stale tombstone) or still invisible to the review
1090
+ // diff (stale forgotten-entity mark).
981
1091
  this.tombstones.delete(entity.expressId);
1092
+ this.forgottenCreatedEntities.delete(entity.expressId);
982
1093
  // Without this the next createEntity() can hand out the same id and
983
1094
  // overwrite the restored entity.
984
1095
  if (entity.expressId > this.nextAllocatedId) {
985
1096
  this.nextAllocatedId = entity.expressId;
986
1097
  }
1098
+ // Bring back whatever `deleteEntity` purged (property/quantity/attribute
1099
+ // mutations, newPsets/newQsets, history) — a no-op if this entity was
1100
+ // never forgotten (e.g. a plain create with nothing purged).
1101
+ this.unstashEntityOverlay(entity.expressId);
1102
+ }
1103
+ /**
1104
+ * Move every current overlay entry for `expressId` out of the live maps
1105
+ * and into `forgottenEntityOverlay`, and drop this entity's own records
1106
+ * from `mutationHistory`. Called by `deleteEntity` when it forgets a
1107
+ * created entity. Only stashes a key if something was actually captured,
1108
+ * so `unstashEntityOverlay` on a plain (never-edited) create is a no-op.
1109
+ */
1110
+ stashAndPurgeEntityOverlay(expressId) {
1111
+ const stash = {
1112
+ propertyEntries: [],
1113
+ quantityEntries: [],
1114
+ attributeEntries: [],
1115
+ positionalAttrs: null,
1116
+ typeMutation: null,
1117
+ newPsets: null,
1118
+ newQsets: null,
1119
+ deletedPsetKeys: [],
1120
+ deletedQsetKeys: [],
1121
+ historyEntries: [],
1122
+ };
1123
+ for (const key of Array.from(this.propertyKeysByEntity.get(expressId) ?? [])) {
1124
+ const mutation = this.propertyMutations.get(key);
1125
+ if (mutation)
1126
+ stash.propertyEntries.push([key, mutation]);
1127
+ this.deletePropertyMutation(expressId, key);
1128
+ }
1129
+ for (const key of Array.from(this.quantityKeysByEntity.get(expressId) ?? [])) {
1130
+ const mutation = this.quantityMutations.get(key);
1131
+ if (mutation)
1132
+ stash.quantityEntries.push([key, mutation]);
1133
+ this.deleteQuantityMutation(expressId, key);
1134
+ }
1135
+ for (const key of Array.from(this.attributeKeysByEntity.get(expressId) ?? [])) {
1136
+ const mutation = this.attributeMutations.get(key);
1137
+ if (mutation)
1138
+ stash.attributeEntries.push([key, mutation]);
1139
+ this.deleteAttributeMutation(expressId, key);
1140
+ }
1141
+ const positional = this.positionalAttrMutations.get(expressId);
1142
+ if (positional) {
1143
+ stash.positionalAttrs = new Map(positional);
1144
+ this.positionalAttrMutations.delete(expressId);
1145
+ }
1146
+ const typeMutation = this.typeMutations.get(expressId);
1147
+ if (typeMutation) {
1148
+ stash.typeMutation = typeMutation;
1149
+ this.typeMutations.delete(expressId);
1150
+ }
1151
+ const psets = this.newPsets.get(expressId);
1152
+ if (psets) {
1153
+ stash.newPsets = new Map(psets);
1154
+ this.newPsets.delete(expressId);
1155
+ }
1156
+ const qsets = this.newQsets.get(expressId);
1157
+ if (qsets) {
1158
+ stash.newQsets = new Map(qsets);
1159
+ this.newQsets.delete(expressId);
1160
+ }
1161
+ const psetPrefix = `${expressId}:`;
1162
+ for (const key of Array.from(this.deletedPsets)) {
1163
+ if (!key.startsWith(psetPrefix))
1164
+ continue;
1165
+ stash.deletedPsetKeys.push(key);
1166
+ this.deletedPsets.delete(key);
1167
+ }
1168
+ for (const key of Array.from(this.deletedQsets)) {
1169
+ if (!key.startsWith(psetPrefix))
1170
+ continue;
1171
+ stash.deletedQsetKeys.push(key);
1172
+ this.deletedQsets.delete(key);
1173
+ }
1174
+ const keptHistory = [];
1175
+ for (const mutation of this.mutationHistory) {
1176
+ if (mutation.entityId === expressId) {
1177
+ stash.historyEntries.push(mutation);
1178
+ }
1179
+ else {
1180
+ keptHistory.push(mutation);
1181
+ }
1182
+ }
1183
+ this.mutationHistory = keptHistory;
1184
+ const hasStashedData = stash.propertyEntries.length > 0 ||
1185
+ stash.quantityEntries.length > 0 ||
1186
+ stash.attributeEntries.length > 0 ||
1187
+ stash.positionalAttrs !== null ||
1188
+ stash.typeMutation !== null ||
1189
+ stash.newPsets !== null ||
1190
+ stash.newQsets !== null ||
1191
+ stash.deletedPsetKeys.length > 0 ||
1192
+ stash.deletedQsetKeys.length > 0 ||
1193
+ stash.historyEntries.length > 0;
1194
+ if (hasStashedData) {
1195
+ this.forgottenEntityOverlay.set(expressId, stash);
1196
+ }
1197
+ }
1198
+ /**
1199
+ * Reverse `stashAndPurgeEntityOverlay`: put everything `deleteEntity`
1200
+ * purged back into the live overlay maps. Called by `restoreNewEntity`.
1201
+ * A no-op if nothing was stashed for `expressId`.
1202
+ */
1203
+ unstashEntityOverlay(expressId) {
1204
+ const stash = this.forgottenEntityOverlay.get(expressId);
1205
+ if (!stash)
1206
+ return;
1207
+ this.forgottenEntityOverlay.delete(expressId);
1208
+ for (const [key, mutation] of stash.propertyEntries)
1209
+ this.setPropertyMutation(expressId, key, mutation);
1210
+ for (const [key, mutation] of stash.quantityEntries)
1211
+ this.setQuantityMutation(expressId, key, mutation);
1212
+ for (const [key, mutation] of stash.attributeEntries)
1213
+ this.setAttributeMutation(expressId, key, mutation);
1214
+ if (stash.positionalAttrs)
1215
+ this.positionalAttrMutations.set(expressId, stash.positionalAttrs);
1216
+ if (stash.typeMutation)
1217
+ this.typeMutations.set(expressId, stash.typeMutation);
1218
+ if (stash.newPsets)
1219
+ this.newPsets.set(expressId, stash.newPsets);
1220
+ if (stash.newQsets)
1221
+ this.newQsets.set(expressId, stash.newQsets);
1222
+ for (const key of stash.deletedPsetKeys)
1223
+ this.deletedPsets.add(key);
1224
+ for (const key of stash.deletedQsetKeys)
1225
+ this.deletedQsets.add(key);
1226
+ // The DELETE_ENTITY `deleteEntity` pushed AFTER the purge (so it would be
1227
+ // the only history entry left for this id) is superseded by this
1228
+ // restore — same reasoning `forgottenCreatedEntities` already applies to
1229
+ // collectEffectiveChanges()'s row filter one layer down: a create and
1230
+ // its delete cancel, they don't survive as a create followed by a delete.
1231
+ // Re-appending the stashed CREATE_ENTITY/CREATE_PROPERTY records BEHIND
1232
+ // that DELETE_ENTITY (the old bug) reordered mutationHistory to
1233
+ // DELETE_ENTITY,CREATE_ENTITY,..., which defeats applyMutations()'s
1234
+ // skippedCreateIds guard (#2036) on replay: the DELETE_ENTITY is seen
1235
+ // before the CREATE_ENTITY it should pair with, so it tombstones an id
1236
+ // that was never really deleted — silent data loss through
1237
+ // exportMutations()/importMutations() on a published package.
1238
+ this.mutationHistory = this.mutationHistory.filter(m => !(m.entityId === expressId && m.type === 'DELETE_ENTITY'));
1239
+ if (stash.historyEntries.length > 0)
1240
+ this.mutationHistory.push(...stash.historyEntries);
987
1241
  }
988
1242
  /**
989
1243
  * Every express id this session deleted — source-buffer entities AND ones it
@@ -1044,6 +1298,12 @@ export class MutablePropertyView {
1044
1298
  qset.quantities = qset.quantities.filter(q => q.name !== quantName);
1045
1299
  if (qset.quantities.length === 0) {
1046
1300
  entityQsets.delete(qsetName);
1301
+ // Same empty-Map trap as `deleteProperty`/`newPsets` (#1967
1302
+ // finding 2(b)) — an empty Map is still truthy, so leave no
1303
+ // trace of this entity in `newQsets` once its last qset is gone.
1304
+ if (entityQsets.size === 0) {
1305
+ this.newQsets.delete(entityId);
1306
+ }
1047
1307
  }
1048
1308
  }
1049
1309
  }
@@ -1053,6 +1313,9 @@ export class MutablePropertyView {
1053
1313
  const entityQsets = this.newQsets.get(entityId);
1054
1314
  if (entityQsets) {
1055
1315
  entityQsets.delete(qsetName);
1316
+ if (entityQsets.size === 0) {
1317
+ this.newQsets.delete(entityId);
1318
+ }
1056
1319
  }
1057
1320
  // Remove all quantity mutations for this qset (only those for this entity).
1058
1321
  const bucket = this.quantityKeysByEntity.get(entityId);
@@ -1088,13 +1351,70 @@ export class MutablePropertyView {
1088
1351
  return this.mutationHistory.filter(m => m.entityId === entityId);
1089
1352
  }
1090
1353
  /**
1091
- * Check if an entity has any mutations
1354
+ * Check if an entity currently carries an overlay change.
1355
+ *
1356
+ * Reads the live overlay (same footprint as {@link hasPendingChanges}),
1357
+ * NOT the append-only `mutationHistory` — undo does not pop history (see
1358
+ * `getMutations()`), so a history-based check could report `true` for an
1359
+ * entity whose edit was fully undone. Called with no `entityId`, this is
1360
+ * exactly {@link hasPendingChanges}.
1361
+ *
1362
+ * Unlike {@link getModifiedEntityCount} (derived from
1363
+ * {@link getEffectiveChanges} so it can't diverge), this is a direct
1364
+ * per-entity map lookup kept O(1)-ish for callers that probe many entities
1365
+ * (e.g. a per-row "has changes" indicator) — re-deriving effective changes
1366
+ * per call would be O(overlay size) each time. That means it can still
1367
+ * report `true` for an entity whose only overlay entry is a no-op edit
1368
+ * (undo landed it back at the base value, so `previousValue === newValue`
1369
+ * — see {@link getEffectiveChanges}'s doc). Over-reporting here is the same
1370
+ * safe direction {@link hasPendingChanges} already documents; nothing in
1371
+ * this repo reads this per-entity form in production as of #1967.
1092
1372
  */
1093
1373
  hasChanges(entityId) {
1094
- if (entityId !== undefined) {
1095
- return this.mutationHistory.some(m => m.entityId === entityId);
1374
+ if (entityId === undefined) {
1375
+ return this.hasPendingChanges();
1376
+ }
1377
+ // A create->delete entity is forgotten, not tombstoned (see `deleteEntity`),
1378
+ // so any other per-entity map entries it left behind (attribute/property/
1379
+ // quantity edits made before the delete) are orphaned — they belong to an
1380
+ // entity that will never be exported. `getEffectiveChanges()` already drops
1381
+ // every row for these ids with no exception; this must agree (issue: the
1382
+ // #1915 forgotten-created blind spot). `restoreNewEntity` removes the id
1383
+ // from this set, so a restored entity falls through to the checks below
1384
+ // exactly as before.
1385
+ if (this.forgottenCreatedEntities.has(entityId))
1386
+ return false;
1387
+ if (this.propertyKeysByEntity.has(entityId))
1388
+ return true;
1389
+ if (this.quantityKeysByEntity.has(entityId))
1390
+ return true;
1391
+ if (this.positionalAttrMutations.has(entityId))
1392
+ return true;
1393
+ if (this.typeMutations.has(entityId))
1394
+ return true;
1395
+ if (this.newPsets.has(entityId))
1396
+ return true;
1397
+ if (this.newQsets.has(entityId))
1398
+ return true;
1399
+ if (this.newEntities.has(entityId))
1400
+ return true;
1401
+ if (this.tombstones.has(entityId))
1402
+ return true;
1403
+ const attrPrefix = `${entityId}:attr:`;
1404
+ for (const key of this.attributeMutations.keys()) {
1405
+ if (key.startsWith(attrPrefix))
1406
+ return true;
1096
1407
  }
1097
- return this.mutationHistory.length > 0;
1408
+ const setPrefix = `${entityId}:`;
1409
+ for (const key of this.deletedPsets) {
1410
+ if (key.startsWith(setPrefix))
1411
+ return true;
1412
+ }
1413
+ for (const key of this.deletedQsets) {
1414
+ if (key.startsWith(setPrefix))
1415
+ return true;
1416
+ }
1417
+ return false;
1098
1418
  }
1099
1419
  /**
1100
1420
  * True when the overlay currently carries anything the STEP exporter would
@@ -1125,14 +1445,72 @@ export class MutablePropertyView {
1125
1445
  this.tombstones.size > 0);
1126
1446
  }
1127
1447
  /**
1128
- * Get count of modified entities
1448
+ * Get count of modified entities.
1449
+ *
1450
+ * Reads the live overlay, NOT `mutationHistory` (issue #1915): undo does
1451
+ * not pop history, so a history-based count could over-report — e.g. after
1452
+ * `setAttribute` + `removeAttributeMutation` (exactly what undoing a
1453
+ * freshly-created attribute mutation does), the overlay is empty again but
1454
+ * history still holds the one entry. This must agree with
1455
+ * {@link hasPendingChanges}: zero here iff that is `false`.
1456
+ *
1457
+ * Must also agree with {@link getEffectiveChanges} — an entity contributing
1458
+ * zero effective rows (a create -> edit -> delete `deleteEntity` forgot, or
1459
+ * an edit fully undone back to its base value) must not be counted here
1460
+ * either. `collectModifiedEntityIds` is deliberately DERIVED FROM
1461
+ * `getEffectiveChanges()` rather than hand-walking the overlay maps a
1462
+ * second time, so the two structurally cannot diverge again (issue: the
1463
+ * #1915 forgotten-created blind spot, and the #1967 no-op-edit blind spot
1464
+ * that a second hand-rolled walk reintroduced).
1129
1465
  */
1130
1466
  getModifiedEntityCount() {
1131
- const entities = new Set();
1132
- for (const mutation of this.mutationHistory) {
1133
- entities.add(mutation.entityId);
1134
- }
1135
- return entities.size;
1467
+ return this.collectModifiedEntityIds().size;
1468
+ }
1469
+ /** Distinct entity ids with at least one row in {@link getEffectiveChanges}. */
1470
+ collectModifiedEntityIds() {
1471
+ const ids = new Set();
1472
+ for (const change of this.getEffectiveChanges())
1473
+ ids.add(change.entityId);
1474
+ return ids;
1475
+ }
1476
+ /**
1477
+ * Enumerate every change the overlay currently carries, as it stands right
1478
+ * now — never from `mutationHistory` (see {@link getModifiedEntityCount}).
1479
+ * This is what the export-review UI (issue #1915) and any snapshot test
1480
+ * should read: `previousValue` is derived from the base data (property
1481
+ * table / on-demand extractor / attribute extractor), so an undo→redo
1482
+ * cycle reports the true original, not a stale history entry.
1483
+ *
1484
+ * Whole-pset/qset deletes and creates are reported as a single
1485
+ * `pset-added` / `pset-deleted` / `qset-added` / `qset-deleted` row rather
1486
+ * than one row per property/quantity inside them (deletePropertySet /
1487
+ * createPropertySet also populate individual property/quantity mutations
1488
+ * internally — those are intentionally not double-reported here).
1489
+ *
1490
+ * Deterministic ordering: entityId, then kind, then name, then setName.
1491
+ */
1492
+ getEffectiveChanges() {
1493
+ return collectEffectiveChanges({
1494
+ attributeMutations: this.attributeMutations,
1495
+ positionalAttrMutations: this.positionalAttrMutations,
1496
+ typeMutations: this.typeMutations,
1497
+ newPsets: this.newPsets,
1498
+ deletedPsets: this.deletedPsets,
1499
+ newQsets: this.newQsets,
1500
+ deletedQsets: this.deletedQsets,
1501
+ propertyKeysByEntity: this.propertyKeysByEntity,
1502
+ propertyMutations: this.propertyMutations,
1503
+ quantityKeysByEntity: this.quantityKeysByEntity,
1504
+ quantityMutations: this.quantityMutations,
1505
+ newEntities: this.newEntities,
1506
+ tombstones: this.tombstones,
1507
+ forgottenCreatedEntities: this.forgottenCreatedEntities,
1508
+ }, {
1509
+ attributeExtractor: this.attributeExtractor,
1510
+ resolveBaseEntityId: (entityId) => this.resolveBaseEntityId(entityId),
1511
+ getBasePropertiesForEntity: (entityId) => this.getBasePropertiesForEntity(entityId),
1512
+ getBaseQuantitiesForEntity: (entityId) => this.getBaseQuantitiesForEntity(entityId),
1513
+ });
1136
1514
  }
1137
1515
  /**
1138
1516
  * Clear all mutations (reset to base state)
@@ -1152,6 +1530,8 @@ export class MutablePropertyView {
1152
1530
  this.typeMutations.clear();
1153
1531
  this.newEntities.clear();
1154
1532
  this.tombstones.clear();
1533
+ this.forgottenCreatedEntities.clear();
1534
+ this.forgottenEntityOverlay.clear();
1155
1535
  this.entityAliases.clear();
1156
1536
  this.nextAllocatedId = 0;
1157
1537
  this.mutationHistory = [];