@bldrs-ai/conway 1.567.1542 → 1.569.1545

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.
Files changed (37) hide show
  1. package/compiled/examples/browser-bundled.cjs +27 -2
  2. package/compiled/examples/cli-bundled.cjs +199 -33
  3. package/compiled/examples/cli-step-bundled.cjs +28 -2
  4. package/compiled/examples/validator-bundled.cjs +27 -2
  5. package/compiled/src/AP214E3_2010/ap214_command_line_main.js +1 -0
  6. package/compiled/src/AP214E3_2010/ap214_regression_main.js +35 -7
  7. package/compiled/src/compat/web-ifc/geometry_progress_accounting.test.d.ts +2 -0
  8. package/compiled/src/compat/web-ifc/geometry_progress_accounting.test.d.ts.map +1 -0
  9. package/compiled/src/compat/web-ifc/geometry_progress_accounting.test.js +503 -0
  10. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts.map +1 -1
  11. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.js +1 -0
  12. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +53 -0
  13. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
  14. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +103 -11
  15. package/compiled/src/ifc/ifc_command_line_main.js +1 -0
  16. package/compiled/src/ifc/ifc_geometry_extraction.d.ts +134 -11
  17. package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
  18. package/compiled/src/ifc/ifc_geometry_extraction.js +214 -31
  19. package/compiled/src/ifc/ifc_regression_main.js +77 -7
  20. package/compiled/src/ifc/ifc_rel_aggregate_extraction.test.js +96 -2
  21. package/compiled/src/loaders/conway_model_loader.d.ts.map +1 -1
  22. package/compiled/src/loaders/conway_model_loader.js +8 -0
  23. package/compiled/src/scripts/bless_perf_snapshot.test.js +44 -1
  24. package/compiled/src/scripts/check_compiled_fresh.test.d.ts +2 -0
  25. package/compiled/src/scripts/check_compiled_fresh.test.d.ts.map +1 -0
  26. package/compiled/src/scripts/check_compiled_fresh.test.js +136 -0
  27. package/compiled/src/scripts/perf_ab_compare.test.js +22 -7
  28. package/compiled/src/scripts/perf_csv_quoting.test.js +46 -8
  29. package/compiled/src/scripts/perf_writer_and_wallclock.test.d.ts +2 -0
  30. package/compiled/src/scripts/perf_writer_and_wallclock.test.d.ts.map +1 -0
  31. package/compiled/src/scripts/perf_writer_and_wallclock.test.js +665 -0
  32. package/compiled/src/statistics/statistics.d.ts +22 -0
  33. package/compiled/src/statistics/statistics.d.ts.map +1 -1
  34. package/compiled/src/statistics/statistics.js +26 -0
  35. package/compiled/src/version/version.js +1 -1
  36. package/compiled/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +3 -2
@@ -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.567.1542";
33
+ var versionString = "Conway v1.569.1545";
34
34
 
35
35
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
36
36
  var wasmType = "";
