brepjs-bim 0.5.0 → 0.6.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.
@@ -16615,6 +16615,8 @@ function addOpenings(model, host, wallId, containerId, idByKeyPath) {
16615
16615
  if (opening.type !== "Opening") continue;
16616
16616
  const fill = opening.children[0];
16617
16617
  if (fill === void 0) return (0, brepjs.err)(specError("FAMILIES_OPENING_NO_FILL", `familiesToBim: opening '${opening.keyPath}' has no fill element`));
16618
+ const keyed = requireKeyed(opening);
16619
+ if (!keyed.ok) return keyed;
16618
16620
  const fillT = peelTranslates(opening.geometry).total;
16619
16621
  const added = addFill(model, fill, {
16620
16622
  materialName: SPEC_DEFAULTS.materialName,
@@ -16634,6 +16636,13 @@ function addOpenings(model, host, wallId, containerId, idByKeyPath) {
16634
16636
  }
16635
16637
  return (0, brepjs.ok)(void 0);
16636
16638
  }
16639
+ /** Every element that mints an IFC identity needs an explicit key: an
16640
+ * index-fallback path is order-dependent, which would silently break the
16641
+ * reorder-stable GlobalId contract. */
16642
+ function requireKeyed(el) {
16643
+ if (el.keyed) return (0, brepjs.ok)(void 0);
16644
+ return (0, brepjs.err)(specError("FAMILIES_UNKEYED_ELEMENT", `familiesToBim: '${el.keyPath}' has no explicit key — IFC identity needs order-independent key paths (add a key to the element)`));
16645
+ }
16637
16646
  /**
16638
16647
  * Project a resolved families tree into an eager BimModel. The caller owns
16639
16648
  * the returned model (`using`); families stays domain-neutral — this adapter
@@ -16650,6 +16659,8 @@ function familiesToBim(root, options) {
16650
16659
  const walk = (el, storeyId) => {
16651
16660
  let containerId = storeyId;
16652
16661
  if (el.type === "Storey") {
16662
+ const keyed = requireKeyed(el);
16663
+ if (!keyed.ok) return keyed;
16653
16664
  const id = model.addStorey({
16654
16665
  name: el.attributes["name"] ?? el.keyPath,
16655
16666
  elevation: el.props["elevation"] ?? 0
@@ -16658,6 +16669,8 @@ function familiesToBim(root, options) {
16658
16669
  idByKeyPath.set(el.keyPath, id);
16659
16670
  containerId = id;
16660
16671
  } else if (el.type === "Wall" || el.type === "Slab") {
16672
+ const keyed = requireKeyed(el);
16673
+ if (!keyed.ok) return keyed;
16661
16674
  const parsed = el.type === "Wall" ? parseWallSpec(specInput(el)) : parseSlabSpec(specInput(el));
16662
16675
  if (!parsed.ok) return parsed;
16663
16676
  const added = el.type === "Wall" ? model.addWall(parsed.value, { stableKey: el.keyPath }) : model.addSlab(parsed.value, { stableKey: el.keyPath });
@@ -16592,6 +16592,8 @@ function addOpenings(model, host, wallId, containerId, idByKeyPath) {
16592
16592
  if (opening.type !== "Opening") continue;
16593
16593
  const fill = opening.children[0];
16594
16594
  if (fill === void 0) return err(specError("FAMILIES_OPENING_NO_FILL", `familiesToBim: opening '${opening.keyPath}' has no fill element`));
16595
+ const keyed = requireKeyed(opening);
16596
+ if (!keyed.ok) return keyed;
16595
16597
  const fillT = peelTranslates(opening.geometry).total;
16596
16598
  const added = addFill(model, fill, {
16597
16599
  materialName: SPEC_DEFAULTS.materialName,
@@ -16611,6 +16613,13 @@ function addOpenings(model, host, wallId, containerId, idByKeyPath) {
16611
16613
  }
16612
16614
  return ok(void 0);
16613
16615
  }
16616
+ /** Every element that mints an IFC identity needs an explicit key: an
16617
+ * index-fallback path is order-dependent, which would silently break the
16618
+ * reorder-stable GlobalId contract. */
16619
+ function requireKeyed(el) {
16620
+ if (el.keyed) return ok(void 0);
16621
+ return err(specError("FAMILIES_UNKEYED_ELEMENT", `familiesToBim: '${el.keyPath}' has no explicit key — IFC identity needs order-independent key paths (add a key to the element)`));
16622
+ }
16614
16623
  /**
16615
16624
  * Project a resolved families tree into an eager BimModel. The caller owns
16616
16625
  * the returned model (`using`); families stays domain-neutral — this adapter
@@ -16627,6 +16636,8 @@ function familiesToBim(root, options) {
16627
16636
  const walk = (el, storeyId) => {
16628
16637
  let containerId = storeyId;
16629
16638
  if (el.type === "Storey") {
16639
+ const keyed = requireKeyed(el);
16640
+ if (!keyed.ok) return keyed;
16630
16641
  const id = model.addStorey({
16631
16642
  name: el.attributes["name"] ?? el.keyPath,
16632
16643
  elevation: el.props["elevation"] ?? 0
@@ -16635,6 +16646,8 @@ function familiesToBim(root, options) {
16635
16646
  idByKeyPath.set(el.keyPath, id);
16636
16647
  containerId = id;
16637
16648
  } else if (el.type === "Wall" || el.type === "Slab") {
16649
+ const keyed = requireKeyed(el);
16650
+ if (!keyed.ok) return keyed;
16638
16651
  const parsed = el.type === "Wall" ? parseWallSpec(specInput(el)) : parseSlabSpec(specInput(el));
16639
16652
  if (!parsed.ok) return parsed;
16640
16653
  const added = el.type === "Wall" ? model.addWall(parsed.value, { stableKey: el.keyPath }) : model.addSlab(parsed.value, { stableKey: el.keyPath });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs-bim",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "BIM layer for brepjs — IFC4-aligned parametric building elements",
5
5
  "keywords": [
6
6
  "bim",