@ifc-lite/mcp 0.17.0 → 0.20.2
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/authored-attribute-value.d.ts +3 -0
- package/dist/authored-attribute-value.d.ts.map +1 -0
- package/dist/authored-attribute-value.js +34 -0
- package/dist/authored-attribute-value.js.map +1 -0
- package/dist/backend-query-relationships.d.ts +5 -0
- package/dist/backend-query-relationships.d.ts.map +1 -0
- package/dist/backend-query-relationships.js +55 -0
- package/dist/backend-query-relationships.js.map +1 -0
- package/dist/backend-query.d.ts +5 -6
- package/dist/backend-query.d.ts.map +1 -1
- package/dist/backend-query.js +81 -140
- package/dist/backend-query.js.map +1 -1
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/effective-entity-membership.d.ts +8 -0
- package/dist/effective-entity-membership.d.ts.map +1 -0
- package/dist/effective-entity-membership.js +23 -0
- package/dist/effective-entity-membership.js.map +1 -0
- package/dist/headless-backend-cost-store.d.ts +14 -0
- package/dist/headless-backend-cost-store.d.ts.map +1 -0
- package/dist/headless-backend-cost-store.js +20 -0
- package/dist/headless-backend-cost-store.js.map +1 -0
- package/dist/headless-backend-store-stubs.d.ts +12 -0
- package/dist/headless-backend-store-stubs.d.ts.map +1 -0
- package/dist/headless-backend-store-stubs.js +9 -0
- package/dist/headless-backend-store-stubs.js.map +1 -0
- package/dist/headless-backend-structural-store.d.ts +21 -0
- package/dist/headless-backend-structural-store.d.ts.map +1 -0
- package/dist/headless-backend-structural-store.js +20 -0
- package/dist/headless-backend-structural-store.js.map +1 -0
- package/dist/headless-backend.d.ts +2 -1
- package/dist/headless-backend.d.ts.map +1 -1
- package/dist/headless-backend.js +9 -9
- package/dist/headless-backend.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +4 -0
- package/dist/loader.js.map +1 -1
- package/dist/overlay.d.ts +23 -28
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +84 -99
- package/dist/overlay.js.map +1 -1
- package/dist/protocol/types.d.ts +0 -1
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/schema-tables.d.ts.map +1 -1
- package/dist/schema-tables.js +4 -2
- package/dist/schema-tables.js.map +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/spatial-tree.d.ts.map +1 -1
- package/dist/spatial-tree.js +3 -9
- package/dist/spatial-tree.js.map +1 -1
- package/dist/tools/check-rules.d.ts +3 -0
- package/dist/tools/check-rules.d.ts.map +1 -0
- package/dist/tools/check-rules.js +75 -0
- package/dist/tools/check-rules.js.map +1 -0
- package/dist/tools/diff-authored-keys.d.ts +68 -0
- package/dist/tools/diff-authored-keys.d.ts.map +1 -0
- package/dist/tools/diff-authored-keys.js +166 -0
- package/dist/tools/diff-authored-keys.js.map +1 -0
- package/dist/tools/diff-content.d.ts +33 -0
- package/dist/tools/diff-content.d.ts.map +1 -0
- package/dist/tools/diff-content.js +158 -0
- package/dist/tools/diff-content.js.map +1 -0
- package/dist/tools/diff-entity-scope.d.ts +53 -0
- package/dist/tools/diff-entity-scope.d.ts.map +1 -0
- package/dist/tools/diff-entity-scope.js +94 -0
- package/dist/tools/diff-entity-scope.js.map +1 -0
- package/dist/tools/diff-fingerprints.d.ts +4 -2
- package/dist/tools/diff-fingerprints.d.ts.map +1 -1
- package/dist/tools/diff-fingerprints.js +69 -153
- package/dist/tools/diff-fingerprints.js.map +1 -1
- package/dist/tools/diff.d.ts.map +1 -1
- package/dist/tools/diff.js +40 -117
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/entity-target-schema.d.ts +16 -0
- package/dist/tools/entity-target-schema.d.ts.map +1 -0
- package/dist/tools/entity-target-schema.js +32 -0
- package/dist/tools/entity-target-schema.js.map +1 -0
- package/dist/tools/flow.d.ts +3 -0
- package/dist/tools/flow.d.ts.map +1 -0
- package/dist/tools/flow.js +239 -0
- package/dist/tools/flow.js.map +1 -0
- package/dist/tools/ids-accessor.d.ts +9 -1
- package/dist/tools/ids-accessor.d.ts.map +1 -1
- package/dist/tools/ids-accessor.js +9 -2
- package/dist/tools/ids-accessor.js.map +1 -1
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mutate.d.ts.map +1 -1
- package/dist/tools/mutate.js +15 -46
- package/dist/tools/mutate.js.map +1 -1
- package/dist/tools/structural.d.ts +11 -0
- package/dist/tools/structural.d.ts.map +1 -0
- package/dist/tools/structural.js +23 -0
- package/dist/tools/structural.js.map +1 -0
- package/dist/tools/util.d.ts.map +1 -1
- package/dist/tools/util.js +15 -18
- package/dist/tools/util.js.map +1 -1
- package/dist/tools/validation.d.ts.map +1 -1
- package/dist/tools/validation.js +40 -41
- package/dist/tools/validation.js.map +1 -1
- package/dist/tools/viewer.d.ts.map +1 -1
- package/dist/tools/viewer.js +10 -29
- package/dist/tools/viewer.js.map +1 -1
- package/dist/validate.d.ts +22 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +45 -10
- package/dist/validate.js.map +1 -1
- package/dist/viewer-manager.d.ts.map +1 -1
- package/dist/viewer-manager.js +1 -0
- package/dist/viewer-manager.js.map +1 -1
- package/package.json +24 -20
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribute reading for the MCP diff adapter, and authored keys (issue #4955): `key_from` keys the
|
|
3
|
+
* comparison on `Tag` or a `Pset.Prop` instead of GlobalId. Read through the
|
|
4
|
+
* session OVERLAY, like every other fingerprinted value, so a queued edit or
|
|
5
|
+
* tombstone is what the comparison sees. Split out of `diff-fingerprints.ts`
|
|
6
|
+
* for size.
|
|
7
|
+
*/
|
|
8
|
+
import { EntityExtractor, type IfcDataStore } from '@ifc-lite/parser';
|
|
9
|
+
import type { EntityFingerprint } from '@ifc-lite/diff';
|
|
10
|
+
import type { EffectiveEntity } from '@ifc-lite/data';
|
|
11
|
+
import { type PendingOverlay } from '../overlay.js';
|
|
12
|
+
/** Adapter options (issue #4955); the CLI's `FingerprintAdapterOptions` restated. */
|
|
13
|
+
export interface FingerprintAdapterOptions {
|
|
14
|
+
/** `Tag` or `<PsetName>.<PropertyName>`; see the CLI copy for the rules. */
|
|
15
|
+
keyProperty?: string;
|
|
16
|
+
duplicateAuthoredKeys?: Map<string, number[]>;
|
|
17
|
+
}
|
|
18
|
+
/** Prefix on a fingerprint key taken from an authored property rather than a GlobalId. */
|
|
19
|
+
export declare const AUTHORED_KEY_PREFIX = "prop:";
|
|
20
|
+
/**
|
|
21
|
+
* A collision found on either revision invalidates that authored value on
|
|
22
|
+
* both. The first side may already have been built when the second reveals the
|
|
23
|
+
* collision, so repair its key back to the entity's GlobalId before diffing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fallbackPairDuplicateAuthoredKeys(sides: readonly {
|
|
26
|
+
fingerprints: EntityFingerprint<number>[];
|
|
27
|
+
store: IfcDataStore;
|
|
28
|
+
overlay?: PendingOverlay | null;
|
|
29
|
+
}[], duplicates: ReadonlyMap<string, number[]>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Build the key resolver for one model: `prop:<value>` where the entity carries
|
|
32
|
+
* a non-empty value nobody else does, GlobalId otherwise. Duplicate values are
|
|
33
|
+
* reported on `options.duplicateAuthoredKeys`. Tombstoned entities are not
|
|
34
|
+
* owners: a deleted twin no longer contests the key. A created entity has no
|
|
35
|
+
* store row to read and keeps its GlobalId.
|
|
36
|
+
*/
|
|
37
|
+
export declare function authoredKeyResolver(store: IfcDataStore, overlay: PendingOverlay | null | undefined, options: FingerprintAdapterOptions, extractor: EntityExtractor, entities: readonly EffectiveEntity[]): (expressId: number, globalId: string) => string;
|
|
38
|
+
/**
|
|
39
|
+
* One named attribute, read positionally through the **cross-schema** attribute
|
|
40
|
+
* list (issue #2021). The CLI's twin, and it must stay one.
|
|
41
|
+
*
|
|
42
|
+
* `extractAllEntityAttributes` names attributes through the parser's IFC4
|
|
43
|
+
* codegen pin, which answers an EMPTY list for a class the pin does not carry —
|
|
44
|
+
* so a `.find(name === 'Tag')` over it silently finds nothing on every
|
|
45
|
+
* IFC4X3-only type object (`IfcRailType`, `IfcTrackElementType`,
|
|
46
|
+
* `IfcSignalType`, …) while working perfectly on IFC2X3 and IFC4. That is a
|
|
47
|
+
* no-op nobody would notice: the entity is in scope, its class name is right,
|
|
48
|
+
* `isTypeObject` is right, and only the evidence is missing.
|
|
49
|
+
*
|
|
50
|
+
* This is the same pinned-registry family as the membership defect `#2001`
|
|
51
|
+
* fixed, and it has to be answered from the same place: the inheritance chain
|
|
52
|
+
* decides *whether* to read a `Tag`, so the attribute list that decides *where*
|
|
53
|
+
* it sits must span the same schemas. `getAttributeNamesAcrossSchemas` returns
|
|
54
|
+
* the pinned result unchanged for every class the pin does know, so this is
|
|
55
|
+
* additive — no IFC2X3 or IFC4 entity's hash moves because of it.
|
|
56
|
+
*
|
|
57
|
+
* Reads the raw STEP slot rather than reusing `extractAllEntityAttributes`'
|
|
58
|
+
* display normalization: this value is hashed, not shown, so `$` (absent) is
|
|
59
|
+
* the only case that needs interpreting and it arrives as null.
|
|
60
|
+
*/
|
|
61
|
+
export declare function attributeAcrossSchemas(store: IfcDataStore, expressId: number, ifcType: string, attributeName: string): string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* An overlay override wins whenever one exists, including when it is empty —
|
|
64
|
+
* `entity_set_attribute` with `''` clears the attribute, and falling back to
|
|
65
|
+
* the stored value there would hash the edit away.
|
|
66
|
+
*/
|
|
67
|
+
export declare function override(edited: string | undefined, stored: string | undefined): string | undefined;
|
|
68
|
+
//# sourceMappingURL=diff-authored-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-authored-keys.d.ts","sourceRoot":"","sources":["../../src/tools/diff-authored-keys.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EAKf,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE9D,qFAAqF;AACrF,MAAM,WAAW,yBAAyB;IACxC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC/C;AAED,0FAA0F;AAC1F,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,SAAS;IACd,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;CACjC,EAAE,EACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACxC,IAAI,CA8BN;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EAC1C,OAAO,EAAE,yBAAyB,EAClC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAoDjD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,SAAS,CAQpB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGnG"}
|
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Attribute reading for the MCP diff adapter, and authored keys (issue #4955): `key_from` keys the
|
|
6
|
+
* comparison on `Tag` or a `Pset.Prop` instead of GlobalId. Read through the
|
|
7
|
+
* session OVERLAY, like every other fingerprinted value, so a queued edit or
|
|
8
|
+
* tombstone is what the comparison sees. Split out of `diff-fingerprints.ts`
|
|
9
|
+
* for size.
|
|
10
|
+
*/
|
|
11
|
+
import { EntityExtractor, authoredKeyValue, extractRootAttributesFromEntity, getAttributeNamesAcrossSchemas, parseAuthoredKeySpec, } from '@ifc-lite/parser';
|
|
12
|
+
import { stepText } from '../overlay.js';
|
|
13
|
+
/** Prefix on a fingerprint key taken from an authored property rather than a GlobalId. */
|
|
14
|
+
export const AUTHORED_KEY_PREFIX = 'prop:';
|
|
15
|
+
/**
|
|
16
|
+
* A collision found on either revision invalidates that authored value on
|
|
17
|
+
* both. The first side may already have been built when the second reveals the
|
|
18
|
+
* collision, so repair its key back to the entity's GlobalId before diffing.
|
|
19
|
+
*/
|
|
20
|
+
export function fallbackPairDuplicateAuthoredKeys(sides, duplicates) {
|
|
21
|
+
if (duplicates.size === 0)
|
|
22
|
+
return;
|
|
23
|
+
for (const { fingerprints, store, overlay } of sides) {
|
|
24
|
+
const extractor = new EntityExtractor(store.source);
|
|
25
|
+
for (const fingerprint of fingerprints) {
|
|
26
|
+
if (!fingerprint.key.startsWith(AUTHORED_KEY_PREFIX))
|
|
27
|
+
continue;
|
|
28
|
+
const value = fingerprint.key.slice(AUTHORED_KEY_PREFIX.length);
|
|
29
|
+
if (!duplicates.has(value))
|
|
30
|
+
continue;
|
|
31
|
+
const named = overlay?.attributes(fingerprint.ref);
|
|
32
|
+
const positional = overlay?.positionalAttributes?.(fingerprint.ref);
|
|
33
|
+
const editedGlobalId = named?.has('GlobalId') ? named.get('GlobalId')
|
|
34
|
+
: positional?.has(0) ? stepText(positional.get(0)) ?? '' : undefined;
|
|
35
|
+
const createdGlobalId = overlay?.createdEntity(fingerprint.ref)?.globalId;
|
|
36
|
+
const tableGlobalId = store.entities.getGlobalId(fingerprint.ref);
|
|
37
|
+
// @raw-entity-enumeration-ok collision fallback uses this point source record only after checking effective and created GlobalIds
|
|
38
|
+
const ref = store.entityIndex.byId.get(fingerprint.ref);
|
|
39
|
+
const entity = ref ? extractor.extractEntity(ref) : undefined;
|
|
40
|
+
const sourceGlobalId = entity ? extractRootAttributesFromEntity(entity).globalId : undefined;
|
|
41
|
+
// An explicit empty edit clears GlobalId. Falling back to the source
|
|
42
|
+
// here would resurrect an identity the session no longer has; the
|
|
43
|
+
// fingerprint builder omits that source entity before this repair pass.
|
|
44
|
+
const globalId = editedGlobalId !== undefined ? editedGlobalId
|
|
45
|
+
: createdGlobalId !== undefined ? createdGlobalId
|
|
46
|
+
: (tableGlobalId || sourceGlobalId);
|
|
47
|
+
if (!globalId) {
|
|
48
|
+
throw new Error(`Cannot restore GlobalId for authored-key collision on #${fingerprint.ref}`);
|
|
49
|
+
}
|
|
50
|
+
fingerprint.key = globalId;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the key resolver for one model: `prop:<value>` where the entity carries
|
|
56
|
+
* a non-empty value nobody else does, GlobalId otherwise. Duplicate values are
|
|
57
|
+
* reported on `options.duplicateAuthoredKeys`. Tombstoned entities are not
|
|
58
|
+
* owners: a deleted twin no longer contests the key. A created entity has no
|
|
59
|
+
* store row to read and keeps its GlobalId.
|
|
60
|
+
*/
|
|
61
|
+
export function authoredKeyResolver(store, overlay, options, extractor, entities) {
|
|
62
|
+
const keySpec = options.keyProperty ? parseAuthoredKeySpec(options.keyProperty) : undefined;
|
|
63
|
+
if (!keySpec)
|
|
64
|
+
return (_expressId, globalId) => globalId;
|
|
65
|
+
const authoredKeyOf = (expressId) => {
|
|
66
|
+
if (keySpec.kind === 'tag') {
|
|
67
|
+
const edited = overlay?.attributes(expressId).get('Tag');
|
|
68
|
+
if (edited !== undefined)
|
|
69
|
+
return edited.trim().length > 0 ? edited.trim() : undefined;
|
|
70
|
+
const created = overlay?.createdEntity(expressId);
|
|
71
|
+
// @raw-entity-enumeration-ok Tag slot discovery needs the source class for this one entity when no queued retype or creation supplies it
|
|
72
|
+
const effectiveType = overlay?.effectiveType?.(expressId)
|
|
73
|
+
?? created?.ifcType ?? store.entityIndex.byId.get(expressId)?.type;
|
|
74
|
+
const tagIndex = effectiveType ? getAttributeNamesAcrossSchemas(effectiveType).indexOf('Tag') : -1;
|
|
75
|
+
const positional = overlay?.positionalAttributes?.(expressId);
|
|
76
|
+
if (tagIndex >= 0 && positional?.has(tagIndex))
|
|
77
|
+
return stepText(positional.get(tagIndex));
|
|
78
|
+
if (created) {
|
|
79
|
+
return tagIndex < 0 ? undefined : stepText(created.attributes[tagIndex]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (overlay) {
|
|
83
|
+
const set = overlay.propertySets(expressId).find((pset) => pset.name === keySpec.pset);
|
|
84
|
+
const property = set?.properties.find((p) => p.name === keySpec.property);
|
|
85
|
+
if (set) {
|
|
86
|
+
const raw = property?.value;
|
|
87
|
+
if (raw === null || raw === undefined)
|
|
88
|
+
return undefined;
|
|
89
|
+
const value = typeof raw === 'object' ? JSON.stringify(raw) : String(raw);
|
|
90
|
+
return value.trim().length > 0 ? value.trim() : undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return authoredKeyValue(store, expressId, keySpec, extractor);
|
|
94
|
+
};
|
|
95
|
+
// A first pass so a value two entities share can be refused for both,
|
|
96
|
+
// instead of the diff's first-wins index quietly keeping one.
|
|
97
|
+
const owners = new Map();
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
for (const { expressId } of entities) {
|
|
100
|
+
if (seen.has(expressId))
|
|
101
|
+
continue;
|
|
102
|
+
seen.add(expressId);
|
|
103
|
+
const value = authoredKeyOf(expressId);
|
|
104
|
+
if (value === undefined)
|
|
105
|
+
continue;
|
|
106
|
+
const list = owners.get(value);
|
|
107
|
+
if (list)
|
|
108
|
+
list.push(expressId);
|
|
109
|
+
else
|
|
110
|
+
owners.set(value, [expressId]);
|
|
111
|
+
}
|
|
112
|
+
for (const [value, ids] of owners) {
|
|
113
|
+
if (ids.length > 1)
|
|
114
|
+
options.duplicateAuthoredKeys?.set(value, ids);
|
|
115
|
+
}
|
|
116
|
+
return (expressId, globalId) => {
|
|
117
|
+
const value = authoredKeyOf(expressId);
|
|
118
|
+
if (value === undefined)
|
|
119
|
+
return globalId;
|
|
120
|
+
return (owners.get(value)?.length ?? 0) === 1 ? `${AUTHORED_KEY_PREFIX}${value}` : globalId;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* One named attribute, read positionally through the **cross-schema** attribute
|
|
125
|
+
* list (issue #2021). The CLI's twin, and it must stay one.
|
|
126
|
+
*
|
|
127
|
+
* `extractAllEntityAttributes` names attributes through the parser's IFC4
|
|
128
|
+
* codegen pin, which answers an EMPTY list for a class the pin does not carry —
|
|
129
|
+
* so a `.find(name === 'Tag')` over it silently finds nothing on every
|
|
130
|
+
* IFC4X3-only type object (`IfcRailType`, `IfcTrackElementType`,
|
|
131
|
+
* `IfcSignalType`, …) while working perfectly on IFC2X3 and IFC4. That is a
|
|
132
|
+
* no-op nobody would notice: the entity is in scope, its class name is right,
|
|
133
|
+
* `isTypeObject` is right, and only the evidence is missing.
|
|
134
|
+
*
|
|
135
|
+
* This is the same pinned-registry family as the membership defect `#2001`
|
|
136
|
+
* fixed, and it has to be answered from the same place: the inheritance chain
|
|
137
|
+
* decides *whether* to read a `Tag`, so the attribute list that decides *where*
|
|
138
|
+
* it sits must span the same schemas. `getAttributeNamesAcrossSchemas` returns
|
|
139
|
+
* the pinned result unchanged for every class the pin does know, so this is
|
|
140
|
+
* additive — no IFC2X3 or IFC4 entity's hash moves because of it.
|
|
141
|
+
*
|
|
142
|
+
* Reads the raw STEP slot rather than reusing `extractAllEntityAttributes`'
|
|
143
|
+
* display normalization: this value is hashed, not shown, so `$` (absent) is
|
|
144
|
+
* the only case that needs interpreting and it arrives as null.
|
|
145
|
+
*/
|
|
146
|
+
export function attributeAcrossSchemas(store, expressId, ifcType, attributeName) {
|
|
147
|
+
const index = getAttributeNamesAcrossSchemas(ifcType).indexOf(attributeName);
|
|
148
|
+
if (index < 0)
|
|
149
|
+
return undefined;
|
|
150
|
+
// @raw-entity-enumeration-ok this point source slot is the base value; buildDataInput applies pending named and positional Tag edits
|
|
151
|
+
const ref = store.entityIndex.byId.get(expressId);
|
|
152
|
+
if (!ref)
|
|
153
|
+
return undefined;
|
|
154
|
+
const raw = new EntityExtractor(store.source).extractEntity(ref)?.attributes?.[index];
|
|
155
|
+
return typeof raw === 'string' || typeof raw === 'number' ? String(raw) : undefined;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* An overlay override wins whenever one exists, including when it is empty —
|
|
159
|
+
* `entity_set_attribute` with `''` clears the attribute, and falling back to
|
|
160
|
+
* the stored value there would hash the edit away.
|
|
161
|
+
*/
|
|
162
|
+
export function override(edited, stored) {
|
|
163
|
+
const value = edited !== undefined ? edited : stored;
|
|
164
|
+
return value ? value : undefined;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=diff-authored-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-authored-keys.js","sourceRoot":"","sources":["../../src/tools/diff-authored-keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type PendingOverlay } from '../overlay.js';
|
|
2
|
+
import type { LoadedModel } from '../context.js';
|
|
3
|
+
/** Default cap on the listed `contentMatches`. Per-kind totals are reported
|
|
4
|
+
* whole, so the cap bounds the payload without hiding anything. */
|
|
5
|
+
export declare const DEFAULT_MAX_MATCHES = 200;
|
|
6
|
+
/**
|
|
7
|
+
* Default cap on the GlobalIds listed per side of one match.
|
|
8
|
+
*
|
|
9
|
+
* `max_matches` bounds how many groups come back, not how big one is, and a
|
|
10
|
+
* `duplicated` / `deduplicated` / `ambiguous` group is a set of candidates that
|
|
11
|
+
* in a repetitive model can run to thousands of entities on each side. Without
|
|
12
|
+
* this, `max_matches: 1` could still return a model-sized payload and overflow
|
|
13
|
+
* the context window the cap exists to protect.
|
|
14
|
+
*
|
|
15
|
+
* 20 is chosen against what the list is *for*. A group is the engine declining
|
|
16
|
+
* to guess, so its members are a hand-off to the caller; twenty candidates on a
|
|
17
|
+
* side is already past what anyone resolves by reading a list rather than by
|
|
18
|
+
* querying the two models. The overwhelmingly common match is a 1:1 `renamed`
|
|
19
|
+
* pair, which this never touches. Every group still reports `baseCount` and
|
|
20
|
+
* `headCount` whole, computed before the cap for the same reason
|
|
21
|
+
* `contentMatchCounts` is: truncation must never be what makes a model look
|
|
22
|
+
* cleanly matched.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_MAX_GROUP_MEMBERS = 20;
|
|
25
|
+
/** Run the real `@ifc-lite/diff` engine over two loaded models. See the
|
|
26
|
+
* module doc for the scope and abstention contract. */
|
|
27
|
+
export declare function contentDiff(left: LoadedModel, right: LoadedModel, overlays: {
|
|
28
|
+
left: PendingOverlay | null;
|
|
29
|
+
right: PendingOverlay | null;
|
|
30
|
+
}, maxMatches: number, maxGroupMembers: number, keyProperty?: string, splitMerge?: boolean, successors?: boolean): Record<string, unknown>;
|
|
31
|
+
/** One-line human summary of the engine result, for the text block. */
|
|
32
|
+
export declare function describeCounts(content: Record<string, unknown>): string;
|
|
33
|
+
//# sourceMappingURL=diff-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-content.d.ts","sourceRoot":"","sources":["../../src/tools/diff-content.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;oEACoE;AACpE,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAiB5C;wDACwD;AACxD,wBAAgB,WAAW,CACzB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE;IAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,EACvE,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,OAAO,EACpB,UAAU,CAAC,EAAE,OAAO,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwFzB;AAgBD,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAOvE"}
|
|
@@ -0,0 +1,158 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* `model_diff`'s `by_content` half: the real `@ifc-lite/diff` engine run over
|
|
6
|
+
* two loaded models (issue #1891), plus `split_merge` / `successors`
|
|
7
|
+
* (issue #4956). Split out of `diff.ts` for size.
|
|
8
|
+
*
|
|
9
|
+
* **Data scope only.** This server has no geometry pipeline, so there is no
|
|
10
|
+
* world geometry hash and no bounding box; `scope: 'data'` is the honest
|
|
11
|
+
* description of what it can see, and every unambiguous 1:1 content match is
|
|
12
|
+
* therefore reported as `renamed` rather than `moved`/`reshaped`. `split_merge`
|
|
13
|
+
* and `successors` are geometry-only stages: they are threaded through to
|
|
14
|
+
* `diffModels` so the params exist and cost nothing today, but on this server
|
|
15
|
+
* they currently produce no claims either way (`diff.splitMerges` /
|
|
16
|
+
* `diff.successors` stay `undefined`, the engine's abstention) — they start
|
|
17
|
+
* producing claims the moment this server gains a geometry pass, with no
|
|
18
|
+
* further plumbing change. See `diff-fingerprints.ts`.
|
|
19
|
+
*/
|
|
20
|
+
import { diffModels } from '@ifc-lite/diff';
|
|
21
|
+
import { buildModelFingerprints } from './diff-fingerprints.js';
|
|
22
|
+
import { fallbackPairDuplicateAuthoredKeys } from './diff-authored-keys.js';
|
|
23
|
+
import { pendingMutationsField } from '../overlay.js';
|
|
24
|
+
/** Default cap on the listed `contentMatches`. Per-kind totals are reported
|
|
25
|
+
* whole, so the cap bounds the payload without hiding anything. */
|
|
26
|
+
export const DEFAULT_MAX_MATCHES = 200;
|
|
27
|
+
/**
|
|
28
|
+
* Default cap on the GlobalIds listed per side of one match.
|
|
29
|
+
*
|
|
30
|
+
* `max_matches` bounds how many groups come back, not how big one is, and a
|
|
31
|
+
* `duplicated` / `deduplicated` / `ambiguous` group is a set of candidates that
|
|
32
|
+
* in a repetitive model can run to thousands of entities on each side. Without
|
|
33
|
+
* this, `max_matches: 1` could still return a model-sized payload and overflow
|
|
34
|
+
* the context window the cap exists to protect.
|
|
35
|
+
*
|
|
36
|
+
* 20 is chosen against what the list is *for*. A group is the engine declining
|
|
37
|
+
* to guess, so its members are a hand-off to the caller; twenty candidates on a
|
|
38
|
+
* side is already past what anyone resolves by reading a list rather than by
|
|
39
|
+
* querying the two models. The overwhelmingly common match is a 1:1 `renamed`
|
|
40
|
+
* pair, which this never touches. Every group still reports `baseCount` and
|
|
41
|
+
* `headCount` whole, computed before the cap for the same reason
|
|
42
|
+
* `contentMatchCounts` is: truncation must never be what makes a model look
|
|
43
|
+
* cleanly matched.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_MAX_GROUP_MEMBERS = 20;
|
|
46
|
+
/**
|
|
47
|
+
* Kinds an agent has to resolve itself, listed before the ones the engine
|
|
48
|
+
* already retired. Truncating the list must never be what drops the
|
|
49
|
+
* "we could not tell" groups.
|
|
50
|
+
*/
|
|
51
|
+
const UNRESOLVED_FIRST = [
|
|
52
|
+
'ambiguous',
|
|
53
|
+
'duplicated',
|
|
54
|
+
'deduplicated',
|
|
55
|
+
'moved',
|
|
56
|
+
'reshaped',
|
|
57
|
+
'respecified',
|
|
58
|
+
'renamed',
|
|
59
|
+
];
|
|
60
|
+
/** Run the real `@ifc-lite/diff` engine over two loaded models. See the
|
|
61
|
+
* module doc for the scope and abstention contract. */
|
|
62
|
+
export function contentDiff(left, right, overlays, maxMatches, maxGroupMembers, keyProperty, splitMerge, successors) {
|
|
63
|
+
// Authored keys (issue #4955): `prop:<value>` where the model maintains one,
|
|
64
|
+
// GlobalId otherwise; a value two entities share is refused for both and
|
|
65
|
+
// reported so the agent sees why those two fell back.
|
|
66
|
+
const duplicateAuthoredKeys = new Map();
|
|
67
|
+
const adapter = { keyProperty, duplicateAuthoredKeys };
|
|
68
|
+
const baseFingerprints = buildModelFingerprints(left.store, overlays.left, adapter);
|
|
69
|
+
const headFingerprints = buildModelFingerprints(right.store, overlays.right, adapter);
|
|
70
|
+
fallbackPairDuplicateAuthoredKeys([
|
|
71
|
+
{ fingerprints: baseFingerprints, store: left.store, overlay: overlays.left },
|
|
72
|
+
{ fingerprints: headFingerprints, store: right.store, overlay: overlays.right },
|
|
73
|
+
], duplicateAuthoredKeys);
|
|
74
|
+
const diff = diffModels(baseFingerprints, headFingerprints, {
|
|
75
|
+
scope: 'data',
|
|
76
|
+
matchUnpairedByContent: true,
|
|
77
|
+
detectSplitMerge: splitMerge,
|
|
78
|
+
detectSuccessors: successors,
|
|
79
|
+
});
|
|
80
|
+
const matches = [...(diff.contentMatches ?? [])].sort((a, b) => UNRESOLVED_FIRST.indexOf(a.kind) - UNRESOLVED_FIRST.indexOf(b.kind));
|
|
81
|
+
const byKind = {};
|
|
82
|
+
for (const match of matches)
|
|
83
|
+
byKind[match.kind] = (byKind[match.kind] ?? 0) + 1;
|
|
84
|
+
return {
|
|
85
|
+
scope: diff.scope,
|
|
86
|
+
keyProperty: keyProperty ?? null,
|
|
87
|
+
duplicateAuthoredKeys: [...duplicateAuthoredKeys.keys()].sort(),
|
|
88
|
+
counts: diff.counts,
|
|
89
|
+
// Whole totals, computed before the cap: an agent reading `contentMatches`
|
|
90
|
+
// can always tell whether the list it got is the whole story.
|
|
91
|
+
contentMatchCounts: byKind,
|
|
92
|
+
contentMatches: matches.slice(0, maxMatches).map((match) => ({
|
|
93
|
+
kind: match.kind,
|
|
94
|
+
ifcType: match.base[0]?.ifcType ?? match.head[0]?.ifcType,
|
|
95
|
+
// Groups are reported as groups. Collapsing a `duplicated` or
|
|
96
|
+
// `ambiguous` set to a single pair would be the engine guessing, which
|
|
97
|
+
// is exactly what it refuses to do (#1923). Long groups are cut to
|
|
98
|
+
// `maxGroupMembers` per side and say so, whole size included, so a cut
|
|
99
|
+
// group still reads as a group of that size.
|
|
100
|
+
...listSide('base', match.base, maxGroupMembers),
|
|
101
|
+
...listSide('head', match.head, maxGroupMembers),
|
|
102
|
+
...(match.distance !== undefined ? { distance: match.distance } : {}),
|
|
103
|
+
})),
|
|
104
|
+
truncatedMatches: Math.max(0, matches.length - maxMatches),
|
|
105
|
+
// Absent (not `[]`) exactly when the stage did not run, or ran and the
|
|
106
|
+
// geometry abstention fired — this server's current state, always, until
|
|
107
|
+
// it has a geometry pipeline. The engine's "absent means not proved"
|
|
108
|
+
// contract, preserved rather than flattened.
|
|
109
|
+
splitMerges: diff.splitMerges?.map((claim) => ({
|
|
110
|
+
kind: claim.kind,
|
|
111
|
+
confidence: claim.confidence,
|
|
112
|
+
whole: claim.whole.key,
|
|
113
|
+
pieces: claim.pieces.map((piece) => piece.key),
|
|
114
|
+
})),
|
|
115
|
+
successors: diff.successors?.map((claim) => ({
|
|
116
|
+
confidence: claim.confidence,
|
|
117
|
+
base: claim.base.key,
|
|
118
|
+
head: claim.head.key,
|
|
119
|
+
overlap: claim.overlap,
|
|
120
|
+
distance: claim.distance,
|
|
121
|
+
})),
|
|
122
|
+
// Uncommitted edits are folded into the comparison; saying how many there
|
|
123
|
+
// are is what separates "the two files differ" from "this session has
|
|
124
|
+
// edits it has not written yet". Absent when neither model has any.
|
|
125
|
+
//
|
|
126
|
+
// `pendingMutations` is the scalar every other payload carries; the split a
|
|
127
|
+
// two-model comparison can additionally give lives under its own name rather
|
|
128
|
+
// than overloading that one.
|
|
129
|
+
...pendingMutationsField(overlays.left, overlays.right),
|
|
130
|
+
...(overlays.left || overlays.right
|
|
131
|
+
? {
|
|
132
|
+
pendingMutationsBySide: {
|
|
133
|
+
base: overlays.left?.pendingMutations ?? 0,
|
|
134
|
+
head: overlays.right?.pendingMutations ?? 0,
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
: {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** One side of a match: the (capped) keys, the whole count, and whether the
|
|
141
|
+
* list was cut. Emits `base`/`baseCount`/`baseTruncated` (or `head…`). */
|
|
142
|
+
function listSide(side, entities, maxGroupMembers) {
|
|
143
|
+
return {
|
|
144
|
+
[side]: entities.slice(0, maxGroupMembers).map((entity) => entity.key),
|
|
145
|
+
[`${side}Count`]: entities.length,
|
|
146
|
+
[`${side}Truncated`]: entities.length > maxGroupMembers,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** One-line human summary of the engine result, for the text block. */
|
|
150
|
+
export function describeCounts(content) {
|
|
151
|
+
const counts = content.counts;
|
|
152
|
+
const byKind = content.contentMatchCounts;
|
|
153
|
+
const kinds = Object.entries(byKind).map(([kind, n]) => `${n} ${kind}`).join(', ');
|
|
154
|
+
return `${counts.added} added, ${counts.modified} modified, ${counts.deleted} deleted, `
|
|
155
|
+
+ `${counts.unchanged} unchanged`
|
|
156
|
+
+ (kinds ? `; content matches: ${kinds}` : '; no content matches');
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=diff-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-content.js","sourceRoot":"","sources":["../../src/tools/diff-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Membership and type scope for MCP model diff (#5249). */
|
|
2
|
+
import { type EffectiveEntity } from '@ifc-lite/data';
|
|
3
|
+
import { type IfcDataStore } from '@ifc-lite/parser';
|
|
4
|
+
import type { PendingOverlay } from '../overlay.js';
|
|
5
|
+
/** How an uppercase STEP type participates in the comparison. `name` is the
|
|
6
|
+
* registry's PascalCase spelling, used instead of the `EntityTable`'s
|
|
7
|
+
* `'Unknown'` for entities the table never took in. */
|
|
8
|
+
interface TypeRole {
|
|
9
|
+
role: 'independent' | 'dependent' | 'unknown';
|
|
10
|
+
name: string;
|
|
11
|
+
/** Whether the class is an `IfcTypeObject` — the gate on hashing `Tag`
|
|
12
|
+
* (issue #2021). A class no bundled schema declares is `false` rather than
|
|
13
|
+
* guessed; the fingerprint then carries no `Tag`, which is the same answer
|
|
14
|
+
* as a type object that has none. */
|
|
15
|
+
typeObject: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Classify one STEP type against the three `IfcRoot` branches.
|
|
19
|
+
*
|
|
20
|
+
* The chain has to come from **every bundled schema**, not from the parser's
|
|
21
|
+
* IFC4 codegen pin. `getInheritanceChainForEntity` answers an empty chain for
|
|
22
|
+
* any class the pin does not carry, and that is not a rare corner: IFC2X3 alone
|
|
23
|
+
* puts 23 `IfcObjectDefinition` classes there (`IfcMove`, `IfcOrderAction`,
|
|
24
|
+
* `IfcScheduleTimeControl`, `IfcSpaceProgram`, `IfcServiceLife`, …) and IFC4X3
|
|
25
|
+
* another 77. Judging those as `unknown` dropped the ones the `EntityTable`
|
|
26
|
+
* does not hold — real objects with real GlobalIds — and let through the
|
|
27
|
+
* IFC2X3-only *resource* classes it does hold under a `…STYLE` name, keyed on
|
|
28
|
+
* the Name in slot 0. Both are wrong answers about an IFC2X3 file, which is
|
|
29
|
+
* still most of what is in the wild.
|
|
30
|
+
*
|
|
31
|
+
* `unknown` is still not `dependent`: a vendor extension no schema declares has
|
|
32
|
+
* no chain to judge, so it keeps exactly the reach the `EntityTable` already
|
|
33
|
+
* gave it — which for a `…TYPE` class is a genuine GlobalId, since the parser's
|
|
34
|
+
* type-object branch is name-based and takes those in. Guessing that an
|
|
35
|
+
* unrecognised class is an `IfcObject` and reading its source record instead
|
|
36
|
+
* would cost one STEP extraction per row of every unrecognised bucket in the
|
|
37
|
+
* model, on every call, to reach entities almost no file has. The price of that
|
|
38
|
+
* choice is that a vendor `IfcRoot` subtype whose name does not end in `TYPE`
|
|
39
|
+
* stays uncompared.
|
|
40
|
+
*
|
|
41
|
+
* The one name the chain is not needed for is `IFCREL…`: that prefix is the
|
|
42
|
+
* parser's own rule for taking an unrecognised relationship into the table, and
|
|
43
|
+
* a relationship is excluded here whether any schema can confirm it or not.
|
|
44
|
+
* Without this, an unrecognised `IfcRelXxx` would be the single class of entity
|
|
45
|
+
* that got in through the relationship branch the comparison deliberately shuts.
|
|
46
|
+
*/
|
|
47
|
+
export declare function classifyType(typeKey: string): TypeRole;
|
|
48
|
+
/** Limit the canonical walk to potentially comparable classes before it visits
|
|
49
|
+
* geometry buckets. Sparse retype destinations and authored classes are added
|
|
50
|
+
* even when absent from the parsed source index. */
|
|
51
|
+
export declare function comparableEntities(store: IfcDataStore, overlay?: PendingOverlay | null): EffectiveEntity[];
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=diff-entity-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-entity-scope.d.ts","sourceRoot":"","sources":["../../src/tools/diff-entity-scope.ts"],"names":[],"mappings":"AAIA,4DAA4D;AAC5D,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;wDAEwD;AACxD,UAAU,QAAQ;IAChB,IAAI,EAAE,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb;;;0CAGsC;IACtC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAqBtD;AAED;;oDAEoD;AACpD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,GAAG,eAAe,EAAE,CA0B1G"}
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
/** Membership and type scope for MCP model diff (#5249). */
|
|
5
|
+
import { iterateEffectiveEntities } from '@ifc-lite/data';
|
|
6
|
+
import { getInheritanceChainAcrossSchemas } from '@ifc-lite/parser';
|
|
7
|
+
/**
|
|
8
|
+
* Classify one STEP type against the three `IfcRoot` branches.
|
|
9
|
+
*
|
|
10
|
+
* The chain has to come from **every bundled schema**, not from the parser's
|
|
11
|
+
* IFC4 codegen pin. `getInheritanceChainForEntity` answers an empty chain for
|
|
12
|
+
* any class the pin does not carry, and that is not a rare corner: IFC2X3 alone
|
|
13
|
+
* puts 23 `IfcObjectDefinition` classes there (`IfcMove`, `IfcOrderAction`,
|
|
14
|
+
* `IfcScheduleTimeControl`, `IfcSpaceProgram`, `IfcServiceLife`, …) and IFC4X3
|
|
15
|
+
* another 77. Judging those as `unknown` dropped the ones the `EntityTable`
|
|
16
|
+
* does not hold — real objects with real GlobalIds — and let through the
|
|
17
|
+
* IFC2X3-only *resource* classes it does hold under a `…STYLE` name, keyed on
|
|
18
|
+
* the Name in slot 0. Both are wrong answers about an IFC2X3 file, which is
|
|
19
|
+
* still most of what is in the wild.
|
|
20
|
+
*
|
|
21
|
+
* `unknown` is still not `dependent`: a vendor extension no schema declares has
|
|
22
|
+
* no chain to judge, so it keeps exactly the reach the `EntityTable` already
|
|
23
|
+
* gave it — which for a `…TYPE` class is a genuine GlobalId, since the parser's
|
|
24
|
+
* type-object branch is name-based and takes those in. Guessing that an
|
|
25
|
+
* unrecognised class is an `IfcObject` and reading its source record instead
|
|
26
|
+
* would cost one STEP extraction per row of every unrecognised bucket in the
|
|
27
|
+
* model, on every call, to reach entities almost no file has. The price of that
|
|
28
|
+
* choice is that a vendor `IfcRoot` subtype whose name does not end in `TYPE`
|
|
29
|
+
* stays uncompared.
|
|
30
|
+
*
|
|
31
|
+
* The one name the chain is not needed for is `IFCREL…`: that prefix is the
|
|
32
|
+
* parser's own rule for taking an unrecognised relationship into the table, and
|
|
33
|
+
* a relationship is excluded here whether any schema can confirm it or not.
|
|
34
|
+
* Without this, an unrecognised `IfcRelXxx` would be the single class of entity
|
|
35
|
+
* that got in through the relationship branch the comparison deliberately shuts.
|
|
36
|
+
*/
|
|
37
|
+
export function classifyType(typeKey) {
|
|
38
|
+
const upper = typeKey.toUpperCase();
|
|
39
|
+
const chain = getInheritanceChainAcrossSchemas(upper);
|
|
40
|
+
if (chain.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
role: upper.startsWith('IFCREL') ? 'dependent' : 'unknown',
|
|
43
|
+
name: typeKey,
|
|
44
|
+
typeObject: false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// The chain holds the class itself plus its supertypes; which end the leaf
|
|
48
|
+
// sits at is the schema source's business — the union walk answers leaf→root
|
|
49
|
+
// and the IFC4 pin it falls back to answers root→leaf — so find it by name.
|
|
50
|
+
const name = chain.find((ancestor) => ancestor.toUpperCase() === upper) ?? typeKey;
|
|
51
|
+
const typeObject = chain.includes('IfcTypeObject');
|
|
52
|
+
if (!chain.includes('IfcRoot'))
|
|
53
|
+
return { role: 'dependent', name, typeObject };
|
|
54
|
+
return {
|
|
55
|
+
role: chain.includes('IfcObjectDefinition') ? 'independent' : 'dependent',
|
|
56
|
+
name,
|
|
57
|
+
typeObject,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Limit the canonical walk to potentially comparable classes before it visits
|
|
61
|
+
* geometry buckets. Sparse retype destinations and authored classes are added
|
|
62
|
+
* even when absent from the parsed source index. */
|
|
63
|
+
export function comparableEntities(store, overlay) {
|
|
64
|
+
const types = new Set();
|
|
65
|
+
// @raw-entity-enumeration-ok the diff chooses candidate source CLASS keys only; iterateEffectiveEntities handles membership below
|
|
66
|
+
for (const type of store.entityIndex.byType.keys()) {
|
|
67
|
+
if (classifyType(type).role !== 'dependent')
|
|
68
|
+
types.add(type);
|
|
69
|
+
}
|
|
70
|
+
const retypes = overlay?.typeMutations?.();
|
|
71
|
+
for (const change of retypes?.values() ?? []) {
|
|
72
|
+
if (classifyType(change.newType).role !== 'dependent')
|
|
73
|
+
types.add(change.newType);
|
|
74
|
+
}
|
|
75
|
+
const created = overlay?.created ?? [];
|
|
76
|
+
for (const entity of created) {
|
|
77
|
+
if (classifyType(entity.ifcType).role !== 'dependent')
|
|
78
|
+
types.add(entity.ifcType);
|
|
79
|
+
}
|
|
80
|
+
if (types.size === 0)
|
|
81
|
+
return [];
|
|
82
|
+
const createdById = new Map(created.map(entity => [entity.expressId, entity]));
|
|
83
|
+
const membership = overlay ? {
|
|
84
|
+
isDeleted: (id) => overlay.deleted.has(id),
|
|
85
|
+
getNewEntities: () => created.map(entity => ({ expressId: entity.expressId, type: entity.ifcType })),
|
|
86
|
+
getNewEntity: (id) => {
|
|
87
|
+
const entity = createdById.get(id);
|
|
88
|
+
return entity ? { type: entity.ifcType } : null;
|
|
89
|
+
},
|
|
90
|
+
getTypeMutations: () => retypes ?? new Map(),
|
|
91
|
+
} : null;
|
|
92
|
+
return [...iterateEffectiveEntities(store, membership, [...types])];
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=diff-entity-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-entity-scope.js","sourceRoot":"","sources":["../../src/tools/diff-entity-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -84,7 +84,9 @@
|
|
|
84
84
|
*/
|
|
85
85
|
import { type EntityFingerprint } from '@ifc-lite/diff';
|
|
86
86
|
import { type IfcDataStore } from '@ifc-lite/parser';
|
|
87
|
-
import type
|
|
87
|
+
import { type FingerprintAdapterOptions } from './diff-authored-keys.js';
|
|
88
|
+
export { AUTHORED_KEY_PREFIX, type FingerprintAdapterOptions } from './diff-authored-keys.js';
|
|
89
|
+
import { type PendingOverlay } from '../overlay.js';
|
|
88
90
|
/** Adapter handle threaded through the diff: the entity's express id. */
|
|
89
91
|
export type DiffRef = number;
|
|
90
92
|
/**
|
|
@@ -102,5 +104,5 @@ export type DiffRef = number;
|
|
|
102
104
|
* values. Omitting it (the CLI twin has no session to overlay) is the original
|
|
103
105
|
* store-only behaviour exactly.
|
|
104
106
|
*/
|
|
105
|
-
export declare function buildModelFingerprints(store: IfcDataStore, overlay?: PendingOverlay | null): EntityFingerprint<DiffRef>[];
|
|
107
|
+
export declare function buildModelFingerprints(store: IfcDataStore, overlay?: PendingOverlay | null, options?: FingerprintAdapterOptions): EntityFingerprint<DiffRef>[];
|
|
106
108
|
//# sourceMappingURL=diff-fingerprints.d.ts.map
|