@bldrs-ai/conway 1.499.1419 → 1.500.1420

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.
@@ -30,7 +30,7 @@ var import_node_process = require("node:process");
30
30
  var readline = __toESM(require("node:readline"), 1);
31
31
 
32
32
  // compiled/src/version/version.js
33
- var versionString = "Conway v1.499.1419";
33
+ var versionString = "Conway v1.500.1420";
34
34
 
35
35
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
36
36
  var wasmType = "";
@@ -2940,9 +2940,6 @@ var StepParser = class extends StepHeaderParser {
2940
2940
  input2.begin();
2941
2941
  const elementResult = parseInlineElement();
2942
2942
  if (elementResult !== void 0) {
2943
- for (const element of elementResult[0].elements) {
2944
- element.expressID = expressID;
2945
- }
2946
2943
  input2.rollback();
2947
2944
  if (tokenws(INF)) {
2948
2945
  break;
@@ -14965,7 +14965,7 @@ ${t5.join("\n")}` : "";
14965
14965
  var import_process = require("process");
14966
14966
 
14967
14967
  // compiled/src/version/version.js
14968
- var versionString = "Conway v1.499.1419";
14968
+ var versionString = "Conway v1.500.1420";
14969
14969
 
14970
14970
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
14971
14971
  function pThreadsAllowed() {
@@ -18754,9 +18754,6 @@ var StepParser = class extends StepHeaderParser {
18754
18754
  input.begin();
18755
18755
  const elementResult = parseInlineElement();
18756
18756
  if (elementResult !== void 0) {
18757
- for (const element of elementResult[0].elements) {
18758
- element.expressID = expressID;
18759
- }
18760
18757
  input.rollback();
18761
18758
  if (tokenws(INF)) {
18762
18759
  break;
@@ -15943,7 +15943,7 @@ var ParsingBuffer = class {
15943
15943
  };
15944
15944
 
15945
15945
  // compiled/src/version/version.js
15946
- var versionString = "Conway v1.499.1419";
15946
+ var versionString = "Conway v1.500.1420";
15947
15947
 
15948
15948
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
15949
15949
  function pThreadsAllowed() {
@@ -18662,9 +18662,6 @@ var StepParser = class extends StepHeaderParser {
18662
18662
  input.begin();
18663
18663
  const elementResult = parseInlineElement();
18664
18664
  if (elementResult !== void 0) {
18665
- for (const element of elementResult[0].elements) {
18666
- element.expressID = expressID;
18667
- }
18668
18665
  input.rollback();
18669
18666
  if (tokenws(INF)) {
18670
18667
  break;
@@ -944,7 +944,7 @@ var EntityTypesIfcCount = 909;
944
944
  var entity_types_ifc_gen_default = EntityTypesIfc;
945
945
 
946
946
  // compiled/src/version/version.js
947
- var versionString = "Conway v1.499.1419";
947
+ var versionString = "Conway v1.500.1420";
948
948
 
949
949
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
950
950
  var wasmType = "";
@@ -3854,9 +3854,6 @@ var StepParser = class extends StepHeaderParser {
3854
3854
  input.begin();
3855
3855
  const elementResult = parseInlineElement();
3856
3856
  if (elementResult !== void 0) {
3857
- for (const element of elementResult[0].elements) {
3858
- element.expressID = expressID;
3859
- }
3860
3857
  input.rollback();
3861
3858
  if (tokenws(INF)) {
3862
3859
  break;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inf_recovery_index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inf_recovery_index.test.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/inf_recovery_index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ // `INF` where a real belongs is not valid ISO 10303-21, but exporters emit it
2
+ // for unbounded quantities and the parser recovers from it. The recovery used
3
+ // to stamp the offending record's express ID over every top-level entry
4
+ // indexed so far in the block, which corrupted every lookup that followed.
5
+ //
6
+ // It stayed hidden only because no public corpus model reaches this recovery.
7
+ // Not because a failed parse is harmless: the stamping ran before the rollback
8
+ // and the INF check, so any top-level inline-instance failure poisoned the
9
+ // index, and parseDataToModel builds a model from those elements whatever the
10
+ // ParseResult. A private model reaches it - `IFCQUANTITYLENGTH(...,INF,$)`
11
+ // from a Swiss architectural export - where it cost 3,116 entities and
12
+ // produced 630 relationship failures all reporting the same express ID.
13
+ // See bldrs-ai/conway#412, #481, #482.
14
+ //
15
+ // Parser-only, so no geometry wasm is initialized here.
16
+ import { describe, expect, test } from "@jest/globals";
17
+ import IfcStepParser from "../../ifc/ifc_step_parser.js";
18
+ import ParsingBuffer from "../../parsing/parsing_buffer.js";
19
+ import { ParseResult } from "./step_parser.js";
20
+ const parser = IfcStepParser.Instance;
21
+ // The middle record carries the bare INF. The records around it are ordinary,
22
+ // and they are the ones the stamping loop used to overwrite - the bug is not
23
+ // in how the INF record itself is read, it is in what reading it does to
24
+ // everything already indexed.
25
+ // The express IDs under test, kept in one place so the assertions read as
26
+ // "the IDs written in the source", which is exactly what the bug destroyed.
27
+ const BEFORE_ID = 11;
28
+ const INF_ID = 22;
29
+ const AFTER_ID = 33;
30
+ const EXPECTED_IDS = [BEFORE_ID, INF_ID, AFTER_ID];
31
+ // One template, one substitution point. Derived with String.replace on a
32
+ // whitespace-exact literal, the controls below would silently become copies of
33
+ // the INF case the moment anyone reflowed the source - replace() returns its
34
+ // input unchanged when the token is absent - and "indexes the same as the
35
+ // equivalent record with a real" would degrade to expect(x).toEqual(x), which
36
+ // passes even with the bug reinstated.
37
+ const VALUE_SLOT = "<<VALUE>>";
38
+ const TEMPLATE = [
39
+ `#${BEFORE_ID}= IFCQUANTITYLENGTH('before',$,$,1.5,$);`,
40
+ `#${INF_ID}= IFCQUANTITYLENGTH('unbounded',$,$,${VALUE_SLOT},$);`,
41
+ `#${AFTER_ID}= IFCQUANTITYLENGTH('after',$,$,2.5,$);`,
42
+ ].join("\n");
43
+ /**
44
+ * Build the fixture with a given value in the middle record's 4th attribute.
45
+ *
46
+ * @param value The value text to substitute.
47
+ * @return {string} The fixture source.
48
+ */
49
+ function withValue(value) {
50
+ if (!TEMPLATE.includes(VALUE_SLOT)) {
51
+ throw new Error("fixture template lost its substitution slot");
52
+ }
53
+ return TEMPLATE.replace(VALUE_SLOT, value);
54
+ }
55
+ // Leading whitespace preserved from the real export, since it is what puts an
56
+ // identifier-like token where an attribute is expected.
57
+ const WITH_INF = withValue(" INF");
58
+ // The control: whatever the index looks like with an ordinary real is what the
59
+ // INF version has to match.
60
+ const WITHOUT_INF = withValue("3.5");
61
+ // The same export writes `-INF` for the opposite bound. Also a CONTROL, not a
62
+ // second case of the bug: a leading '-' is dispatched as a number, so it never
63
+ // reaches the inline-instance recovery and was never affected. Kept so that
64
+ // stays visible - the defect is specific to the bare identifier-like form, and
65
+ // anyone reading INF handling should not assume both directions went through
66
+ // it.
67
+ const WITH_NEGATIVE_INF = withValue(" -INF");
68
+ /**
69
+ * Parse a data block and return each top-level entry's express ID, in order.
70
+ *
71
+ * @param source The STEP text to parse.
72
+ * @return {number[]} Express IDs as indexed.
73
+ */
74
+ function indexedExpressIDs(source) {
75
+ const input = new ParsingBuffer(new TextEncoder().encode(source));
76
+ const [index, result] = parser.parseDataBlock(input);
77
+ // INCOMPLETE is the expected result for a bare run of records with no
78
+ // ENDSEC; terminator - what matters here is that it is not a syntax error.
79
+ expect([ParseResult.COMPLETE, ParseResult.INCOMPLETE]).toContain(result);
80
+ return index.elements.map((element) => element.expressID);
81
+ }
82
+ describe("a bare INF attribute", () => {
83
+ test("does not stamp its express ID over the records before it", () => {
84
+ expect(indexedExpressIDs(WITH_INF)).toEqual(EXPECTED_IDS);
85
+ });
86
+ test("indexes the same as the equivalent record with a real", () => {
87
+ expect(indexedExpressIDs(WITH_INF))
88
+ .toEqual(indexedExpressIDs(WITHOUT_INF));
89
+ });
90
+ // Passes with or without the fix, by design - see WITH_NEGATIVE_INF above.
91
+ test("negative INF is dispatched as a number and never took this path", () => {
92
+ expect(indexedExpressIDs(WITH_NEGATIVE_INF)).toEqual(EXPECTED_IDS);
93
+ });
94
+ // The whole file has to remain resolvable, not just the index array: a
95
+ // corrupted express ID column makes getElementByExpressID hand back the
96
+ // wrong record, which is how this surfaced as relationship failures rather
97
+ // than as a parse error.
98
+ test("every record is still reachable by its own express ID", () => {
99
+ const [result, model] = parser.parseDataToModel(new ParsingBuffer(new TextEncoder().encode(WITH_INF)));
100
+ expect([ParseResult.COMPLETE, ParseResult.INCOMPLETE]).toContain(result);
101
+ expect(model).not.toBeUndefined();
102
+ for (const expressID of EXPECTED_IDS) {
103
+ const element = model?.getElementByExpressID(expressID);
104
+ expect(element).not.toBeUndefined();
105
+ expect(element?.expressID).toBe(expressID);
106
+ }
107
+ });
108
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"step_parser.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/step_parser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"step_parser.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/step_parser.ts"],"names":[],"mappings":"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"}
@@ -661,9 +661,38 @@ export default class StepParser extends StepHeaderParser {
661
661
  input.begin();
662
662
  const elementResult = parseInlineElement();
663
663
  if (elementResult !== (void 0)) {
664
- for (const element of elementResult[0].elements) {
665
- element.expressID = expressID;
666
- }
664
+ // No stamping here. This used to run
665
+ //
666
+ // for ( const element of elementResult[0].elements ) {
667
+ // element.expressID = expressID
668
+ // }
669
+ //
670
+ // and elementResult[0] is `indexResult` - syntaxError() returns
671
+ // it wholesale - so that wrote THIS record's express ID over
672
+ // every top-level entry indexed so far in the block.
673
+ //
674
+ // It cannot have been doing anything useful. Both pushEntry
675
+ // sites already set `expressID` at construction, so every entry
676
+ // arrives correct and the loop could only overwrite correct
677
+ // values with one wrong one. The same recovery inside the nested
678
+ // parseInlineElement - begin, parse, rollback, tokenws(INF) - has
679
+ // never had the loop and works.
680
+ //
681
+ // Note it ran BEFORE the rollback and the INF check, so it was
682
+ // never specific to INF: any top-level inline-instance parse
683
+ // failure poisoned the index the same way. And the index is not
684
+ // discarded on a syntax error - parseDataToModel builds the
685
+ // model from itemIndex.elements whatever the ParseResult, and
686
+ // the loaders only log and continue - so a poisoned index does
687
+ // reach consumers.
688
+ //
689
+ // What kept it hidden is narrower than that: no public corpus
690
+ // model reaches this recovery at all. A private one does, via an
691
+ // exporter writing `IFCQUANTITYLENGTH(...,INF,$)` for an
692
+ // unbounded quantity - not valid ISO 10303-21, but what the tool
693
+ // emits - where it cost 3,116 entities and produced 630
694
+ // relationship failures all naming the same record.
695
+ // See #412, #481, #482.
667
696
  input.rollback();
668
697
  if (tokenws(INF)) {
669
698
  break;
@@ -5,5 +5,5 @@
5
5
  // only the first segment (major) is meaningful and is the one CI carries forward.
6
6
  // Must stay in `vN.N.N` shape: the CI stamp regex, scripts/updateVersion.mjs, and
7
7
  // statistics.ts all match `v\d+\.\d+\.\d+`.
8
- const versionString = 'Conway v1.499.1419';
8
+ const versionString = 'Conway v1.500.1420';
9
9
  export { versionString };