@@ -121,6 +121,31 @@ var Statistics = class {
121
121
  setGeometryTime(value) {
122
122
  this.geometryTime = value;
123
123
  }
124
+ /**
125
+ * Which pipeline measured this row (conway#555).
126
+ *
127
+ * The perf columns are not all comparable across pipelines: the IFC
128
+ * regression child runs with `RegressionCaptureState.memoization = FULL`,
129
+ * which keeps CSG temporaries in the map `calculateGeometrySize()` sums,
130
+ * so its `Geometry Memory` reads ~30% above the CLI's for the same model
131
+ * (MB-Khaya, 22.3 vs 16.8 MB). Retention has a matching caveat — the
132
+ * loader brings up a `ConwayGeometry` per load and the regression children
133
+ * do not. Naming the writer is what lets a reader, and
134
+ * `scripts/gen_delta_csv.cjs`, tell those apart instead of differencing
135
+ * across them.
136
+ *
137
+ * @return {string | undefined} - the writer label, or undefined
138
+ */
139
+ getWriter() {
140
+ return this.writer;
141
+ }
142
+ /**
143
+ *
144
+ * @param value - a stable pipeline identifier, e.g. `ifc-cli`
145
+ */
146
+ setWriter(value) {
147
+ this.writer = value;
148
+ }
124
149
  /**
125
150
  *
126
151
  * @return {number | undefined} - total execution time or undefined
@@ -361,7 +386,7 @@ var Statistics = class {
361
386
  const retained = `Retained RSS Delta: ${formatRetained(this.retainedRss)}, Retained Heap-Used Delta: ${formatRetained(this.retainedHeapUsed)}, Retained External Delta: ${formatRetained(this.retainedExternal)}, `;
362
387
  const breakdown = this.formatGeometryTypeCounts();
363
388
  const geometryTypes = breakdown !== void 0 ? `, Geometry Types: ${breakdown}` : "";
364
- return `[${dateString}]: Load Status: ${this.loadStatus}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
389
+ return `[${dateString}]: Load Status: ${this.loadStatus}, Writer: ${this.writer ?? "N/A"}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
365
390
  }
366
391
  };
367
392
 
@@ -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.567.1542";
14968
+ var versionString = "Conway v1.569.1545";
14969
14969
 
14970
14970
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
14971
14971
  function pThreadsAllowed() {
@@ -15795,6 +15795,31 @@ var Statistics = class {
15795
15795
  setGeometryTime(value) {
15796
15796
  this.geometryTime = value;
15797
15797
  }
15798
+ /**
15799
+ * Which pipeline measured this row (conway#555).
15800
+ *
15801
+ * The perf columns are not all comparable across pipelines: the IFC
15802
+ * regression child runs with `RegressionCaptureState.memoization = FULL`,
15803
+ * which keeps CSG temporaries in the map `calculateGeometrySize()` sums,
15804
+ * so its `Geometry Memory` reads ~30% above the CLI's for the same model
15805
+ * (MB-Khaya, 22.3 vs 16.8 MB). Retention has a matching caveat — the
15806
+ * loader brings up a `ConwayGeometry` per load and the regression children
15807
+ * do not. Naming the writer is what lets a reader, and
15808
+ * `scripts/gen_delta_csv.cjs`, tell those apart instead of differencing
15809
+ * across them.
15810
+ *
15811
+ * @return {string | undefined} - the writer label, or undefined
15812
+ */
15813
+ getWriter() {
15814
+ return this.writer;
15815
+ }
15816
+ /**
15817
+ *
15818
+ * @param value - a stable pipeline identifier, e.g. `ifc-cli`
15819
+ */
15820
+ setWriter(value) {
15821
+ this.writer = value;
15822
+ }
15798
15823
  /**
15799
15824
  *
15800
15825
  * @return {number | undefined} - total execution time or undefined
@@ -16035,7 +16060,7 @@ var Statistics = class {
16035
16060
  const retained = `Retained RSS Delta: ${formatRetained(this.retainedRss)}, Retained Heap-Used Delta: ${formatRetained(this.retainedHeapUsed)}, Retained External Delta: ${formatRetained(this.retainedExternal)}, `;
16036
16061
  const breakdown = this.formatGeometryTypeCounts();
16037
16062
  const geometryTypes = breakdown !== void 0 ? `, Geometry Types: ${breakdown}` : "";
16038
- return `[${dateString}]: Load Status: ${this.loadStatus}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
16063
+ return `[${dateString}]: Load Status: ${this.loadStatus}, Writer: ${this.writer ?? "N/A"}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
16039
16064
  }
16040
16065
  };
16041
16066
 
@@ -93591,13 +93616,56 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93591
93616
  return this.aggregateTargetLocalIDs_;
93592
93617
  }
93593
93618
  const targets = /* @__PURE__ */ new Set();
93594
- const productTypes = new Set(IfcProduct.query);
93595
93619
  for (const relAggregate of this.model.types(IfcRelAggregates)) {
93596
- this.addAggregateTargets_(relAggregate, targets, productTypes);
93620
+ this.addAggregateTargets_(relAggregate, targets);
93597
93621
  }
93598
93622
  this.aggregateTargetLocalIDs_ = targets;
93599
93623
  return targets;
93600
93624
  }
