@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.
- package/dist/change-set-to-ops.d.ts.map +1 -1
- package/dist/change-set-to-ops.js +12 -0
- package/dist/change-set-to-ops.js.map +1 -1
- package/dist/effective-changes.d.ts +60 -0
- package/dist/effective-changes.d.ts.map +1 -0
- package/dist/effective-changes.js +277 -0
- package/dist/effective-changes.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mutable-property-view.d.ts +113 -3
- package/dist/mutable-property-view.d.ts.map +1 -1
- package/dist/mutable-property-view.js +398 -18
- package/dist/mutable-property-view.js.map +1 -1
- package/dist/types.d.ts +49 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutable-property-view.d.ts","sourceRoot":"","sources":["../src/mutable-property-view.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAY,WAAW,EAAY,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAyD,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
978
|
-
//
|
|
979
|
-
//
|
|
980
|
-
//
|
|
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
|
|
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
|
|
1095
|
-
return this.
|
|
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
|
-
|
|
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
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
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 = [];
|