brepjs-bim 0.19.0 → 0.20.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.
@@ -27652,27 +27652,27 @@ var GEOMETRY_PROPS = /* @__PURE__ */ new Set([
27652
27652
  "psets"
27653
27653
  ]);
27654
27654
  var SPEC_ROUTES = {
27655
- Wall: {
27655
+ wall: {
27656
27656
  parse: parseWallSpec,
27657
27657
  add: (m, spec, key) => m.addWall(spec, { stableKey: key })
27658
27658
  },
27659
- Slab: {
27659
+ slab: {
27660
27660
  parse: parseSlabSpec,
27661
27661
  add: (m, spec, key) => m.addSlab(spec, { stableKey: key })
27662
27662
  },
27663
- Column: {
27663
+ column: {
27664
27664
  parse: parseColumnSpec,
27665
27665
  add: (m, spec, key) => m.addColumn(spec, { stableKey: key })
27666
27666
  },
27667
- Beam: {
27667
+ beam: {
27668
27668
  parse: parseBeamSpec,
27669
27669
  add: (m, spec, key) => m.addBeam(spec, { stableKey: key })
27670
27670
  },
27671
- Roof: {
27671
+ roof: {
27672
27672
  parse: parseRoofSpec,
27673
27673
  add: (m, spec, key) => m.addRoof(spec, { stableKey: key })
27674
27674
  },
27675
- Stair: {
27675
+ stair: {
27676
27676
  parse: parseStairSpec,
27677
27677
  add: (m, spec, key) => m.addStair(spec, { stableKey: key }),
27678
27678
  input: stairSpecInput
@@ -27707,8 +27707,28 @@ function stairSpecInput(el) {
27707
27707
  })
27708
27708
  };
27709
27709
  }
27710
- function specRoute(type) {
27711
- return Object.hasOwn(SPEC_ROUTES, type) ? SPEC_ROUTES[type] : void 0;
27710
+ /**
27711
+ * Families predating `archetype` are routed by their display name. Keeping
27712
+ * this fallback makes the declaration purely additive, at the cost of the
27713
+ * original trap surviving for undeclared families: rename one and it stops
27714
+ * routing.
27715
+ */
27716
+ var NAME_ARCHETYPES = {
27717
+ Storey: "storey",
27718
+ Wall: "wall",
27719
+ Slab: "slab",
27720
+ Column: "column",
27721
+ Beam: "beam",
27722
+ Roof: "roof",
27723
+ Stair: "stair",
27724
+ Door: "door",
27725
+ Window: "window"
27726
+ };
27727
+ function archetypeFor(el) {
27728
+ return el.archetype ?? NAME_ARCHETYPES[el.type];
27729
+ }
27730
+ function specRoute(archetype) {
27731
+ return archetype !== void 0 && Object.hasOwn(SPEC_ROUTES, archetype) ? SPEC_ROUTES[archetype] : void 0;
27712
27732
  }
27713
27733
  /** Total of the resolved geometry's OUTER literal translate chain. The
27714
27734
  * transform vocabulary is translate-only, so frame differences are exact
@@ -27816,17 +27836,18 @@ function collectSpecProps(el) {
27816
27836
  return out;
27817
27837
  }
27818
27838
  function addFill(model, fill, input, identity) {
27819
- if (fill.type === "Door") {
27839
+ const archetype = archetypeFor(fill);
27840
+ if (archetype === "door") {
27820
27841
  const parsed = parseDoorSpec(input);
27821
27842
  if (!parsed.ok) return parsed;
27822
27843
  return model.addDoor(parsed.value, identity);
27823
27844
  }
27824
- if (fill.type === "Window") {
27845
+ if (archetype === "window") {
27825
27846
  const parsed = parseWindowSpec(input);
27826
27847
  if (!parsed.ok) return parsed;
27827
27848
  return model.addWindow(parsed.value, identity);
27828
27849
  }
27829
- return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
27850
+ return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}' (archetype: ${archetype ?? "none"}) — a filler needs archetype: 'door' or 'window'`));
27830
27851
  }
27831
27852
  /** Materialize an unrouted element's IR and add it as a proxy. The body is
27832
27853
  * authoritative for a proxy (no parametric spec to diverge from), so baked
@@ -27930,12 +27951,14 @@ function familiesToBim(root, options) {
27930
27951
  if (project !== null) model.aggregate(project.localId, siteResult.value);
27931
27952
  model.aggregate(siteResult.value, buildingId);
27932
27953
  const idByKeyPath = /* @__PURE__ */ new Map();
27954
+ const proxied = [];
27933
27955
  const walk = (el, storeyId, rotated) => {
27934
27956
  const rotatedHere = rotated || hasRotateOp(el);
27935
- let proxied = false;
27957
+ let proxiedHere = false;
27936
27958
  let containerId = storeyId;
27937
- const route = specRoute(el.type);
27938
- if (el.type === "Storey") {
27959
+ const archetype = archetypeFor(el);
27960
+ const route = specRoute(archetype);
27961
+ if (archetype === "storey") {
27939
27962
  const keyed = requireKeyed(el);
27940
27963
  if (!keyed.ok) return keyed;
27941
27964
  const storeyResult = model.addStorey({
@@ -27960,26 +27983,31 @@ function familiesToBim(root, options) {
27960
27983
  const added = route.add(model, parsed.value, el.keyPath);
27961
27984
  if (!added.ok) return added;
27962
27985
  idByKeyPath.set(el.keyPath, added.value);
27963
- if (containerId === null) return (0, brepjs.err)(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
27986
+ if (containerId === null) return (0, brepjs.err)(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment; a container family needs archetype: 'storey' to be recognised under any name`));
27964
27987
  model.placeIn(added.value, containerId);
27965
- if (el.type === "Wall") {
27988
+ if (archetype === "wall") {
27966
27989
  const opened = addOpenings(model, el, added.value, containerId, idByKeyPath);
27967
27990
  if (!opened.ok) return opened;
27968
27991
  }
27969
27992
  } else if (el.type === "Opening") return (0, brepjs.err)(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
27970
27993
  else if (el.type !== "Group" && el.geometry.kind !== "Empty") {
27971
- if (options.proxyEvaluator === void 0) return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' — add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
27994
+ if (options.proxyEvaluator === void 0) return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' (archetype: ${el.archetype ?? "none"}) routing is by archetype, so declare one on the family, add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
27972
27995
  const keyed = requireKeyed(el);
27973
27996
  if (!keyed.ok) return keyed;
27974
- if (containerId === null) return (0, brepjs.err)(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
27997
+ if (containerId === null) return (0, brepjs.err)(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment; a container family needs archetype: 'storey' to be recognised under any name`));
27975
27998
  const added = addProxyElement(model, el, options.proxyEvaluator);
27976
27999
  if (!added.ok) return added;
27977
28000
  model.placeIn(added.value, containerId);
27978
28001
  idByKeyPath.set(el.keyPath, added.value);
27979
- proxied = true;
28002
+ proxied.push({
28003
+ keyPath: el.keyPath,
28004
+ type: el.type,
28005
+ archetype: el.archetype
28006
+ });
28007
+ proxiedHere = true;
27980
28008
  }
27981
28009
  for (const child of el.children) {
27982
- if ((el.type === "Wall" || proxied) && child.type === "Opening") continue;
28010
+ if ((archetype === "wall" || proxiedHere) && child.type === "Opening") continue;
27983
28011
  const r = walk(child, containerId, rotatedHere);
27984
28012
  if (!r.ok) return r;
27985
28013
  }
@@ -27992,7 +28020,8 @@ function familiesToBim(root, options) {
27992
28020
  }
27993
28021
  return (0, brepjs.ok)({
27994
28022
  model,
27995
- idByKeyPath
28023
+ idByKeyPath,
28024
+ proxied
27996
28025
  });
27997
28026
  }
27998
28027
  //#endregion
@@ -27629,27 +27629,27 @@ var GEOMETRY_PROPS = /* @__PURE__ */ new Set([
27629
27629
  "psets"
27630
27630
  ]);
27631
27631
  var SPEC_ROUTES = {
27632
- Wall: {
27632
+ wall: {
27633
27633
  parse: parseWallSpec,
27634
27634
  add: (m, spec, key) => m.addWall(spec, { stableKey: key })
27635
27635
  },
27636
- Slab: {
27636
+ slab: {
27637
27637
  parse: parseSlabSpec,
27638
27638
  add: (m, spec, key) => m.addSlab(spec, { stableKey: key })
27639
27639
  },
27640
- Column: {
27640
+ column: {
27641
27641
  parse: parseColumnSpec,
27642
27642
  add: (m, spec, key) => m.addColumn(spec, { stableKey: key })
27643
27643
  },
27644
- Beam: {
27644
+ beam: {
27645
27645
  parse: parseBeamSpec,
27646
27646
  add: (m, spec, key) => m.addBeam(spec, { stableKey: key })
27647
27647
  },
27648
- Roof: {
27648
+ roof: {
27649
27649
  parse: parseRoofSpec,
27650
27650
  add: (m, spec, key) => m.addRoof(spec, { stableKey: key })
27651
27651
  },
27652
- Stair: {
27652
+ stair: {
27653
27653
  parse: parseStairSpec,
27654
27654
  add: (m, spec, key) => m.addStair(spec, { stableKey: key }),
27655
27655
  input: stairSpecInput
@@ -27684,8 +27684,28 @@ function stairSpecInput(el) {
27684
27684
  })
27685
27685
  };
27686
27686
  }
27687
- function specRoute(type) {
27688
- return Object.hasOwn(SPEC_ROUTES, type) ? SPEC_ROUTES[type] : void 0;
27687
+ /**
27688
+ * Families predating `archetype` are routed by their display name. Keeping
27689
+ * this fallback makes the declaration purely additive, at the cost of the
27690
+ * original trap surviving for undeclared families: rename one and it stops
27691
+ * routing.
27692
+ */
27693
+ var NAME_ARCHETYPES = {
27694
+ Storey: "storey",
27695
+ Wall: "wall",
27696
+ Slab: "slab",
27697
+ Column: "column",
27698
+ Beam: "beam",
27699
+ Roof: "roof",
27700
+ Stair: "stair",
27701
+ Door: "door",
27702
+ Window: "window"
27703
+ };
27704
+ function archetypeFor(el) {
27705
+ return el.archetype ?? NAME_ARCHETYPES[el.type];
27706
+ }
27707
+ function specRoute(archetype) {
27708
+ return archetype !== void 0 && Object.hasOwn(SPEC_ROUTES, archetype) ? SPEC_ROUTES[archetype] : void 0;
27689
27709
  }
27690
27710
  /** Total of the resolved geometry's OUTER literal translate chain. The
27691
27711
  * transform vocabulary is translate-only, so frame differences are exact
@@ -27793,17 +27813,18 @@ function collectSpecProps(el) {
27793
27813
  return out;
27794
27814
  }
27795
27815
  function addFill(model, fill, input, identity) {
27796
- if (fill.type === "Door") {
27816
+ const archetype = archetypeFor(fill);
27817
+ if (archetype === "door") {
27797
27818
  const parsed = parseDoorSpec(input);
27798
27819
  if (!parsed.ok) return parsed;
27799
27820
  return model.addDoor(parsed.value, identity);
27800
27821
  }
27801
- if (fill.type === "Window") {
27822
+ if (archetype === "window") {
27802
27823
  const parsed = parseWindowSpec(input);
27803
27824
  if (!parsed.ok) return parsed;
27804
27825
  return model.addWindow(parsed.value, identity);
27805
27826
  }
27806
- return err(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
27827
+ return err(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}' (archetype: ${archetype ?? "none"}) — a filler needs archetype: 'door' or 'window'`));
27807
27828
  }
27808
27829
  /** Materialize an unrouted element's IR and add it as a proxy. The body is
27809
27830
  * authoritative for a proxy (no parametric spec to diverge from), so baked
@@ -27907,12 +27928,14 @@ function familiesToBim(root, options) {
27907
27928
  if (project !== null) model.aggregate(project.localId, siteResult.value);
27908
27929
  model.aggregate(siteResult.value, buildingId);
27909
27930
  const idByKeyPath = /* @__PURE__ */ new Map();
27931
+ const proxied = [];
27910
27932
  const walk = (el, storeyId, rotated) => {
27911
27933
  const rotatedHere = rotated || hasRotateOp(el);
27912
- let proxied = false;
27934
+ let proxiedHere = false;
27913
27935
  let containerId = storeyId;
27914
- const route = specRoute(el.type);
27915
- if (el.type === "Storey") {
27936
+ const archetype = archetypeFor(el);
27937
+ const route = specRoute(archetype);
27938
+ if (archetype === "storey") {
27916
27939
  const keyed = requireKeyed(el);
27917
27940
  if (!keyed.ok) return keyed;
27918
27941
  const storeyResult = model.addStorey({
@@ -27937,26 +27960,31 @@ function familiesToBim(root, options) {
27937
27960
  const added = route.add(model, parsed.value, el.keyPath);
27938
27961
  if (!added.ok) return added;
27939
27962
  idByKeyPath.set(el.keyPath, added.value);
27940
- if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
27963
+ if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment; a container family needs archetype: 'storey' to be recognised under any name`));
27941
27964
  model.placeIn(added.value, containerId);
27942
- if (el.type === "Wall") {
27965
+ if (archetype === "wall") {
27943
27966
  const opened = addOpenings(model, el, added.value, containerId, idByKeyPath);
27944
27967
  if (!opened.ok) return opened;
27945
27968
  }
27946
27969
  } else if (el.type === "Opening") return err(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
27947
27970
  else if (el.type !== "Group" && el.geometry.kind !== "Empty") {
27948
- if (options.proxyEvaluator === void 0) return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' — add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
27971
+ if (options.proxyEvaluator === void 0) return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' (archetype: ${el.archetype ?? "none"}) routing is by archetype, so declare one on the family, add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
27949
27972
  const keyed = requireKeyed(el);
27950
27973
  if (!keyed.ok) return keyed;
27951
- if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
27974
+ if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment; a container family needs archetype: 'storey' to be recognised under any name`));
27952
27975
  const added = addProxyElement(model, el, options.proxyEvaluator);
27953
27976
  if (!added.ok) return added;
27954
27977
  model.placeIn(added.value, containerId);
27955
27978
  idByKeyPath.set(el.keyPath, added.value);
27956
- proxied = true;
27979
+ proxied.push({
27980
+ keyPath: el.keyPath,
27981
+ type: el.type,
27982
+ archetype: el.archetype
27983
+ });
27984
+ proxiedHere = true;
27957
27985
  }
27958
27986
  for (const child of el.children) {
27959
- if ((el.type === "Wall" || proxied) && child.type === "Opening") continue;
27987
+ if ((archetype === "wall" || proxiedHere) && child.type === "Opening") continue;
27960
27988
  const r = walk(child, containerId, rotatedHere);
27961
27989
  if (!r.ok) return r;
27962
27990
  }
@@ -27969,7 +27997,8 @@ function familiesToBim(root, options) {
27969
27997
  }
27970
27998
  return ok({
27971
27999
  model,
27972
- idByKeyPath
28000
+ idByKeyPath,
28001
+ proxied
27973
28002
  });
27974
28003
  }
27975
28004
  //#endregion
@@ -18,10 +18,24 @@ export interface FamiliesToBimOptions {
18
18
  */
19
19
  readonly proxyEvaluator?: csg.Evaluator | undefined;
20
20
  }
21
+ export interface ProxiedElement {
22
+ readonly keyPath: string;
23
+ /** The family's display name, as resolved. */
24
+ readonly type: string;
25
+ readonly archetype: string | undefined;
26
+ }
21
27
  export interface FamiliesBimResult {
22
28
  readonly model: BimModel;
23
29
  /** LocalId per geometry-bearing families key path. */
24
30
  readonly idByKeyPath: ReadonlyMap<string, LocalId>;
31
+ /**
32
+ * Elements exported as IfcBuildingElementProxy because no spec route
33
+ * matched, in walk order. Only ever non-empty when `proxyEvaluator` is set:
34
+ * without it an unrouted element is a hard error instead. A renamed family
35
+ * that has lost its routing lands here rather than in the file as the type
36
+ * you meant, so check this before trusting an export.
37
+ */
38
+ readonly proxied: readonly ProxiedElement[];
25
39
  }
26
40
  /**
27
41
  * Project a resolved families tree into an eager BimModel. The caller owns
package/dist/index.d.ts CHANGED
@@ -97,4 +97,4 @@ export type { IdsDocument, IdsSpecification, IdsFacet, IdsRestriction, IdsCardin
97
97
  export { serializeBcfFiles, parseBcfFiles } from './bcf/index.js';
98
98
  export type { BcfColoring, BcfComment, BcfComponent, BcfComponents, BcfContainerData, BcfFiles, BcfProject, BcfTopic, BcfVersion, BcfViewpoint, BcfVisibility, } from './bcf/index.js';
99
99
  export { bcfError, idsError } from './errors/bimError.js';
100
- export { familiesToBim, type FamiliesToBimOptions, type FamiliesBimResult, } from './familiesAdapter.js';
100
+ export { familiesToBim, type FamiliesToBimOptions, type FamiliesBimResult, type ProxiedElement, } from './familiesAdapter.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs-bim",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "BIM layer for brepjs — IFC4-aligned parametric building elements",
5
5
  "keywords": [
6
6
  "bim",