93625
+ /**
93626
+ * The per-relationship product counts the PAGED walk collected, handed
93627
+ * over and dropped in one call.
93628
+ *
93629
+ * Counts are how many products the aggregates pass will extract for a
93630
+ * relationship — the size of the unit of work
93631
+ * {@link extractRelAggregateGeometryIncremental} steps through — which the
93632
+ * demand pump needs to report progress in products rather than in
93633
+ * relationships (conway#565: SKYLARK250 puts ~1,960 products under one
93634
+ * relationship, so a relationship-counted bar sat at 1/2 for the whole
93635
+ * load).
93636
+ *
93637
+ * **Collected only on the windowed walk, and only because that is the one
93638
+ * place it is free.** {@link ensureAggregateTargetLocalIDs} already pages
93639
+ * and scans every `IfcRelAggregates` record, so the count is the number it
93640
+ * was discarding. A resident model has no such moment worth capturing —
93641
+ * it can read the same records at adopt time for nothing — so the sync
93642
+ * walk collects nothing at all and the whole-model extraction, which
93643
+ * shares that walk, retains nothing.
93644
+ *
93645
+ * **Handed over rather than cached** (conway#569 review). The prefix the
93646
+ * pump builds from these is itself the durable structure; keeping a second
93647
+ * copy for the model's life is retention with no reader. Measured on an
93648
+ * adverse model — 50,000 relationships over 100,000 products, the shape
93649
+ * where relationship count approaches product count — the map holds
93650
+ * 1.75 MB at 36.7 bytes per relationship. Not fatal beside the 16.4 MB
93651
+ * the target Set from the same walk retains, but it is avoidable, and
93652
+ * `setGeometryShard` refuses to run once a worklist exists so the prefix
93653
+ * is built exactly once and there is nothing to hand it to twice.
93654
+ *
93655
+ * **The same number the resident pump derives for itself**, from the same
93656
+ * call: {@link aggregateRelatedProductCount}. Approximate in one
93657
+ * direction only — a `RelatedObjects` list that needs the generated
93658
+ * getter counts 0 — and the pump's own drain condition, not this number,
93659
+ * decides when the pass is finished.
93660
+ *
93661
+ * @return {Map<number, number> | undefined} Counts keyed by relationship
93662
+ * localID, or undefined where no paged walk ran. The caller owns it.
93663
+ */
93664
+ takeAggregateRelatedProductCounts() {
93665
+ const counts = this.pagedAggregateProductCounts_;
93666
+ this.pagedAggregateProductCounts_ = void 0;
93667
+ return counts;
93668
+ }
93601
93669
  /**
93602
93670
  * Windowed twin of {@link aggregateTargetLocalIDs}: page each
93603
93671
  * relationship record before scanning it, so the set is COMPLETE rather
@@ -93634,7 +93702,7 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93634
93702
  return this.aggregateTargetLocalIDs_;
93635
93703
  }
93636
93704
  const targets = /* @__PURE__ */ new Set();
93637
- const productTypes = new Set(IfcProduct.query);
93705
+ const counts = /* @__PURE__ */ new Map();
93638
93706
  const wave = Math.max(1, Math.floor(waveSize));
93639
93707
  let pending = [];
93640
93708
  const drain = async () => {
@@ -93646,7 +93714,8 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93646
93714
  }
93647
93715
  await Promise.all([...pinned].map((localID) => this.model.ensureResidentByLocalID(localID)));
93648
93716
  for (const relAggregate of pending) {
93649
- this.addAggregateTargets_(relAggregate, targets, productTypes, true);
93717
+ this.addAggregateTargets_(relAggregate, targets, true);
93718
+ counts.set(relAggregate.localID, this.aggregateRelatedProductCount(relAggregate));
93650
93719
  }
93651
93720
  } finally {
93652
93721
  this.model.releaseSourceViews(pinned);
@@ -93664,6 +93733,7 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93664
93733
  await drain();
93665
93734
  }
93666
93735
  this.aggregateTargetLocalIDs_ = targets;
93736
+ this.pagedAggregateProductCounts_ = counts;
93667
93737
  this.aggregateTargetsPaged_ = true;
93668
93738
  return targets;
93669
93739
  }
@@ -93672,30 +93742,62 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93672
93742
  *
93673
93743
  * Shared by the sync and paged walks so there is a single definition of
93674
93744
  * what an aggregate target is. Reads only this relationship's own record:
