@ifc-lite/ifcx 1.2.0 → 1.3.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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Federated Composition Engine for IFCX
3
+ *
4
+ * Composes multiple IFCX files (layers) into a unified stage using
5
+ * USD-inspired layer semantics:
6
+ * - Higher layers (lower index) have stronger opinions
7
+ * - Attributes merge with strongest layer winning
8
+ * - Children accumulate across layers
9
+ * - null values remove attributes/children
10
+ * - Inheritance is resolved across layer boundaries
11
+ */
12
+ import type { IfcxFile, ComposedNode } from './types.js';
13
+ import { LayerStack } from './layer-stack.js';
14
+ import { PathIndex } from './path-resolver.js';
15
+ /**
16
+ * Options for federated composition.
17
+ */
18
+ export interface ComposeOptions {
19
+ /** Progress callback */
20
+ onProgress?: (phase: string, percent: number) => void;
21
+ /** Maximum inheritance depth (default: 10) */
22
+ maxInheritDepth?: number;
23
+ }
24
+ /**
25
+ * Extended ComposedNode with source tracking.
26
+ */
27
+ export interface ComposedNodeWithSources extends ComposedNode {
28
+ /** Track which layer contributed each attribute */
29
+ attributeSources: Map<string, {
30
+ layerId: string;
31
+ layerName: string;
32
+ }>;
33
+ /** All layers that contributed to this node */
34
+ contributingLayers: Set<string>;
35
+ }
36
+ /**
37
+ * Result of federated composition.
38
+ */
39
+ export interface FederatedCompositionResult {
40
+ /** Composed nodes by path */
41
+ composed: Map<string, ComposedNodeWithSources>;
42
+ /** Path index for lookups */
43
+ pathIndex: PathIndex;
44
+ /** Root nodes (no parent) */
45
+ roots: ComposedNodeWithSources[];
46
+ /** Statistics */
47
+ stats: {
48
+ totalNodes: number;
49
+ layersUsed: number;
50
+ inheritanceResolutions: number;
51
+ crossLayerReferences: number;
52
+ };
53
+ }
54
+ /**
55
+ * Compose multiple IFCX layers into a unified stage.
56
+ *
57
+ * Algorithm:
58
+ * 1. Build path index across all layers
59
+ * 2. Collect all unique paths
60
+ * 3. For each path, merge nodes from all layers (strongest first)
61
+ * 4. Resolve inheritance references (may cross layers)
62
+ * 5. Build parent-child tree from children references
63
+ */
64
+ export declare function composeFederated(layerStack: LayerStack, options?: ComposeOptions): FederatedCompositionResult;
65
+ /**
66
+ * Compose a single IFCX file (convenience wrapper).
67
+ */
68
+ export declare function composeSingleFile(file: IfcxFile): FederatedCompositionResult;
69
+ //# sourceMappingURL=federated-composition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federated-composition.d.ts","sourceRoot":"","sources":["../src/federated-composition.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAY,YAAY,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAa,MAAM,oBAAoB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAgBD;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,mDAAmD;IACnD,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,+CAA+C;IAC/C,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,6BAA6B;IAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IAC/C,6BAA6B;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,6BAA6B;IAC7B,KAAK,EAAE,uBAAuB,EAAE,CAAC;IACjC,iBAAiB;IACjB,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,cAAmB,GAC3B,0BAA0B,CAiG5B;AAmQD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,0BAA0B,CAI5E"}
@@ -0,0 +1,300 @@
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 { LayerStack } from './layer-stack.js';
5
+ import { PathIndex } from './path-resolver.js';
6
+ /**
7
+ * Compose multiple IFCX layers into a unified stage.
8
+ *
9
+ * Algorithm:
10
+ * 1. Build path index across all layers
11
+ * 2. Collect all unique paths
12
+ * 3. For each path, merge nodes from all layers (strongest first)
13
+ * 4. Resolve inheritance references (may cross layers)
14
+ * 5. Build parent-child tree from children references
15
+ */
16
+ export function composeFederated(layerStack, options = {}) {
17
+ const { onProgress, maxInheritDepth = 10 } = options;
18
+ onProgress?.('indexing', 0);
19
+ // Get enabled layers in strength order
20
+ const layers = layerStack.getEnabledLayers();
21
+ if (layers.length === 0) {
22
+ return {
23
+ composed: new Map(),
24
+ pathIndex: new PathIndex(),
25
+ roots: [],
26
+ stats: {
27
+ totalNodes: 0,
28
+ layersUsed: 0,
29
+ inheritanceResolutions: 0,
30
+ crossLayerReferences: 0,
31
+ },
32
+ };
33
+ }
34
+ // Build path index
35
+ const pathIndex = new PathIndex();
36
+ pathIndex.buildIndex(layers);
37
+ onProgress?.('indexing', 100);
38
+ onProgress?.('merging', 0);
39
+ // Phase 1: Collect and merge all nodes by path
40
+ const preComposed = new Map();
41
+ const allPaths = layerStack.getAllPaths();
42
+ let pathCount = 0;
43
+ const totalPaths = allPaths.size;
44
+ for (const path of allPaths) {
45
+ // Skip hierarchical paths - they're references, not definitions
46
+ if (path.includes('/'))
47
+ continue;
48
+ const pre = mergeNodesForPath(path, layers);
49
+ preComposed.set(path, pre);
50
+ pathCount++;
51
+ if (pathCount % 100 === 0) {
52
+ onProgress?.('merging', Math.round((pathCount / totalPaths) * 100));
53
+ }
54
+ }
55
+ onProgress?.('merging', 100);
56
+ onProgress?.('inheritance', 0);
57
+ // Phase 2: Resolve inheritance
58
+ let inheritanceResolutions = 0;
59
+ let crossLayerReferences = 0;
60
+ const visited = new Set();
61
+ for (const [path, pre] of preComposed) {
62
+ const result = resolveInheritance(path, pre, preComposed, pathIndex, visited, new Set(), maxInheritDepth);
63
+ inheritanceResolutions += result.resolutions;
64
+ crossLayerReferences += result.crossLayer;
65
+ }
66
+ onProgress?.('inheritance', 100);
67
+ onProgress?.('tree', 0);
68
+ // Phase 3: Build composed tree
69
+ const composed = new Map();
70
+ for (const [path] of preComposed) {
71
+ if (!composed.has(path)) {
72
+ composeNode(path, preComposed, composed, new Set(), layers, pathIndex);
73
+ }
74
+ }
75
+ onProgress?.('tree', 100);
76
+ // Find roots
77
+ const roots = findRoots(composed);
78
+ return {
79
+ composed,
80
+ pathIndex,
81
+ roots,
82
+ stats: {
83
+ totalNodes: composed.size,
84
+ layersUsed: layers.length,
85
+ inheritanceResolutions,
86
+ crossLayerReferences,
87
+ },
88
+ };
89
+ }
90
+ /**
91
+ * Merge all nodes for a path across layers.
92
+ * Layers are processed in strength order (strongest first).
93
+ */
94
+ function mergeNodesForPath(path, layers) {
95
+ const result = {
96
+ path,
97
+ children: {},
98
+ inherits: {},
99
+ attributes: {},
100
+ attributeSources: new Map(),
101
+ definedInLayers: new Set(),
102
+ };
103
+ // Process layers in reverse order (weakest first) so stronger layers override
104
+ for (let i = layers.length - 1; i >= 0; i--) {
105
+ const layer = layers[i];
106
+ const nodes = layer.nodesByPath.get(path);
107
+ if (!nodes)
108
+ continue;
109
+ result.definedInLayers.add(layer.id);
110
+ // Process all nodes for this path in this layer
111
+ for (const node of nodes) {
112
+ // Merge children
113
+ if (node.children) {
114
+ for (const [key, value] of Object.entries(node.children)) {
115
+ if (value === null) {
116
+ // null removes the child
117
+ delete result.children[key];
118
+ }
119
+ else {
120
+ result.children[key] = value;
121
+ }
122
+ }
123
+ }
124
+ // Merge inherits
125
+ if (node.inherits) {
126
+ for (const [key, value] of Object.entries(node.inherits)) {
127
+ if (value === null) {
128
+ delete result.inherits[key];
129
+ }
130
+ else {
131
+ result.inherits[key] = value;
132
+ }
133
+ }
134
+ }
135
+ // Merge attributes
136
+ if (node.attributes) {
137
+ for (const [key, value] of Object.entries(node.attributes)) {
138
+ result.attributes[key] = value;
139
+ result.attributeSources.set(key, layer.id);
140
+ }
141
+ }
142
+ }
143
+ }
144
+ return result;
145
+ }
146
+ /**
147
+ * Resolve inheritance for a pre-composed node.
148
+ */
149
+ function resolveInheritance(path, pre, allNodes, pathIndex, resolved, visiting, maxDepth, depth = 0) {
150
+ if (resolved.has(path)) {
151
+ return { resolutions: 0, crossLayer: 0 };
152
+ }
153
+ if (visiting.has(path)) {
154
+ // Circular inheritance - skip
155
+ console.warn(`Circular inheritance detected at path: ${path}`);
156
+ return { resolutions: 0, crossLayer: 0 };
157
+ }
158
+ if (depth > maxDepth) {
159
+ console.warn(`Max inheritance depth exceeded at path: ${path}`);
160
+ return { resolutions: 0, crossLayer: 0 };
161
+ }
162
+ visiting.add(path);
163
+ let resolutions = 0;
164
+ let crossLayer = 0;
165
+ // Resolve each inherit reference
166
+ for (const [, inheritPath] of Object.entries(pre.inherits)) {
167
+ if (!inheritPath)
168
+ continue;
169
+ // Try to resolve the path (may be in different layer)
170
+ const resolvedPath = pathIndex.resolvePath(inheritPath);
171
+ if (!resolvedPath)
172
+ continue;
173
+ const inherited = allNodes.get(resolvedPath);
174
+ if (!inherited)
175
+ continue;
176
+ // Check if cross-layer reference
177
+ const inheritedLayers = inherited.definedInLayers;
178
+ const hasCommonLayer = [...pre.definedInLayers].some((l) => inheritedLayers.has(l));
179
+ if (!hasCommonLayer) {
180
+ crossLayer++;
181
+ }
182
+ // Recursively resolve inheritance of the inherited node first
183
+ const subResult = resolveInheritance(resolvedPath, inherited, allNodes, pathIndex, resolved, visiting, maxDepth, depth + 1);
184
+ resolutions += subResult.resolutions;
185
+ crossLayer += subResult.crossLayer;
186
+ // Merge inherited data (inherited values are weaker - don't override existing)
187
+ for (const [key, value] of Object.entries(inherited.attributes)) {
188
+ if (!(key in pre.attributes)) {
189
+ pre.attributes[key] = value;
190
+ // Track source from inherited node
191
+ const inheritedSource = inherited.attributeSources.get(key);
192
+ if (inheritedSource) {
193
+ pre.attributeSources.set(key, inheritedSource);
194
+ }
195
+ }
196
+ }
197
+ for (const [key, value] of Object.entries(inherited.children)) {
198
+ if (!(key in pre.children)) {
199
+ pre.children[key] = value;
200
+ }
201
+ }
202
+ resolutions++;
203
+ }
204
+ visiting.delete(path);
205
+ resolved.add(path);
206
+ return { resolutions, crossLayer };
207
+ }
208
+ /**
209
+ * Compose a single node and its children.
210
+ */
211
+ function composeNode(path, preComposed, composed, visiting, layers, pathIndex) {
212
+ // Already composed?
213
+ const existing = composed.get(path);
214
+ if (existing)
215
+ return existing;
216
+ // Cycle detection
217
+ if (visiting.has(path)) {
218
+ // Return a stub to break the cycle
219
+ const stub = {
220
+ path,
221
+ attributes: new Map(),
222
+ children: new Map(),
223
+ attributeSources: new Map(),
224
+ contributingLayers: new Set(),
225
+ };
226
+ composed.set(path, stub);
227
+ return stub;
228
+ }
229
+ visiting.add(path);
230
+ const pre = preComposed.get(path);
231
+ const node = {
232
+ path,
233
+ attributes: new Map(),
234
+ children: new Map(),
235
+ attributeSources: new Map(),
236
+ contributingLayers: new Set(pre?.definedInLayers || []),
237
+ };
238
+ if (pre) {
239
+ // Copy attributes
240
+ for (const [key, value] of Object.entries(pre.attributes)) {
241
+ node.attributes.set(key, value);
242
+ // Track source
243
+ const sourceLayerId = pre.attributeSources.get(key);
244
+ if (sourceLayerId) {
245
+ const layer = layers.find((l) => l.id === sourceLayerId);
246
+ if (layer) {
247
+ node.attributeSources.set(key, {
248
+ layerId: layer.id,
249
+ layerName: layer.name,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ // Resolve and add children
255
+ for (const [name, childPath] of Object.entries(pre.children)) {
256
+ if (!childPath)
257
+ continue;
258
+ // Child path might be hierarchical (uuid/ChildName) - resolve via pathIndex
259
+ const resolvedChildPath = pathIndex.resolvePath(childPath) || childPath;
260
+ const childPre = preComposed.get(resolvedChildPath);
261
+ if (childPre) {
262
+ const child = composeNode(resolvedChildPath, preComposed, composed, visiting, layers, pathIndex);
263
+ child.parent = node;
264
+ node.children.set(name, child);
265
+ }
266
+ }
267
+ }
268
+ visiting.delete(path);
269
+ composed.set(path, node);
270
+ return node;
271
+ }
272
+ /**
273
+ * Find root nodes (nodes with no parent).
274
+ */
275
+ function findRoots(composed) {
276
+ const childPaths = new Set();
277
+ // Collect all child paths
278
+ for (const node of composed.values()) {
279
+ for (const child of node.children.values()) {
280
+ childPaths.add(child.path);
281
+ }
282
+ }
283
+ // Roots are nodes not referenced as children
284
+ const roots = [];
285
+ for (const node of composed.values()) {
286
+ if (!childPaths.has(node.path) && !node.parent) {
287
+ roots.push(node);
288
+ }
289
+ }
290
+ return roots;
291
+ }
292
+ /**
293
+ * Compose a single IFCX file (convenience wrapper).
294
+ */
295
+ export function composeSingleFile(file) {
296
+ const stack = new LayerStack();
297
+ stack.addLayer(file, new ArrayBuffer(0), 'single');
298
+ return composeFederated(stack);
299
+ }
300
+ //# sourceMappingURL=federated-composition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federated-composition.js","sourceRoot":"","sources":["../src/federated-composition.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  import { type MeshData } from './geometry-extractor.js';
2
2
  import { StringTable } from '@ifc-lite/data';
3
3
  import type { SpatialHierarchy, EntityTable, PropertyTable, QuantityTable, RelationshipGraph } from '@ifc-lite/data';
4
+ import { LayerStack } from './layer-stack.js';
5
+ import { PathIndex } from './path-resolver.js';
4
6
  export * from './types.js';
5
7
  export { composeIfcx, findRoots, getDescendants, getPathToRoot } from './composition.js';
6
8
  export { extractEntities } from './entity-extractor.js';
7
9
  export { extractProperties, isQuantityProperty } from './property-extractor.js';
8
10
  export { extractGeometry, type MeshData } from './geometry-extractor.js';
9
11
  export { buildHierarchy } from './hierarchy-builder.js';
12
+ export { LayerStack, createLayerStack, type IfcxLayer, type LayerSource, } from './layer-stack.js';
13
+ export { PathIndex, createPathIndex, parsePath, type ParsedPath, type PathEntry, } from './path-resolver.js';
14
+ export { composeFederated, type ComposeOptions, type FederatedCompositionResult, type ComposedNodeWithSources, } from './federated-composition.js';
10
15
  /**
11
16
  * Result of parsing an IFCX file.
12
17
  * Compatible with existing ifc-lite data structures.
@@ -54,4 +59,64 @@ export declare function parseIfcx(buffer: ArrayBuffer, options?: IfcxParseOption
54
59
  * Detect if a buffer contains IFCX (JSON) or IFC (STEP) format.
55
60
  */
56
61
  export declare function detectFormat(buffer: ArrayBuffer): 'ifcx' | 'ifc' | 'unknown';
62
+ /**
63
+ * Input for federated parsing - a buffer with a name.
64
+ */
65
+ export interface FederatedFileInput {
66
+ buffer: ArrayBuffer;
67
+ name: string;
68
+ }
69
+ /**
70
+ * Options for federated IFCX parsing.
71
+ */
72
+ export interface FederatedParseOptions extends IfcxParseOptions {
73
+ /** Maximum inheritance depth (default: 10) */
74
+ maxInheritDepth?: number;
75
+ }
76
+ /**
77
+ * Result of parsing federated IFCX files.
78
+ * Extends the standard result with layer information.
79
+ */
80
+ export interface FederatedIfcxParseResult extends IfcxParseResult {
81
+ /** Layer stack with all loaded layers */
82
+ layerStack: LayerStack;
83
+ /** Path index for cross-file lookups */
84
+ pathIndex: PathIndex;
85
+ /** Composition statistics */
86
+ compositionStats: {
87
+ layersUsed: number;
88
+ inheritanceResolutions: number;
89
+ crossLayerReferences: number;
90
+ };
91
+ /** Map from path to layer IDs that define it */
92
+ pathToLayers: Map<string, string[]>;
93
+ }
94
+ /**
95
+ * Parse multiple IFCX files as federated layers.
96
+ *
97
+ * Files are loaded as layers with the first file being the base (weakest)
98
+ * and subsequent files being overlays (stronger). Layer order can be
99
+ * adjusted after parsing using the returned LayerStack.
100
+ *
101
+ * @param files - Array of file buffers with names
102
+ * @param options - Parse options
103
+ * @returns Federated parse result with layer information
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const result = await parseFederatedIfcx([
108
+ * { buffer: baseBuffer, name: 'hello-wall.ifcx' },
109
+ * { buffer: overlayBuffer, name: 'add-fire-rating.ifcx' },
110
+ * ]);
111
+ *
112
+ * // Wall now has FireRating property from overlay
113
+ * const wallProps = result.properties.getForEntity(wallId);
114
+ * ```
115
+ */
116
+ export declare function parseFederatedIfcx(files: FederatedFileInput[], options?: FederatedParseOptions): Promise<FederatedIfcxParseResult>;
117
+ /**
118
+ * Add an overlay layer to an existing federated result.
119
+ * Returns a new result with the overlay applied.
120
+ */
121
+ export declare function addIfcxOverlay(baseResult: FederatedIfcxParseResult, overlayBuffer: ArrayBuffer, overlayName: string, options?: FederatedParseOptions): Promise<FederatedIfcxParseResult>;
57
122
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEzE,OAAO,EACL,WAAW,EAIZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGrH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,QAAQ,EAAE,WAAW,CAAC;IACtB,8BAA8B;IAC9B,UAAU,EAAE,aAAa,CAAC;IAC1B,8BAA8B;IAC9B,UAAU,EAAE,aAAa,CAAC;IAC1B,yBAAyB;IACzB,aAAa,EAAE,iBAAiB,CAAC;IACjC,wBAAwB;IACxB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,wCAAwC;IACxC,OAAO,EAAE,WAAW,CAAC;IACrB,sCAAsC;IACtC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACrE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAwE1B;AAiHD;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAe5E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -7,6 +7,9 @@ import { extractProperties, isQuantityProperty } from './property-extractor.js';
7
7
  import { extractGeometry } from './geometry-extractor.js';
8
8
  import { buildHierarchy } from './hierarchy-builder.js';
9
9
  import { StringTable, RelationshipGraphBuilder, RelationshipType, QuantityTableBuilder, } from '@ifc-lite/data';
10
+ // Federated composition imports
11
+ import { createLayerStack } from './layer-stack.js';
12
+ import { composeFederated, } from './federated-composition.js';
10
13
  // Re-export types
11
14
  export * from './types.js';
12
15
  export { composeIfcx, findRoots, getDescendants, getPathToRoot } from './composition.js';
@@ -14,6 +17,10 @@ export { extractEntities } from './entity-extractor.js';
14
17
  export { extractProperties, isQuantityProperty } from './property-extractor.js';
15
18
  export { extractGeometry } from './geometry-extractor.js';
16
19
  export { buildHierarchy } from './hierarchy-builder.js';
20
+ // Re-export federated composition
21
+ export { LayerStack, createLayerStack, } from './layer-stack.js';
22
+ export { PathIndex, createPathIndex, parsePath, } from './path-resolver.js';
23
+ export { composeFederated, } from './federated-composition.js';
17
24
  /**
18
25
  * Parse an IFCX file and return data compatible with existing ifc-lite pipeline.
19
26
  */
@@ -186,4 +193,165 @@ export function detectFormat(buffer) {
186
193
  }
187
194
  return 'unknown';
188
195
  }
196
+ /**
197
+ * Parse multiple IFCX files as federated layers.
198
+ *
199
+ * Files are loaded as layers with the first file being the base (weakest)
200
+ * and subsequent files being overlays (stronger). Layer order can be
201
+ * adjusted after parsing using the returned LayerStack.
202
+ *
203
+ * @param files - Array of file buffers with names
204
+ * @param options - Parse options
205
+ * @returns Federated parse result with layer information
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const result = await parseFederatedIfcx([
210
+ * { buffer: baseBuffer, name: 'hello-wall.ifcx' },
211
+ * { buffer: overlayBuffer, name: 'add-fire-rating.ifcx' },
212
+ * ]);
213
+ *
214
+ * // Wall now has FireRating property from overlay
215
+ * const wallProps = result.properties.getForEntity(wallId);
216
+ * ```
217
+ */
218
+ export async function parseFederatedIfcx(files, options = {}) {
219
+ const startTime = performance.now();
220
+ if (files.length === 0) {
221
+ throw new Error('At least one IFCX file is required');
222
+ }
223
+ options.onProgress?.({ phase: 'parse', percent: 0 });
224
+ // Phase 1: Parse all files and build layer stack
225
+ const layerStack = createLayerStack();
226
+ let totalSize = 0;
227
+ for (let i = 0; i < files.length; i++) {
228
+ const { buffer, name } = files[i];
229
+ totalSize += buffer.byteLength;
230
+ const text = new TextDecoder().decode(buffer);
231
+ let file;
232
+ try {
233
+ file = JSON.parse(text);
234
+ }
235
+ catch (e) {
236
+ throw new Error(`Invalid IFCX file "${name}": JSON parse error - ${e}`);
237
+ }
238
+ // Validate header
239
+ if (!file.header?.ifcxVersion?.toLowerCase().includes('ifcx')) {
240
+ throw new Error(`Invalid IFCX file "${name}": missing or invalid header.ifcxVersion`);
241
+ }
242
+ // Add as layer (first file is weakest, last is strongest)
243
+ // We add at position 0 so later files become strongest
244
+ layerStack.addLayerAt(file, buffer, name, 0, {
245
+ type: 'file',
246
+ filename: name,
247
+ size: buffer.byteLength,
248
+ });
249
+ options.onProgress?.({
250
+ phase: 'parse',
251
+ percent: Math.round(((i + 1) / files.length) * 100),
252
+ });
253
+ }
254
+ options.onProgress?.({ phase: 'compose', percent: 0 });
255
+ // Phase 2: Compose federated layers
256
+ const compositionResult = composeFederated(layerStack, {
257
+ onProgress: (phase, percent) => {
258
+ options.onProgress?.({ phase: `compose-${phase}`, percent });
259
+ },
260
+ maxInheritDepth: options.maxInheritDepth,
261
+ });
262
+ options.onProgress?.({ phase: 'compose', percent: 100 });
263
+ // Convert composed nodes to standard ComposedNode format for extractors
264
+ const composed = new Map();
265
+ for (const [path, node] of compositionResult.composed) {
266
+ composed.set(path, node);
267
+ }
268
+ // Phase 3: Extract entities
269
+ options.onProgress?.({ phase: 'entities', percent: 0 });
270
+ const strings = new StringTable();
271
+ const { entities, pathToId, idToPath } = extractEntities(composed, strings);
272
+ options.onProgress?.({ phase: 'entities', percent: 100 });
273
+ // Phase 4: Extract properties
274
+ options.onProgress?.({ phase: 'properties', percent: 0 });
275
+ const properties = extractProperties(composed, pathToId, strings);
276
+ options.onProgress?.({ phase: 'properties', percent: 100 });
277
+ // Phase 5: Extract geometry
278
+ options.onProgress?.({ phase: 'geometry', percent: 0 });
279
+ const meshes = extractGeometry(composed, pathToId);
280
+ options.onProgress?.({ phase: 'geometry', percent: 100 });
281
+ // Phase 6: Build hierarchy
282
+ options.onProgress?.({ phase: 'hierarchy', percent: 0 });
283
+ const spatialHierarchy = buildHierarchy(composed, pathToId);
284
+ options.onProgress?.({ phase: 'hierarchy', percent: 100 });
285
+ // Phase 7: Build relationships
286
+ options.onProgress?.({ phase: 'relationships', percent: 0 });
287
+ const relationships = buildRelationships(composed, pathToId);
288
+ options.onProgress?.({ phase: 'relationships', percent: 100 });
289
+ // Phase 8: Build quantities
290
+ const quantities = buildQuantities(composed, pathToId, strings);
291
+ // Build path-to-layers map
292
+ const pathToLayers = new Map();
293
+ for (const [path, node] of compositionResult.composed) {
294
+ pathToLayers.set(path, Array.from(node.contributingLayers));
295
+ }
296
+ const parseTime = performance.now() - startTime;
297
+ return {
298
+ entities,
299
+ properties,
300
+ quantities,
301
+ relationships,
302
+ spatialHierarchy,
303
+ strings,
304
+ meshes,
305
+ pathToId,
306
+ idToPath,
307
+ schemaVersion: 'IFC5',
308
+ fileSize: totalSize,
309
+ entityCount: entities.count,
310
+ parseTime,
311
+ // Federated-specific fields
312
+ layerStack,
313
+ pathIndex: compositionResult.pathIndex,
314
+ compositionStats: {
315
+ layersUsed: compositionResult.stats.layersUsed,
316
+ inheritanceResolutions: compositionResult.stats.inheritanceResolutions,
317
+ crossLayerReferences: compositionResult.stats.crossLayerReferences,
318
+ },
319
+ pathToLayers,
320
+ };
321
+ }
322
+ /**
323
+ * Add an overlay layer to an existing federated result.
324
+ * Returns a new result with the overlay applied.
325
+ */
326
+ export async function addIfcxOverlay(baseResult, overlayBuffer, overlayName, options = {}) {
327
+ // Parse overlay file
328
+ const text = new TextDecoder().decode(overlayBuffer);
329
+ let file;
330
+ try {
331
+ file = JSON.parse(text);
332
+ }
333
+ catch (e) {
334
+ throw new Error(`Invalid IFCX overlay "${overlayName}": JSON parse error - ${e}`);
335
+ }
336
+ // Validate header
337
+ if (!file.header?.ifcxVersion?.toLowerCase().includes('ifcx')) {
338
+ throw new Error(`Invalid IFCX overlay "${overlayName}": missing or invalid header.ifcxVersion`);
339
+ }
340
+ // Add to layer stack (at top = strongest)
341
+ baseResult.layerStack.addLayer(file, overlayBuffer, overlayName, {
342
+ type: 'file',
343
+ filename: overlayName,
344
+ size: overlayBuffer.byteLength,
345
+ });
346
+ // Re-compose with new layer
347
+ // Collect all files from the layer stack
348
+ const files = baseResult.layerStack
349
+ .getLayers()
350
+ .map((layer) => ({
351
+ buffer: layer.buffer,
352
+ name: layer.name,
353
+ }))
354
+ .reverse(); // Reverse because layers are stored strongest-first
355
+ return parseFederatedIfcx(files, options);
356
+ }
189
357
  //# sourceMappingURL=index.js.map