@willcgage/module-schematic 0.66.0 → 0.68.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.
package/dist/index.cjs CHANGED
@@ -534,7 +534,7 @@ function endplateWidthFor(widths, id) {
534
534
  return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
535
535
  }
536
536
  function isLoopDoc(doc) {
537
- return doc.loop === true || doc.endplates.length === 1;
537
+ return doc.loop === true;
538
538
  }
539
539
  var MAIN_TRACK_ID = "main";
540
540
  var MAIN2_TRACK_ID = "main2";
@@ -703,18 +703,20 @@ function stateToDoc(state, recordNumber) {
703
703
  ]
704
704
  ) : [
705
705
  { id: "A", label: "West", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
706
- // Non-loop modules always have a real B ("none" never applies).
707
- {
708
- id: "B",
709
- label: "East",
710
- tracks: [
711
- {
712
- trackId: MAIN_TRACK_ID,
713
- lane: 0,
714
- config: state.configB === "none" ? "single" : state.configB
715
- }
716
- ]
717
- }
706
+ // ⚠️ `configB: "none"` means the module HAS NO FAR ENDPLATE, and
707
+ // that is not loop-only (#184). An *end of the line* or a *pocket*
708
+ // presents one conforming face and the track simply stops. The
709
+ // standard governs the ends a module offers for joining; it never
710
+ // required a module to offer two. This used to coerce "none" to
711
+ // "single" and emit a B regardless, so a single-ended module was
712
+ // impossible to author.
713
+ ...state.configB !== "none" ? [
714
+ {
715
+ id: "B",
716
+ label: "East",
717
+ tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }]
718
+ }
719
+ ] : []
718
720
  ],
719
721
  // Branch endplates C, D, … — junction connections at pos, off one side
720
722
  // (#170). A set can carry several (e.g. a second railroad through).
@@ -929,8 +931,14 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
929
931
  loopReturn: loop && d.loopReturn === "main2" ? "main2" : "same",
930
932
  mainsSwapped: d.mainsSwapped === true,
931
933
  configA: configOf("A"),
932
- // On a loop, a missing B means pure turnback; present = interchange loop.
933
- configB: loop && !hasB ? "none" : configOf("B"),
934
+ // NO ENDPLATE B ⇒ "none", whatever kind of module this is. On a loop that
935
+ // reads as a pure turnback (a present B makes it an interchange); on an end
936
+ // of the line or a pocket it simply means the module has one face (#184).
937
+ // ⚠️ This used to be `loop && !hasB`, so "none" only survived a round trip on
938
+ // a loop — which passed its test yesterday ONLY because a single-endplate
939
+ // module was still being misclassified as one (#191). The absence of the
940
+ // plate is the fact; why it's absent doesn't change what to read.
941
+ configB: hasB ? configOf("B") : "none",
934
942
  branches: branchEps.map((ep) => ({
935
943
  label: ep.label ?? "Branch",
936
944
  pos: sc(ep.at.pos),
@@ -1984,7 +1992,7 @@ function deriveEndplatePoses(geo) {
1984
1992
  trackOffsets: offsetsFor(cfg(0), half)
1985
1993
  })
1986
1994
  );
1987
- const noB = geo.geometryType === "dead_end" || geo.loop === true;
1995
+ const noB = geo.geometryType === "dead_end" || geo.loop === true || geo.endplateConfigs?.[1] === "none";
1988
1996
  const end = sectionedEndPose({ sections: geo.sections });
1989
1997
  if (!noB && end) {
1990
1998
  poses.push(