@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":"change-set-to-ops.d.ts","sourceRoot":"","sources":["../src/change-set-to-ops.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAErE,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,6DAA6D;IAC7D,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,2DAA2D;IAC3D,SAAS,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,8DAA8D;IAC9D,mBAAmB,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC5D,2DAA2D;IAC3D,MAAM,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAChD;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB;IACE,EAAE,EAAE,eAAe,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC,CAAC;CAC9C,GACD;IAAE,EAAE,EAAE,qBAAqB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,EAAE,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,EAAE,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,WAAW,EAAE,CAAC;IACnB,mEAAmE;IACnE,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,oEAAoE;IACpE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB;AAID,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAOT;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,sBAAsB,GAC/B,kBAAkB,CAoEpB"}
1
+ {"version":3,"file":"change-set-to-ops.d.ts","sourceRoot":"","sources":["../src/change-set-to-ops.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAErE,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,6DAA6D;IAC7D,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,2DAA2D;IAC3D,SAAS,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,8DAA8D;IAC9D,mBAAmB,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC5D,2DAA2D;IAC3D,MAAM,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAChD;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB;IACE,EAAE,EAAE,eAAe,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC,CAAC;CAC9C,GACD;IAAE,EAAE,EAAE,qBAAqB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,EAAE,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,EAAE,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,WAAW,EAAE,CAAC;IACnB,mEAAmE;IACnE,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,oEAAoE;IACpE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB;AAID,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAOT;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,sBAAsB,GAC/B,kBAAkB,CA+EpB"}
@@ -84,7 +84,19 @@ export function changeSetToOps(changeSet, resolver) {
84
84
  applyMutation(mutation, entity, setMember, componentFor, entityOps, resolver, skipped);
85
85
  }
86
86
  const ops = [...entityOps.values()];
87
+ // Component ops fold in mutation order alongside entityOps above, but an
88
+ // entity's terminal state (LWW over entityOps) is only known once the
89
+ // whole mutation list has been consumed. Filter here, after both are
90
+ // complete, rather than during the fold: a DELETE_ENTITY appearing after
91
+ // the property mutation that produced a component would otherwise be
92
+ // missed by an in-flight check (the same forward-pass hazard as #2044).
93
+ // An entity tombstoned and later recreated (DELETE_ENTITY then
94
+ // CREATE_ENTITY for the same identity) resolves to 'add-entity' here,
95
+ // since entityOps is itself LWW keyed by entity — so its components
96
+ // correctly survive.
87
97
  for (const [entity, perEntity] of components) {
98
+ if (entityOps.get(entity)?.op === 'tombstone-entity')
99
+ continue;
88
100
  for (const [componentKey, values] of perEntity) {
89
101
  if (values === null) {
90
102
  ops.push({ op: 'tombstone-component', entity, componentKey });
@@ -1 +1 @@
1
- {"version":3,"file":"change-set-to-ops.js","sourceRoot":"","sources":["../src/change-set-to-ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"change-set-to-ops.js","sourceRoot":"","sources":["../src/change-set-to-ops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Pure enumeration of `MutablePropertyView.getEffectiveChanges()` (issue
3
+ * #1915), extracted out of `mutable-property-view.ts` to keep that file from
4
+ * growing further past the ~400-line module-split guideline (AGENTS.md).
5
+ *
6
+ * `collectEffectiveChanges` takes a narrow, explicitly-typed snapshot of the
7
+ * overlay maps/sets it needs plus a small set of resolver callbacks — never
8
+ * the view instance itself — so it stays a pure function, independently
9
+ * testable, and the view's private state stays private (the view passes a
10
+ * `Readonly`-wrapped view of its own maps at call time).
11
+ */
12
+ import type { PropertySet, QuantitySet } from '@ifc-lite/data';
13
+ import type { AttributeMutation, EffectiveChange, EntityTypeMutation, IfcAttributeValue, NewEntity, PropertyMutation, QuantityMutation } from './types.js';
14
+ /** Base-value extractor for an entity attribute — see `MutablePropertyView.setAttributeExtractor`. */
15
+ export type AttributeExtractor = (entityId: number, attrName: string) => string | null;
16
+ /**
17
+ * Narrow, read-only view of the overlay state `collectEffectiveChanges` reads.
18
+ * Mirrors the corresponding private fields on `MutablePropertyView` exactly —
19
+ * the view passes its own maps/sets directly (they satisfy these `Readonly*`
20
+ * types structurally), so no copying is required per call.
21
+ */
22
+ export interface EffectiveChangesSnapshot {
23
+ attributeMutations: ReadonlyMap<string, AttributeMutation>;
24
+ positionalAttrMutations: ReadonlyMap<number, ReadonlyMap<number, IfcAttributeValue>>;
25
+ typeMutations: ReadonlyMap<number, EntityTypeMutation>;
26
+ newPsets: ReadonlyMap<number, ReadonlyMap<string, PropertySet>>;
27
+ deletedPsets: ReadonlySet<string>;
28
+ newQsets: ReadonlyMap<number, ReadonlyMap<string, QuantitySet>>;
29
+ deletedQsets: ReadonlySet<string>;
30
+ propertyKeysByEntity: ReadonlyMap<number, ReadonlySet<string>>;
31
+ propertyMutations: ReadonlyMap<string, PropertyMutation>;
32
+ quantityKeysByEntity: ReadonlyMap<number, ReadonlySet<string>>;
33
+ quantityMutations: ReadonlyMap<string, QuantityMutation>;
34
+ newEntities: ReadonlyMap<number, NewEntity>;
35
+ tombstones: ReadonlySet<number>;
36
+ /**
37
+ * Ids `createEntity` allocated and `deleteEntity` then forgot (removed from
38
+ * `newEntities` rather than tombstoning — see `MutablePropertyView.deleteEntity`).
39
+ * These ids never appear in `newEntities` or `tombstones`, so without this
40
+ * set the filter below cannot tell "overlay-created then forgotten" apart
41
+ * from "an ordinary source-buffer entity" — both are simply absent from
42
+ * `newEntities`. Any row whose `entityId` is in this set is dropped
43
+ * entirely: the entity never existed as far as the export is concerned, so
44
+ * unlike a tombstoned entity there is no `entity-deleted` row to keep.
45
+ */
46
+ forgottenCreatedEntities: ReadonlySet<number>;
47
+ }
48
+ /** Base-data resolvers `collectEffectiveChanges` needs for `previousValue`. */
49
+ export interface EffectiveChangesResolvers {
50
+ /** Registered via `setAttributeExtractor`, or `null` if none was set. */
51
+ attributeExtractor: AttributeExtractor | null;
52
+ /** `MutablePropertyView.resolveBaseEntityId` — follows duplicate aliases. */
53
+ resolveBaseEntityId: (entityId: number) => number;
54
+ /** `MutablePropertyView`'s private `getBasePropertiesForEntity`. */
55
+ getBasePropertiesForEntity: (entityId: number) => PropertySet[];
56
+ /** `MutablePropertyView`'s private `getBaseQuantitiesForEntity`. */
57
+ getBaseQuantitiesForEntity: (entityId: number) => QuantitySet[];
58
+ }
59
+ export declare function collectEffectiveChanges(snapshot: EffectiveChangesSnapshot, resolvers: EffectiveChangesResolvers): EffectiveChange[];
60
+ //# sourceMappingURL=effective-changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-changes.d.ts","sourceRoot":"","sources":["../src/effective-changes.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAGpB,sGAAsG;AACtG,MAAM,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAEvF;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC3D,uBAAuB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACrF,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACvD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAChE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAChE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,iBAAiB,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACzD,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,iBAAiB,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACzD,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC5C,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC;;;;;;;;;OASG;IACH,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/C;AAED,+EAA+E;AAC/E,MAAM,WAAW,yBAAyB;IACxC,yEAAyE;IACzE,kBAAkB,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC9C,6EAA6E;IAC7E,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,oEAAoE;IACpE,0BAA0B,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;IAChE,oEAAoE;IACpE,0BAA0B,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;CACjE;AA6QD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,wBAAwB,EAClC,SAAS,EAAE,yBAAyB,GACnC,eAAe,EAAE,CAiBnB"}
@@ -0,0 +1,277 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { propertyKey, quantityKey } from './types.js';
5
+ /** Stringify an overlay value for `EffectiveChange.previousValue` / `.newValue`. */
6
+ function stringifyEffectiveValue(value) {
7
+ if (value === null || value === undefined)
8
+ return undefined;
9
+ if (typeof value === 'string')
10
+ return value;
11
+ if (typeof value === 'number' || typeof value === 'boolean')
12
+ return String(value);
13
+ return JSON.stringify(value);
14
+ }
15
+ /** Sort key for `getEffectiveChanges()`: entityId, then kind, then name, then setName. */
16
+ function compareEffectiveChanges(a, b) {
17
+ if (a.entityId !== b.entityId)
18
+ return a.entityId - b.entityId;
19
+ if (a.kind !== b.kind)
20
+ return a.kind < b.kind ? -1 : 1;
21
+ const an = a.name ?? '';
22
+ const bn = b.name ?? '';
23
+ if (an !== bn)
24
+ return an < bn ? -1 : 1;
25
+ const asn = a.setName ?? '';
26
+ const bsn = b.setName ?? '';
27
+ if (asn !== bsn)
28
+ return asn < bsn ? -1 : 1;
29
+ return 0;
30
+ }
31
+ /** Attribute + positional-attribute + retype overlay entries. */
32
+ function collectAttributeAndTypeChanges(snapshot, resolvers, changes) {
33
+ for (const [key, mutation] of snapshot.attributeMutations) {
34
+ const entityId = Number(key.slice(0, key.indexOf(':')));
35
+ const previousValue = resolvers.attributeExtractor
36
+ ? stringifyEffectiveValue(resolvers.attributeExtractor(resolvers.resolveBaseEntityId(entityId), mutation.attribute))
37
+ : stringifyEffectiveValue(mutation.oldValue ?? null);
38
+ changes.push({
39
+ entityId,
40
+ kind: 'attribute',
41
+ name: mutation.attribute,
42
+ previousValue,
43
+ newValue: stringifyEffectiveValue(mutation.value),
44
+ });
45
+ }
46
+ for (const [entityId, indexMap] of snapshot.positionalAttrMutations) {
47
+ for (const [index, value] of indexMap) {
48
+ changes.push({
49
+ entityId,
50
+ kind: 'attribute',
51
+ name: `@${index}`,
52
+ newValue: stringifyEffectiveValue(value),
53
+ });
54
+ }
55
+ }
56
+ for (const [entityId, mutation] of snapshot.typeMutations) {
57
+ changes.push({
58
+ entityId,
59
+ kind: 'type',
60
+ previousValue: mutation.oldType,
61
+ newValue: mutation.newType,
62
+ });
63
+ }
64
+ }
65
+ /** Whole-pset / whole-qset add-or-delete overlay entries. */
66
+ function collectSetLevelChanges(snapshot, changes) {
67
+ for (const [entityId, entityPsets] of snapshot.newPsets) {
68
+ for (const psetName of entityPsets.keys()) {
69
+ changes.push({ entityId, kind: 'pset-added', setName: psetName });
70
+ }
71
+ }
72
+ for (const key of snapshot.deletedPsets) {
73
+ const entityId = Number(key.slice(0, key.indexOf(':')));
74
+ const psetName = key.slice(key.indexOf(':') + 1);
75
+ changes.push({ entityId, kind: 'pset-deleted', setName: psetName });
76
+ }
77
+ for (const [entityId, entityQsets] of snapshot.newQsets) {
78
+ for (const qsetName of entityQsets.keys()) {
79
+ changes.push({ entityId, kind: 'qset-added', setName: qsetName });
80
+ }
81
+ }
82
+ for (const key of snapshot.deletedQsets) {
83
+ const entityId = Number(key.slice(0, key.indexOf(':')));
84
+ const qsetName = key.slice(key.indexOf(':') + 1);
85
+ changes.push({ entityId, kind: 'qset-deleted', setName: qsetName });
86
+ }
87
+ }
88
+ /**
89
+ * Look up the overlay mutation for a key already confirmed present in the
90
+ * entity's `*KeysByEntity` index. The single lookup path
91
+ * `collectPropertyChanges` and `collectQuantityChanges` both use (each calls
92
+ * it twice: once walking base-pset/qset properties, once walking overlay-only
93
+ * keys for newly-added properties/quantities on an existing set) so the two
94
+ * call sites can't silently diverge on how a missing entry is handled.
95
+ */
96
+ function resolveMutation(mutations, key) {
97
+ return mutations.get(key);
98
+ }
99
+ /**
100
+ * Individual property mutations on EXISTING (base) psets. Mirrors
101
+ * `MutablePropertyView.getForEntity()`'s own base-pset walk so prefix
102
+ * stripping (not colon-splitting, which would break on a name containing
103
+ * ':') recovers propName safely. Properties belonging to a newly-created or
104
+ * wholly-deleted pset are reported by `collectSetLevelChanges` and skipped
105
+ * here.
106
+ */
107
+ function collectPropertyChanges(snapshot, resolvers, changes) {
108
+ for (const entityId of snapshot.propertyKeysByEntity.keys()) {
109
+ const entityPropKeys = snapshot.propertyKeysByEntity.get(entityId);
110
+ const basePsets = resolvers.getBasePropertiesForEntity(entityId);
111
+ const visited = new Set();
112
+ for (const pset of basePsets) {
113
+ if (snapshot.deletedPsets.has(`${entityId}:${pset.name}`))
114
+ continue;
115
+ if (snapshot.newPsets.get(entityId)?.has(pset.name))
116
+ continue;
117
+ for (const prop of pset.properties) {
118
+ const key = propertyKey(entityId, pset.name, prop.name);
119
+ if (!entityPropKeys.has(key))
120
+ continue;
121
+ visited.add(key);
122
+ const mutation = resolveMutation(snapshot.propertyMutations, key);
123
+ if (!mutation)
124
+ continue;
125
+ changes.push({
126
+ entityId,
127
+ kind: 'property',
128
+ setName: pset.name,
129
+ name: prop.name,
130
+ previousValue: stringifyEffectiveValue(prop.value),
131
+ newValue: mutation.operation === 'DELETE' ? undefined : stringifyEffectiveValue(mutation.value),
132
+ deleted: mutation.operation === 'DELETE' ? true : undefined,
133
+ });
134
+ }
135
+ const psetPrefix = `${entityId}:${pset.name}:`;
136
+ for (const key of entityPropKeys) {
137
+ if (visited.has(key) || !key.startsWith(psetPrefix))
138
+ continue;
139
+ visited.add(key);
140
+ const mutation = resolveMutation(snapshot.propertyMutations, key);
141
+ if (!mutation)
142
+ continue;
143
+ changes.push({
144
+ entityId,
145
+ kind: 'property',
146
+ setName: pset.name,
147
+ name: key.slice(psetPrefix.length),
148
+ newValue: mutation.operation === 'DELETE' ? undefined : stringifyEffectiveValue(mutation.value),
149
+ deleted: mutation.operation === 'DELETE' ? true : undefined,
150
+ });
151
+ }
152
+ }
153
+ }
154
+ }
155
+ /** Individual quantity mutations on EXISTING (base) qsets — mirrors `collectPropertyChanges`. */
156
+ function collectQuantityChanges(snapshot, resolvers, changes) {
157
+ for (const entityId of snapshot.quantityKeysByEntity.keys()) {
158
+ const entityQtyKeys = snapshot.quantityKeysByEntity.get(entityId);
159
+ const baseQsets = resolvers.getBaseQuantitiesForEntity(entityId);
160
+ const visited = new Set();
161
+ for (const qset of baseQsets) {
162
+ if (snapshot.deletedQsets.has(`${entityId}:${qset.name}`))
163
+ continue;
164
+ if (snapshot.newQsets.get(entityId)?.has(qset.name))
165
+ continue;
166
+ for (const q of qset.quantities) {
167
+ const key = quantityKey(entityId, qset.name, q.name);
168
+ if (!entityQtyKeys.has(key))
169
+ continue;
170
+ visited.add(key);
171
+ const mutation = resolveMutation(snapshot.quantityMutations, key);
172
+ if (!mutation)
173
+ continue;
174
+ changes.push({
175
+ entityId,
176
+ kind: 'quantity',
177
+ setName: qset.name,
178
+ name: q.name,
179
+ previousValue: stringifyEffectiveValue(q.value),
180
+ newValue: mutation.operation === 'DELETE' ? undefined : stringifyEffectiveValue(mutation.value),
181
+ deleted: mutation.operation === 'DELETE' ? true : undefined,
182
+ });
183
+ }
184
+ const qsetPrefix = `${entityId}:${qset.name}:`;
185
+ for (const key of entityQtyKeys) {
186
+ if (visited.has(key) || !key.startsWith(qsetPrefix))
187
+ continue;
188
+ visited.add(key);
189
+ const mutation = resolveMutation(snapshot.quantityMutations, key);
190
+ if (!mutation)
191
+ continue;
192
+ changes.push({
193
+ entityId,
194
+ kind: 'quantity',
195
+ setName: qset.name,
196
+ name: key.slice(qsetPrefix.length),
197
+ newValue: mutation.operation === 'DELETE' ? undefined : stringifyEffectiveValue(mutation.value),
198
+ deleted: mutation.operation === 'DELETE' ? true : undefined,
199
+ });
200
+ }
201
+ }
202
+ }
203
+ }
204
+ /** Entity create / delete overlay entries. */
205
+ function collectEntityChanges(snapshot, changes) {
206
+ for (const [entityId, entity] of snapshot.newEntities) {
207
+ changes.push({ entityId, kind: 'entity-added', newValue: entity.type });
208
+ }
209
+ for (const entityId of snapshot.tombstones) {
210
+ changes.push({ entityId, kind: 'entity-deleted' });
211
+ }
212
+ }
213
+ /**
214
+ * Enumerate every change the overlay currently carries, as it stands right
215
+ * now — never from `mutationHistory`. Backs
216
+ * `MutablePropertyView.getEffectiveChanges()`: `previousValue` is derived
217
+ * from the base data (property table / on-demand extractor / attribute
218
+ * extractor), so an undo→redo cycle reports the true original, not a stale
219
+ * history entry.
220
+ *
221
+ * Whole-pset/qset deletes and creates are reported as a single
222
+ * `pset-added` / `pset-deleted` / `qset-added` / `qset-deleted` row rather
223
+ * than one row per property/quantity inside them (`deletePropertySet` /
224
+ * `createPropertySet` also populate individual property/quantity mutations
225
+ * internally — those are intentionally not double-reported here).
226
+ *
227
+ * Deterministic ordering: entityId, then kind, then name, then setName.
228
+ *
229
+ * A tombstoned entity's other overlay rows (attribute/property/quantity/type
230
+ * edits made before it was deleted) are dropped, keeping only its
231
+ * `entity-deleted` row. This mirrors `step-exporter.ts`'s STEP-export
232
+ * behaviour, which drops attribute/positional/type mutations for a deleted
233
+ * entity (`completeIndex` loop `continue`s on `mutationView.isDeleted()`
234
+ * before applying them) — reporting those superseded edits here would show
235
+ * the user a row for a change the exporter silently discards.
236
+ *
237
+ * An overlay-created entity that was *itself* deleted before ever being
238
+ * exported is dropped even more completely: `deleteEntity` purges a created
239
+ * entity's overlay rows (property/quantity/attribute/type mutations, its
240
+ * `newPsets`/`newQsets` entries, and its own mutation-history records) as
241
+ * soon as it forgets the entity — see that method's doc — so this filter is
242
+ * defense in depth rather than the primary mechanism: the snapshot maps
243
+ * should already be clean for a forgotten id. Any row that does slip through
244
+ * (e.g. a caller-supplied snapshot in a test) is dropped with no exception.
245
+ *
246
+ * A fully-undone edit — undo re-applies the inverse with `skipHistory`,
247
+ * landing the overlay back at the base value — resolves `previousValue` and
248
+ * `newValue` to the same string. That is a no-op from the export's
249
+ * perspective (the exporter would write back exactly what was already
250
+ * there), so it is dropped too rather than shown as a "Status Original ->
251
+ * Original" row for a change the user undid.
252
+ */
253
+ function isNoOpChange(c) {
254
+ if (c.kind !== 'attribute' && c.kind !== 'property' && c.kind !== 'quantity' && c.kind !== 'type') {
255
+ return false;
256
+ }
257
+ return c.previousValue !== undefined && c.previousValue === c.newValue;
258
+ }
259
+ export function collectEffectiveChanges(snapshot, resolvers) {
260
+ const changes = [];
261
+ collectAttributeAndTypeChanges(snapshot, resolvers, changes);
262
+ collectSetLevelChanges(snapshot, changes);
263
+ collectPropertyChanges(snapshot, resolvers, changes);
264
+ collectQuantityChanges(snapshot, resolvers, changes);
265
+ collectEntityChanges(snapshot, changes);
266
+ changes.sort(compareEffectiveChanges);
267
+ return changes.filter((c) => {
268
+ if (snapshot.forgottenCreatedEntities.has(c.entityId))
269
+ return false;
270
+ if (c.kind !== 'entity-deleted' && snapshot.tombstones.has(c.entityId))
271
+ return false;
272
+ if (isNoOpChange(c))
273
+ return false;
274
+ return true;
275
+ });
276
+ }
277
+ //# sourceMappingURL=effective-changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-changes.js","sourceRoot":"","sources":["../src/effective-changes.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @ifc-lite/mutations - Mutation tracking for IFC data
3
3
  */
4
4
  export * from './types.js';
5
- export { MutablePropertyView, type PropertyExtractor, type QuantityExtractor } from './mutable-property-view.js';
5
+ export { MutablePropertyView, type PropertyExtractor, type QuantityExtractor, type AttributeExtractor, } from './mutable-property-view.js';
6
6
  export { StoreEditor, OVERLAY_BYTE_OFFSET, setEntityTypeNormalizer, type EntityTypeNormalizer, } from './store-editor.js';
7
7
  export { ChangeSetManager } from './change-set.js';
8
8
  export { BulkQueryEngine, type SelectionCriteria, type BulkAction, type BulkQuery, type BulkQueryPreview, type BulkQueryResult, type PropertyFilter, type FilterOperator, } from './bulk-query-engine.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACjH,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * @ifc-lite/mutations - Mutation tracking for IFC data
6
6
  */
7
7
  export * from './types.js';
8
- export { MutablePropertyView } from './mutable-property-view.js';
8
+ export { MutablePropertyView, } from './mutable-property-view.js';
9
9
  export { StoreEditor, OVERLAY_BYTE_OFFSET, setEntityTypeNormalizer, } from './store-editor.js';
10
10
  export { ChangeSetManager } from './change-set.js';
11
11
  export { BulkQueryEngine, } from './bulk-query-engine.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAkD,MAAM,4BAA4B,CAAC;AACjH,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,uBAAuB,GAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,GAQhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,GASb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,oBAAoB,GAKrB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,mBAAmB,GAIpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,uBAAuB,GAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,GAQhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,YAAY,GASb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,oBAAoB,GAKrB,MAAM,wBAAwB,CAAC"}
@@ -9,7 +9,9 @@
9
9
  */
10
10
  import type { PropertyTable, PropertySet, QuantitySet } from '@ifc-lite/data';
11
11
  import { PropertyValueType, QuantityType } from '@ifc-lite/data';
12
- import type { IfcAttributeValue, PropertyValue, EntityTypeMutation, Mutation, NewEntity } from './types.js';
12
+ import type { IfcAttributeValue, PropertyValue, EntityTypeMutation, Mutation, NewEntity, EffectiveChange } from './types.js';
13
+ import { type AttributeExtractor } from './effective-changes.js';
14
+ export type { AttributeExtractor } from './effective-changes.js';
13
15
  /**
14
16
  * Function type for on-demand property extraction
15
17
  * Allows globalId to be optional to match extractPropertiesOnDemand return type
@@ -32,6 +34,7 @@ export declare class MutablePropertyView {
32
34
  private baseTable;
33
35
  private onDemandExtractor;
34
36
  private quantityExtractor;
37
+ private attributeExtractor;
35
38
  private propertyMutations;
36
39
  private quantityMutations;
37
40
  /**
@@ -53,6 +56,39 @@ export declare class MutablePropertyView {
53
56
  private typeMutations;
54
57
  private newEntities;
55
58
  private tombstones;
59
+ /**
60
+ * Ids `createEntity` allocated and `deleteEntity` then forgot (removed from
61
+ * `newEntities`, per that method's "existing entities are tombstoned; new
62
+ * entities are simply forgotten" contract). Tracked separately so
63
+ * `getEffectiveChanges()` / `collectEffectiveChanges` can tell "overlay-created
64
+ * then forgotten" apart from "an ordinary source-buffer entity" — both are
65
+ * otherwise indistinguishable, being simply absent from `newEntities`.
66
+ * `restoreNewEntity` (the undo-of-delete counterpart) clears the id back out.
67
+ */
68
+ private forgottenCreatedEntities;
69
+ /**
70
+ * Snapshot of a forgotten-created entity's overlay rows, stashed by
71
+ * `deleteEntity` and restored by `restoreNewEntity`.
72
+ *
73
+ * `deleteEntity` on an overlay-created entity does more than drop it from
74
+ * `newEntities` — it also PURGES every other overlay entry the entity left
75
+ * behind (property/quantity/attribute/positional/type mutations, its
76
+ * `newPsets`/`newQsets` entries, and its own `mutationHistory` records).
77
+ * Without that purge, an entity that was created, edited, then deleted
78
+ * before export left a dangling reference: `StepExporter` derives its
79
+ * property/quantity work list from `getMutations()` (the append-only
80
+ * history) and reads `getForEntity()` / `getQuantitiesForEntity()` straight
81
+ * off `newPsets` / `newQsets` — neither of which the review-side
82
+ * `forgottenCreatedEntities` filter in `effective-changes.ts` touches. The
83
+ * review dialog looked clean while the exported file still contained an
84
+ * `IFCPROPERTYSET` + `IFCRELDEFINESBYPROPERTIES` pointing at an expressId
85
+ * that was never actually created (maintainer finding on #1967).
86
+ *
87
+ * The purged data is captured here, not discarded, because `restoreNewEntity`
88
+ * (undo of the delete) must bring it all back — rows AND count AND what the
89
+ * exporter would see — not just re-add the bare `NewEntity` record.
90
+ */
91
+ private forgottenEntityOverlay;
56
92
  /**
57
93
  * Overlay-entity → source-entity aliases for property/quantity reads.
58
94
  *
@@ -98,6 +134,14 @@ export declare class MutablePropertyView {
98
134
  * Set an on-demand quantity extractor function
99
135
  */
100
136
  setQuantityExtractor(extractor: QuantityExtractor): void;
137
+ /**
138
+ * Set the base entity-attribute extractor (Name, Description, ObjectType,
139
+ * Tag, ...), used only to resolve `previousValue` in `getEffectiveChanges()`.
140
+ * Without one, attribute `previousValue` falls back to whatever `oldValue`
141
+ * the overlay entry itself carries — which undo can leave stale/absent (see
142
+ * `getEffectiveChanges()` doc).
143
+ */
144
+ setAttributeExtractor(extractor: AttributeExtractor): void;
101
145
  /**
102
146
  * Get base properties for an entity (before mutations)
103
147
  * Uses on-demand extraction if available, otherwise falls back to base table.
@@ -283,6 +327,20 @@ export declare class MutablePropertyView {
283
327
  * restore (the slice's undo stack does this).
284
328
  */
285
329
  restoreNewEntity(entity: NewEntity): void;
330
+ /**
331
+ * Move every current overlay entry for `expressId` out of the live maps
332
+ * and into `forgottenEntityOverlay`, and drop this entity's own records
333
+ * from `mutationHistory`. Called by `deleteEntity` when it forgets a
334
+ * created entity. Only stashes a key if something was actually captured,
335
+ * so `unstashEntityOverlay` on a plain (never-edited) create is a no-op.
336
+ */
337
+ private stashAndPurgeEntityOverlay;
338
+ /**
339
+ * Reverse `stashAndPurgeEntityOverlay`: put everything `deleteEntity`
340
+ * purged back into the live overlay maps. Called by `restoreNewEntity`.
341
+ * A no-op if nothing was stashed for `expressId`.
342
+ */
343
+ private unstashEntityOverlay;
286
344
  /**
287
345
  * Every express id this session deleted — source-buffer entities AND ones it
288
346
  * created and then deleted. The two are not distinguishable from this set
@@ -326,7 +384,24 @@ export declare class MutablePropertyView {
326
384
  */
327
385
  getMutationsForEntity(entityId: number): Mutation[];
328
386
  /**
329
- * Check if an entity has any mutations
387
+ * Check if an entity currently carries an overlay change.
388
+ *
389
+ * Reads the live overlay (same footprint as {@link hasPendingChanges}),
390
+ * NOT the append-only `mutationHistory` — undo does not pop history (see
391
+ * `getMutations()`), so a history-based check could report `true` for an
392
+ * entity whose edit was fully undone. Called with no `entityId`, this is
393
+ * exactly {@link hasPendingChanges}.
394
+ *
395
+ * Unlike {@link getModifiedEntityCount} (derived from
396
+ * {@link getEffectiveChanges} so it can't diverge), this is a direct
397
+ * per-entity map lookup kept O(1)-ish for callers that probe many entities
398
+ * (e.g. a per-row "has changes" indicator) — re-deriving effective changes
399
+ * per call would be O(overlay size) each time. That means it can still
400
+ * report `true` for an entity whose only overlay entry is a no-op edit
401
+ * (undo landed it back at the base value, so `previousValue === newValue`
402
+ * — see {@link getEffectiveChanges}'s doc). Over-reporting here is the same
403
+ * safe direction {@link hasPendingChanges} already documents; nothing in
404
+ * this repo reads this per-entity form in production as of #1967.
330
405
  */
331
406
  hasChanges(entityId?: number): boolean;
332
407
  /**
@@ -346,9 +421,44 @@ export declare class MutablePropertyView {
346
421
  */
347
422
  hasPendingChanges(): boolean;
348
423
  /**
349
- * Get count of modified entities
424
+ * Get count of modified entities.
425
+ *
426
+ * Reads the live overlay, NOT `mutationHistory` (issue #1915): undo does
427
+ * not pop history, so a history-based count could over-report — e.g. after
428
+ * `setAttribute` + `removeAttributeMutation` (exactly what undoing a
429
+ * freshly-created attribute mutation does), the overlay is empty again but
430
+ * history still holds the one entry. This must agree with
431
+ * {@link hasPendingChanges}: zero here iff that is `false`.
432
+ *
433
+ * Must also agree with {@link getEffectiveChanges} — an entity contributing
434
+ * zero effective rows (a create -> edit -> delete `deleteEntity` forgot, or
435
+ * an edit fully undone back to its base value) must not be counted here
436
+ * either. `collectModifiedEntityIds` is deliberately DERIVED FROM
437
+ * `getEffectiveChanges()` rather than hand-walking the overlay maps a
438
+ * second time, so the two structurally cannot diverge again (issue: the
439
+ * #1915 forgotten-created blind spot, and the #1967 no-op-edit blind spot
440
+ * that a second hand-rolled walk reintroduced).
350
441
  */
351
442
  getModifiedEntityCount(): number;
443
+ /** Distinct entity ids with at least one row in {@link getEffectiveChanges}. */
444
+ private collectModifiedEntityIds;
445
+ /**
446
+ * Enumerate every change the overlay currently carries, as it stands right
447
+ * now — never from `mutationHistory` (see {@link getModifiedEntityCount}).
448
+ * This is what the export-review UI (issue #1915) and any snapshot test
449
+ * should read: `previousValue` is derived from the base data (property
450
+ * table / on-demand extractor / attribute extractor), so an undo→redo
451
+ * cycle reports the true original, not a stale history entry.
452
+ *
453
+ * Whole-pset/qset deletes and creates are reported as a single
454
+ * `pset-added` / `pset-deleted` / `qset-added` / `qset-deleted` row rather
455
+ * than one row per property/quantity inside them (deletePropertySet /
456
+ * createPropertySet also populate individual property/quantity mutations
457
+ * internally — those are intentionally not double-reported here).
458
+ *
459
+ * Deterministic ordering: entityId, then kind, then name, then setName.
460
+ */
461
+ getEffectiveChanges(): EffectiveChange[];
352
462
  /**
353
463
  * Clear all mutations (reset to base state)
354
464
  */