93675
- * children resolve through the express map and the typeID column, so the
93676
- * scan never hydrates a child product (doing so paged most of the file
93677
- * during demand prep).
93745
+ * the entries are scanned as `#N` references off the record's own bytes,
93746
+ * never through `relAggregate.RelatedObjects` — that generated getter
93747
+ * memoizes the materialised array onto the relationship, which is the
93748
+ * 2.7 GB retention of conway#549 §4.1 and, on a windowed source, pages
93749
+ * most of the file during demand prep.
93750
+ *
93751
+ * Classification goes through {@link relatedProductByExpressID_}, the
93752
+ * same call the aggregates pass makes, so deferral and extraction cannot
93753
+ * disagree about which entries are products. The typeID column cannot
93754
+ * answer this: a STEP complex instance is recorded under type 0
93755
+ * (`EXTERNALMAPPINGCONTAINER`) while `getTypedElementByExpressID` finds
93756
+ * the `IfcProduct` among its `multiMapping` variants, so a column test
93757
+ * left a complex product out of the target set and both passes then
93758
+ * extracted it — one product, two scene instances (conway#566 left this
93759
+ * one in place deliberately; it is milder than the paging divergence
93760
+ * that PR fixed, because it costs a duplicate extraction rather than a
93761
+ * non-resident read).
93762
+ *
93763
+ * Materialising to classify costs no source bytes and no residency:
93764
+ * `getTypedElementByExpressID` builds its descriptor from the columns
93765
+ * (`makeDescriptor`) and the entity constructor has an empty body, so
93766
+ * nothing here can throw `StepBufferNotResidentError` on a windowed
93767
+ * source. What it does add is one cached descriptor per related object
93768
+ * — ~117 B (conway#549 §4.3) against a set the aggregates pass
93769
+ * materialises in full anyway, so ~233 KB on SKYLARK250's 1,992
93770
+ * children. It does NOT touch a child's closure: the B-rep subtree is
93771
+ * only reachable through the generated getters, which this never calls.
93772
+ *
93773
+ * A throw from the classification abandons the rest of the
93774
+ * relationship, which is what the pass does with the same entry — its
93775
+ * permissive catch drops the remaining products too. Recording targets
93776
+ * past that point would defer products the pass will never reach, i.e.
93777
+ * extract them in neither pass.
93778
+ *
93779
+ * Deliberately NOT the place the progress denominator is counted, even
93780
+ * though the entries pass through here for free and, since conway#568,
93781
+ * through the same classification: `targets` is a Set over the WHOLE
93782
+ * model, so a product reached through two relationships collapses into
93783
+ * one entry here while costing the pass a step in each. See
93784
+ * {@link aggregateRelatedProductCount}.
93678
93785
  *
93679
93786
  * @param relAggregate The relationship to scan.
93680
93787
  * @param targets Accumulator.
93681
- * @param productTypes The IfcProduct type set.
93682
93788
  * @param strictResidency When set, a non-resident read propagates instead
93683
93789
  * of being tolerated — for the paged walk, which has already made the
93684
93790
  * record resident and so can only reach that error through a defect.
93685
93791
  */
93686
- addAggregateTargets_(relAggregate, targets, productTypes, strictResidency = false) {
93792
+ addAggregateTargets_(relAggregate, targets, strictResidency = false) {
93687
93793
  try {
93688
93794
  relAggregate.forEachReferenceInField(REL_AGGREGATES_RELATED_OFFSET, REL_AGGREGATES_RELATED_BASE, REL_AGGREGATES_RELATED_DEPTH, (expressID) => {
93689
93795
  if (expressID === void 0) {
93690
93796
  return true;
93691
93797
  }
93692
- const relatedLocalID = this.model.resolveExpressID(expressID);
93693
- if (relatedLocalID === void 0) {
93694
- return true;
93695
- }
93696
- const typeID = this.model.typeIDOf(relatedLocalID);
93697
- if (typeID !== void 0 && productTypes.has(typeID)) {
93698
- targets.add(relatedLocalID);
93798
+ const relatedProduct = this.relatedProductByExpressID_(expressID);
93799
+ if (relatedProduct !== void 0) {
93800
+ targets.add(relatedProduct.localID);
93699
93801
  }
93700
93802
  return true;
93701
93803
  });
@@ -94138,10 +94240,10 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
94138
94240
  * `IfcProduct` among its `multiMapping` variants, and the product would
94139
94241
  * then be extracted without ever being paged (conway#566 review).
94140
94242
  *
94141
- * Note {@link aggregateTargetLocalIDs} still reads the column, and has the
94142
- * same blind spot — a complex product is not deferred, so it is extracted
94143
- * by both passes. That is pre-existing and independent of paging: it costs
94144
- * a duplicate extraction, not a non-resident read.
94243
+ * {@link aggregateTargetLocalIDs} classifies through the same call for
94244
+ * the same reason — a column test there left a complex product out of
94245
+ * the deferral set, so both passes extracted it. See
94246
+ * {@link addAggregateTargets_}.
94145
94247
  *
94146
94248
  * Seam for {@link AggregateExtractPager}, which lives in this module.
94147
94249
  *
@@ -94169,6 +94271,53 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
94169
94271
  }
94170
94272
  return productLocalIDs;
94171
94273
  }
94274
+ /**
94275
+ * How many products the aggregates pass will actually extract from one
94276
+ * relationship — the size of the unit of work
94277
+ * {@link extractRelAggregateGeometryIncremental} steps through, and the
94278
+ * per-relationship term of the demand pumps' progress denominator
94279
+ * (conway#565).
94280
+ *
94281
+ * **Derived through the pass's own path** —
94282
+ * {@link relatedAggregateProductLocalIDs} and so
94283
+ * {@link relatedProductByExpressID_} — rather than counted off the
94284
+ * target scan, which has the same entries in hand for free.
94285
+ *
94286
+ * The boundary is why. An entry the classification throws on — a
94287
+ * reference that resolves to nothing, or to something that is not an
94288
+ * `IfcObjectDefinition` — ENDS the relationship: the pass's permissive
94289
+ * catch abandons every product after it. A count that skipped the bad
94290
+ * entry and kept going put work in the denominator that would never run,
94291
+ * and the bar then jumped the whole abandoned tail when the relationship
94292
+ * terminated (conway#569 review). On SKYLARK's shape — one relationship
94293
+ * over ~1,960 products — that jump is the entire bar.
94294
+ * {@link relatedAggregateProductLocalIDs} stops where the pass stops;
94295
+ * {@link addAggregateTargets_}, whose Set collapses a product shared by
94296
+ * two relationships, could not carry the number anyway.
94297
+ *
94298
+ * It is still only a PREDICTION of where the pass stops — see the
94299
+ * `TODO(conway#574)` in `IfcApiProxyIfc.demandProgress_`.
94300
+ *
94301
+ * Costs no source bytes beyond the relationship's own record:
94302
+ * `getTypedElementByExpressID` builds its descriptor from the columns
94303
+ * (conway#566 swept exactly this call over SKYLARK250's 7,818,778
94304
+ * records behind an exhausted window — 0 throws, 0 store bytes). That is
94305
+ * what lets the paged walk call it inside a wave, where only the
94306
+ * relationship record is resident, as well as the resident pump calling
94307
+ * it at worklist-adopt time.
94308
+ *
94309
+ * **0 for a `RelatedObjects` list only the generated getter can
94310
+ * resolve** — an inline entity, `$` or junk. Counting those exactly means
94311
+ * materialising the array both callers exist to avoid, and an under-count
94312
+ * only pauses the bar short of full: `remaining` is derived from the
94313
+ * cursors, never from this.
94314
+ *
94315
+ * @param relAggregate The relationship to cost.
94316
+ * @return {number} Related products the aggregates pass will extract.
94317
+ */
94318
+ aggregateRelatedProductCount(relAggregate) {
94319
+ return this.relatedAggregateProductLocalIDs(relAggregate)?.length ?? 0;
94320
+ }
94172
94321
  /**
94173
94322
  * Page the closures of a contiguous run of
94174
94323
  * {@link relatedAggregateProductLocalIDs} entries.
@@ -94218,7 +94367,15 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
94218
94367
  hop.push(localID);
94219
94368
  await this.model.ensureResidentByLocalID(localID);
94220
94369
  }
94221
- const related = relAggregate.RelatedObjects;
94370
+ let related;
94371
+ try {
94372
+ related = relAggregate.RelatedObjects;
94373
+ } catch (error) {
94374
+ if (error instanceof StepBufferNotResidentError) {
94375
+ throw error;
94376
+ }
94377
+ return;
94378
+ }
94222
94379
  if (related !== null && related !== void 0) {
94223
94380
  for (const product of related) {
94224
94381
  await this.ensureResidentForProductExtract(product.localID, pinned, leafSpans);
@@ -94290,21 +94447,29 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
94290
94447
  * @param relAggregate The relationship to read.
94291
94448
  * @return {number[] | undefined} The related objects' express IDs in
94292
94449
  * record order, or undefined when the list holds an entry that is not a
94293
- * `#N` reference — an inline entity, `$`, or junk. Only the generated
94294
- * getter can resolve (or reject) those, so the caller falls back to it
94295
- * rather than guessing.
94450
+ * `#N` reference — an inline entity, `$`, junk, or a field that is not a
94451
+ * list at all. Only the generated getter can resolve (or reject) those,
94452
+ * so the caller falls back to it rather than guessing. A non-residency
94453
+ * failure propagates instead: see the catch.
94296
94454
  */
94297
94455
  relatedObjectExpressIDs_(relAggregate) {
94298
94456
  const expressIDs = [];
94299
94457
  let allReferences = true;
94300
- relAggregate.forEachReferenceInField(REL_AGGREGATES_RELATED_OFFSET, REL_AGGREGATES_RELATED_BASE, REL_AGGREGATES_RELATED_DEPTH, (expressID) => {
94301
- if (expressID === void 0) {
94302
- allReferences = false;
94303
- return false;
94458
+ try {
94459
+ relAggregate.forEachReferenceInField(REL_AGGREGATES_RELATED_OFFSET, REL_AGGREGATES_RELATED_BASE, REL_AGGREGATES_RELATED_DEPTH, (expressID) => {
94460
+ if (expressID === void 0) {
94461
+ allReferences = false;
94462
+ return false;
94463
+ }
94464
+ expressIDs.push(expressID);
94465
+ return true;
94466
+ });
94467
+ } catch (error) {
94468
+ if (error instanceof StepBufferNotResidentError) {
94469
+ throw error;
94304
94470
  }
94305
- expressIDs.push(expressID);
94306
- return true;
94307
- });
94471
+ return void 0;
94472
+ }
94308
94473
  return allReferences ? expressIDs : void 0;
94309
94474
  }
94310
94475
  /**
@@ -95708,6 +95873,7 @@ function doWork() {
95708
95873
  if (statistics !== void 0) {
95709
95874
  const dataParseTime = parseDataTimeEnd - parseDataTimeStart;
95710
95875
  statistics.setLoadStatus("OK");
95876
+ statistics.setWriter("ifc-cli");
95711
95877
  statistics.setParseTime(dataParseTime);
95712
95878
  statistics.setTotalTime(allTime);
95713
95879
  let FILE_NAME = stepHeader.headers.get("FILE_NAME");
@@ -15943,7 +15943,7 @@ var ParsingBuffer = class {
15943
15943
  };
15944
15944
 
15945
15945
  // compiled/src/version/version.js
15946
- var versionString = "Conway v1.567.1542";
15946
+ var versionString = "Conway v1.569.1545";
15947
15947
 
15948
15948
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
15949
15949
  function pThreadsAllowed() {
@@ -16773,6 +16773,31 @@ var Statistics = class {
16773
16773
  setGeometryTime(value) {
16774
16774
  this.geometryTime = value;
16775
16775
  }
16776
+ /**
16777
+ * Which pipeline measured this row (conway#555).
16778
+ *
16779
+ * The perf columns are not all comparable across pipelines: the IFC
16780
+ * regression child runs with `RegressionCaptureState.memoization = FULL`,
16781
+ * which keeps CSG temporaries in the map `calculateGeometrySize()` sums,
16782
+ * so its `Geometry Memory` reads ~30% above the CLI's for the same model
16783
+ * (MB-Khaya, 22.3 vs 16.8 MB). Retention has a matching caveat — the
16784
+ * loader brings up a `ConwayGeometry` per load and the regression children
16785
+ * do not. Naming the writer is what lets a reader, and
16786
+ * `scripts/gen_delta_csv.cjs`, tell those apart instead of differencing
16787
+ * across them.
16788
+ *
16789
+ * @return {string | undefined} - the writer label, or undefined
16790
+ */
16791
+ getWriter() {
16792
+ return this.writer;
16793
+ }
16794
+ /**
16795
+ *
16796
+ * @param value - a stable pipeline identifier, e.g. `ifc-cli`
16797
+ */
16798
+ setWriter(value) {
16799
+ this.writer = value;
16800
+ }
16776
16801
  /**
16777
16802
  *
16778
16803
  * @return {number | undefined} - total execution time or undefined
@@ -17013,7 +17038,7 @@ var Statistics = class {
17013
17038
  const retained = `Retained RSS Delta: ${formatRetained(this.retainedRss)}, Retained Heap-Used Delta: ${formatRetained(this.retainedHeapUsed)}, Retained External Delta: ${formatRetained(this.retainedExternal)}, `;
17014
17039
  const breakdown = this.formatGeometryTypeCounts();
17015
17040
  const geometryTypes = breakdown !== void 0 ? `, Geometry Types: ${breakdown}` : "";
17016
- return `[${dateString}]: Load Status: ${this.loadStatus}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
17041
+ return `[${dateString}]: Load Status: ${this.loadStatus}, Writer: ${this.writer ?? "N/A"}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
17017
17042
  }
17018
17043
  };
17019
17044
 
@@ -86105,6 +86130,7 @@ async function doWork() {
86105
86130
  if (statistics !== void 0) {
86106
86131
  const dataParseTime = parseDataTimeEnd - parseDataTimeStart;
86107
86132
  statistics.setLoadStatus("OK");
86133
+ statistics.setWriter("ap214-cli");
86108
86134
  statistics.setParseTime(dataParseTime);
86109
86135
  statistics.setTotalTime(allTime);
86110
86136
  let FILE_NAME = stepHeader.headers.get("FILE_NAME");
@@ -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.567.1542";
947
+ var versionString = "Conway v1.569.1545";
948
948
 
949
949
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
950
950
  var wasmType = "";
@@ -1035,6 +1035,31 @@ var Statistics = class {
1035
1035
  setGeometryTime(value) {
1036
1036
  this.geometryTime = value;
1037
1037
  }
1038
+ /**
1039
+ * Which pipeline measured this row (conway#555).
1040
+ *
1041
+ * The perf columns are not all comparable across pipelines: the IFC
1042
+ * regression child runs with `RegressionCaptureState.memoization = FULL`,
1043
+ * which keeps CSG temporaries in the map `calculateGeometrySize()` sums,
1044
+ * so its `Geometry Memory` reads ~30% above the CLI's for the same model
1045
+ * (MB-Khaya, 22.3 vs 16.8 MB). Retention has a matching caveat — the
1046
+ * loader brings up a `ConwayGeometry` per load and the regression children
1047
+ * do not. Naming the writer is what lets a reader, and
1048
+ * `scripts/gen_delta_csv.cjs`, tell those apart instead of differencing
1049
+ * across them.
1050
+ *
1051
+ * @return {string | undefined} - the writer label, or undefined
1052
+ */
1053
+ getWriter() {
1054
+ return this.writer;
1055
+ }
1056
+ /**
1057
+ *
1058
+ * @param value - a stable pipeline identifier, e.g. `ifc-cli`
1059
+ */
1060
+ setWriter(value) {
1061
+ this.writer = value;
1062
+ }
1038
1063
  /**
1039
1064
  *
1040
1065
  * @return {number | undefined} - total execution time or undefined
@@ -1275,7 +1300,7 @@ var Statistics = class {
1275
1300
  const retained = `Retained RSS Delta: ${formatRetained(this.retainedRss)}, Retained Heap-Used Delta: ${formatRetained(this.retainedHeapUsed)}, Retained External Delta: ${formatRetained(this.retainedExternal)}, `;
1276
1301
  const breakdown = this.formatGeometryTypeCounts();
1277
1302
  const geometryTypes = breakdown !== void 0 ? `, Geometry Types: ${breakdown}` : "";
1278
- return `[${dateString}]: Load Status: ${this.loadStatus}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
1303
+ return `[${dateString}]: Load Status: ${this.loadStatus}, Writer: ${this.writer ?? "N/A"}, Project Name: ${this.projectName}, Version: ${versionStr}, Conway Version: ${conwayVersionNumber}-${wasmType}, Parse Time: ${this.parseTime} ms, Geometry Time: ${this.geometryTime} ms, Total Time: ${this.totalTime} ms, Geometry Memory: ${this.geometryMemory?.toFixed(3)} MB, WASM Heap High-Water: ${this.wasmHeapPeak?.toFixed(3)} MB, ` + retained + products + `Memory Statistics: ${this.memoryStatistics}, Preprocessor Version: ${this.preprocessorVersion}, Originating System: ${this.originatingSystem}` + geometryTypes;
1279
1304
  }
1280
1305
  };
1281
1306
 
@@ -217,6 +217,7 @@ async function doWork() {
217
217
  if (statistics !== void 0) {
218
218
  const dataParseTime = parseDataTimeEnd - parseDataTimeStart;
219
219
  statistics.setLoadStatus("OK");
220
+ statistics.setWriter("ap214-cli");
220
221
  statistics.setParseTime(dataParseTime);
221
222
  statistics.setTotalTime(allTime);
222
223
  let FILE_NAME = stepHeader.headers.get("FILE_NAME");
@@ -45,6 +45,18 @@ const KB_PER_MB = 1024;
45
45
  const PERF_MB_PRECISION = 2;
46
46
  /** Placeholder for a column this row has no measurement for. */
47
47
  const UNMEASURED = "N/A";
48
+ /**
49
+ * This child's value for the `writer` column (conway#555).
50
+ *
51
+ * Distinct from the IFC child's for a reason that is easy to miss: the two
52
+ * run as separate processes and `RegressionCaptureState.memoization` is a
53
+ * process-global, so the IFC child's `FULL` — which keeps CSG temporaries in
54
+ * the map `calculateGeometrySize()` sums — does not apply here. A mixed
55
+ * IFC/STEP corpus therefore aggregates two capture modes into one
56
+ * `geometryMemoryMb` column, which is precisely what this column exists to
57
+ * disclose.
58
+ */
59
+ const PERF_WRITER = "ap214-regression";
48
60
  /**
49
61
  * Capture the end-of-load memory figures for a perf row.
50
62
  *
@@ -80,17 +92,23 @@ function capturePerfMemory(geometryMemoryBytes, wasmHeapBytes) {
80
92
  * was not exposed to settle with. No `retainedWasmHeapMb` — the wasm heap is
81
93
  * grow-only, so over one cycle it could only restate `peakWasmHeapMb`.
82
94
  *
95
+ * `writer` names the pipeline (conway#555) and `totalTimeMs` is the load's
96
+ * wall clock rather than the sum of the two stage columns (conway#562), with
97
+ * that sum kept as `parsePlusGeometryMs`. Both changes are described in full
98
+ * on the IFC child's copy.
99
+ *
83
100
  * @param perfPath Path to write the CSV to. Empty string disables.
84
101
  * @param stepFile Source STEP file path (basename used as the row key).
85
102
  * @param status OK or FAIL.
86
103
  * @param parseTimeMs Parse stage duration in ms.
87
104
  * @param geometryTimeMs Geometry extraction duration in ms.
88
- * @param totalTimeMs Sum of parse + geometry in ms.
105
+ * @param totalTimeMs The load's wall clock — file read through teardown.
106
+ * @param parsePlusGeometryMs The sum of the two stage columns above.
89
107
  * @param memory The row's memory figures, captured at the points in the load
90
108
  * each is defined at. Defaults to capturing the end-of-load instants here,
91
109
  * which is what the FAIL paths want — they have nothing else to report.
92
110
  */
93
- async function writePerfCsvIfRequested(perfPath, stepFile, status, parseTimeMs, geometryTimeMs, totalTimeMs, memory = capturePerfMemory()) {
111
+ async function writePerfCsvIfRequested(perfPath, stepFile, status, parseTimeMs, geometryTimeMs, totalTimeMs, parsePlusGeometryMs, memory = capturePerfMemory()) {
94
112
  if (perfPath.length === 0) {
95
113
  return;
96
114
  }
@@ -116,10 +134,12 @@ async function writePerfCsvIfRequested(perfPath, stepFile, status, parseTimeMs,
116
134
  const retainedExternalMb = retained !== void 0 ?
117
135
  retained.externalMb.toFixed(PERF_MB_PRECISION) : UNMEASURED;
118
136
  const fileName = csvSafeString(path.basename(stepFile));
119
- const header = "file,status,parseTimeMs,geometryTimeMs,totalTimeMs,geometryMemoryMb," +
137
+ const header = "file,status,writer,parseTimeMs,geometryTimeMs,totalTimeMs," +
138
+ "parsePlusGeometryMs,geometryMemoryMb," +
120
139
  "peakWasmHeapMb,rssMb,peakRssMb,heapUsedMb,heapTotalMb,externalMb," +
121
140
  "arrayBuffersMb,retainedRssMb,retainedHeapUsedMb,retainedExternalMb\n";
122
- const row = `${fileName},${status},${parseTimeMs},${geometryTimeMs},${totalTimeMs},` +
141
+ const row = `${fileName},${status},${PERF_WRITER},${parseTimeMs},${geometryTimeMs},` +
142
+ `${totalTimeMs},${parsePlusGeometryMs},` +
123
143
  `${geometryMemoryMb},${peakWasmHeapMb},${rssMb},${peakRssMb},` +
124
144
  `${heapUsedMb},${heapTotalMb},${externalMb},${arrayBuffersMb},` +
125
145
  `${retainedRssMb},${retainedHeapUsedMb},${retainedExternalMb}\n`;
@@ -213,6 +233,10 @@ function doWork() {
213
233
  // reason recorded on that function: the batch passes `--expose-gc` to
214
234
  // every child, not just the ones given `--perf`.
215
235
  const memoryBaseline = await settleAndSampleMemoryForPerf(perfPath);
236
+ // Where `totalTimeMs` starts (conway#562): after the settle, so the
237
+ // retention samples stay outside every timed column, and before the
238
+ // file read, which is real load cost no stage clock could see.
239
+ const loadStartMs = Date.now();
216
240
  try {
217
241
  stepBuffer = fs.readFileSync(stepFile);
218
242
  }
@@ -268,7 +292,7 @@ function doWork() {
268
292
  const parseEndMs = Date.now();
269
293
  const parseTimeMs = parseEndMs - parseStartMs;
270
294
  if (model === void 0) {
271
- await writePerfCsvIfRequested(perfPath, stepFile, "FAIL", parseTimeMs, 0, parseTimeMs);
295
+ await writePerfCsvIfRequested(perfPath, stepFile, "FAIL", parseTimeMs, 0, Date.now() - loadStartMs, parseTimeMs);
272
296
  displayErrors(stepFile);
273
297
  return;
274
298
  }
@@ -277,7 +301,9 @@ function doWork() {
277
301
  const extraction = geometryExtraction(model);
278
302
  const geomEndMs = Date.now();
279
303
  const geometryTimeMs = geomEndMs - geomStartMs;
280
- const totalTimeMs = geomEndMs - parseStartMs;
304
+ // The sum `totalTimeMs` used to hold — an identity by construction,
305
+ // since `geomStartMs` is taken on the line after `parseEndMs`.
306
+ const parsePlusGeometryMs = geomEndMs - parseStartMs;
281
307
  const perfStatus = extraction === void 0 ? "FAIL" : "OK";
282
308
  // Sized before anything downstream can release meshes, and only on
283
309
  // the OK path: a failed extraction leaves a partial cache whose size
@@ -301,8 +327,10 @@ function doWork() {
301
327
  // the blessed output beats saving the work. The settle below IS
302
328
  // gated; see `memoryBaseline` above.
303
329
  model.invalidate(true);
330
+ // Closes on the teardown, before the retained settle.
331
+ const totalTimeMs = Date.now() - loadStartMs;
304
332
  memory.retained = retainedMemoryMb(memoryBaseline, await settleAndSampleMemoryForPerf(perfPath));
305
- await writePerfCsvIfRequested(perfPath, stepFile, perfStatus, parseTimeMs, geometryTimeMs, totalTimeMs, memory);
333
+ await writePerfCsvIfRequested(perfPath, stepFile, perfStatus, parseTimeMs, geometryTimeMs, totalTimeMs, parsePlusGeometryMs, memory);
306
334
  // AFTP sizing pass: no-op unless the wasm module was built with
307
335
  // CONWAY_ALLOC_TELEMETRY (see conway-geom structures/alloc_telemetry.h).
308
336
  conwayGeom.dumpAllocTelemetry(path.basename(stepFile));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=geometry_progress_accounting.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry_progress_accounting.test.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/geometry_progress_accounting.test.ts"],"names":[],"mappings":""}