@bldrs-ai/conway 1.528.1490 → 1.530.1503

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 (58) hide show
  1. package/compiled/examples/browser-bundled.cjs +29 -20
  2. package/compiled/examples/cli-bundled.cjs +74 -27
  3. package/compiled/examples/cli-step-bundled.cjs +27 -16
  4. package/compiled/examples/validator-bundled.cjs +29 -20
  5. package/compiled/src/ifc/ifc_demand_extraction.test.js +129 -0
  6. package/compiled/src/ifc/ifc_geometry_extraction.d.ts +31 -2
  7. package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
  8. package/compiled/src/ifc/ifc_geometry_extraction.js +68 -7
  9. package/compiled/src/ifc/ifc_spatial_skeleton.d.ts +145 -0
  10. package/compiled/src/ifc/ifc_spatial_skeleton.d.ts.map +1 -0
  11. package/compiled/src/ifc/ifc_spatial_skeleton.js +266 -0
  12. package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts +2 -0
  13. package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts.map +1 -0
  14. package/compiled/src/ifc/ifc_spatial_skeleton.test.js +199 -0
  15. package/compiled/src/ifc/ifc_stream_open.d.ts +41 -5
  16. package/compiled/src/ifc/ifc_stream_open.d.ts.map +1 -1
  17. package/compiled/src/ifc/ifc_stream_open.js +29 -2
  18. package/compiled/src/ifc/ifc_stream_open.test.js +26 -1
  19. package/compiled/src/index.d.ts +7 -3
  20. package/compiled/src/index.d.ts.map +1 -1
  21. package/compiled/src/index.js +6 -2
  22. package/compiled/src/namespace_surface.test.js +6 -1
  23. package/compiled/src/step/parsing/prefix_type_index.d.ts +124 -0
  24. package/compiled/src/step/parsing/prefix_type_index.d.ts.map +1 -0
  25. package/compiled/src/step/parsing/prefix_type_index.js +207 -0
  26. package/compiled/src/step/parsing/prefix_type_index.test.d.ts +2 -0
  27. package/compiled/src/step/parsing/prefix_type_index.test.d.ts.map +1 -0
  28. package/compiled/src/step/parsing/prefix_type_index.test.js +298 -0
  29. package/compiled/src/step/parsing/record_event.d.ts +23 -0
  30. package/compiled/src/step/parsing/record_event.d.ts.map +1 -0
  31. package/compiled/src/step/parsing/record_event.js +1 -0
  32. package/compiled/src/step/parsing/record_field_cursor.d.ts +94 -0
  33. package/compiled/src/step/parsing/record_field_cursor.d.ts.map +1 -0
  34. package/compiled/src/step/parsing/record_field_cursor.js +189 -0
  35. package/compiled/src/step/parsing/step_parser.d.ts +9 -6
  36. package/compiled/src/step/parsing/step_parser.d.ts.map +1 -1
  37. package/compiled/src/step/parsing/step_parser.js +21 -14
  38. package/compiled/src/step/parsing/streaming_index_builder.d.ts +13 -5
  39. package/compiled/src/step/parsing/streaming_index_builder.d.ts.map +1 -1
  40. package/compiled/src/step/parsing/streaming_index_builder.js +9 -4
  41. package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts +24 -8
  42. package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts.map +1 -1
  43. package/compiled/src/step/parsing/streaming_record_dispatcher.js +34 -13
  44. package/compiled/src/step/step_model_base.d.ts +9 -1
  45. package/compiled/src/step/step_model_base.d.ts.map +1 -1
  46. package/compiled/src/step/step_model_base.js +10 -1
  47. package/compiled/src/stream/index.d.ts +12 -5
  48. package/compiled/src/stream/index.d.ts.map +1 -1
  49. package/compiled/src/stream/index.js +11 -4
  50. package/compiled/src/version/version.js +1 -1
  51. package/compiled/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +1 -1
  53. package/compiled/src/step/parsing/incremental_type_index.d.ts +0 -64
  54. package/compiled/src/step/parsing/incremental_type_index.d.ts.map +0 -1
  55. package/compiled/src/step/parsing/incremental_type_index.js +0 -92
  56. package/compiled/src/step/parsing/incremental_type_index.test.d.ts +0 -2
  57. package/compiled/src/step/parsing/incremental_type_index.test.d.ts.map +0 -1
  58. package/compiled/src/step/parsing/incremental_type_index.test.js +0 -55
@@ -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.528.1490";
33
+ var versionString = "Conway v1.530.1503";
34
34
 
35
35
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
36
36
  var wasmType = "";
@@ -2626,10 +2626,12 @@ var StepParser = class extends StepHeaderParser {
2626
2626
  * buffer; the callback may rebase the buffer's window in place. Must be
2627
2627
  * synchronous on this driver — a returned Promise is a caller bug.
2628
2628
  * @param onRecordIndexed Called as each top-level record is indexed, with
2629
- * its localID, expressID and typeID (0 for external-mapping records) the
2630
- * seam for incremental semantic consumers (type index, roots registry,
2631
- * names skeleton). Must be synchronous and cheap; expensive work belongs on
2632
- * a demand queue, not the parse path.
2629
+ * its localID, expressID, typeID (0 for external-mapping records) and the
2630
+ * record's bytes as an un-materialised `(buffer, byteOffset, byteLength)`
2631
+ * window view the seam for incremental semantic consumers (type index,
2632
+ * roots registry, names skeleton). See {@link RecordEventHandler} for the
2633
+ * window-lifetime rule. Must be synchronous and cheap; expensive work
2634
+ * belongs on a demand queue, not the parse path.
2633
2635
  * @param onProgress Optional byte-cursor progress callback.
2634
2636
  * @return {BlockParseResult} The parsing result, including the index and result enum.
2635
2637
  */
@@ -2913,11 +2915,6 @@ var StepParser = class extends StepHeaderParser {
2913
2915
  if (!charws(SEMICOLON)) {
2914
2916
  return syntaxError();
2915
2917
  }
2916
- {
2917
- const recordLen = input2.address - startElement2;
2918
- onRecordIndexed?.(topLevelCount, expressID, 0, input2.buffer.subarray(input2.cursor - recordLen, input2.cursor));
2919
- }
2920
- ++topLevelCount;
2921
2918
  pushEntry({
2922
2919
  address: startElement2,
2923
2920
  length: input2.address - startElement2,
@@ -2927,6 +2924,11 @@ var StepParser = class extends StepHeaderParser {
2927
2924
  inlineEntities: inlineElements,
2928
2925
  multiMapping: multiElements
2929
2926
  });
2927
+ {
2928
+ const recordLen = input2.address - startElement2;
2929
+ onRecordIndexed?.(topLevelCount, expressID, 0, input2.buffer, input2.cursor - recordLen, recordLen);
2930
+ }
2931
+ ++topLevelCount;
2930
2932
  multiElements = void 0;
2931
2933
  continue;
2932
2934
  }
@@ -3006,11 +3008,6 @@ var StepParser = class extends StepHeaderParser {
3006
3008
  if (!charws(SEMICOLON)) {
3007
3009
  return syntaxError();
3008
3010
  }
3009
- {
3010
- const recordLen = input2.address - startElement;
3011
- onRecordIndexed?.(topLevelCount, expressID, foundItem, input2.buffer.subarray(input2.cursor - recordLen, input2.cursor));
3012
- }
3013
- ++topLevelCount;
3014
3011
  pushEntry({
3015
3012
  address: startElement,
3016
3013
  length: input2.address - startElement,
@@ -3018,6 +3015,11 @@ var StepParser = class extends StepHeaderParser {
3018
3015
  expressID,
3019
3016
  inlineEntities: inlineElements
3020
3017
  });
3018
+ {
3019
+ const recordLen = input2.address - startElement;
3020
+ onRecordIndexed?.(topLevelCount, expressID, foundItem, input2.buffer, input2.cursor - recordLen, recordLen);
3021
+ }
3022
+ ++topLevelCount;
3021
3023
  }
3022
3024
  return parseResult2(ParseResult.INCOMPLETE);
3023
3025
  }
@@ -5812,9 +5814,17 @@ var StepModelBase = class _StepModelBase {
5812
5814
  * extract reads `IfcIndexedPolygonalFace.CoordIndex` as integers —
5813
5815
  * following those faces pinned ~78k records per PSB product. If
5814
5816
  * omitted, `!descend` children use this span path (test helper).
5817
+ * @param claimed Optional out-set of the IDs *this call* claimed and
5818
+ * awaited residency for — a strict subset of `seen`. When `seen` is
5819
+ * shared across concurrent walks (a batch of products sharing mapped
5820
+ * geometry), an ID another walk claimed is skipped here and is NOT
5821
+ * resident yet from this call's point of view: that walk's pin holds
5822
+ * the chunk once it lands, but its read is still in flight. Anything
5823
+ * that must synchronously read a record's bytes after this returns
5824
+ * has to iterate `claimed`, never `seen` (conway#526).
5815
5825
  * @return {Promise<Set<number>>} The local IDs that were visited.
5816
5826
  */
5817
- async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf) {
5827
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf, claimed) {
5818
5828
  if (!this.isSourceExternal) {
5819
5829
  return seen;
5820
5830
  }
@@ -5839,6 +5849,7 @@ var StepModelBase = class _StepModelBase {
5839
5849
  seen.add(id);
5840
5850
  this.pinByLocalID(id);
5841
5851
  wave.push(id);
5852
+ claimed?.add(id);
5842
5853
  }
5843
5854
  frontier.length = 0;
5844
5855
  if (wave.length === 0) {
@@ -72154,13 +72165,11 @@ async function buildIndexStreamingAsync(source, parser211, pool3, onRecordIndexe
72154
72165
  };
72155
72166
  }
72156
72167
  }
72157
- function buildColumnarIndexStreaming(source, parser211, pool3, onRecordIndexed) {
72158
- const sink = new ColumnarIndexSink();
72168
+ function buildColumnarIndexStreaming(source, parser211, pool3, onRecordIndexed, sink = new ColumnarIndexSink()) {
72159
72169
  const { header, result, stats } = buildIndexStreaming(source, parser211, pool3, onRecordIndexed, sink);
72160
72170
  return { header, columns: sink.finalize(), result, stats };
72161
72171
  }
72162
- async function buildColumnarIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, onProgress, yieldIntervalMs) {
72163
- const sink = new ColumnarIndexSink();
72172
+ async function buildColumnarIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, onProgress, yieldIntervalMs, sink = new ColumnarIndexSink()) {
72164
72173
  const { header, result, stats } = await buildIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs);
72165
72174
  return { header, columns: sink.finalize(), result, stats };
72166
72175
  }
@@ -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.528.1490";
14968
+ var versionString = "Conway v1.530.1503";
14969
14969
 
14970
14970
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
14971
14971
  function pThreadsAllowed() {
@@ -18462,10 +18462,12 @@ var StepParser = class extends StepHeaderParser {
18462
18462
  * buffer; the callback may rebase the buffer's window in place. Must be
18463
18463
  * synchronous on this driver — a returned Promise is a caller bug.
18464
18464
  * @param onRecordIndexed Called as each top-level record is indexed, with
18465
- * its localID, expressID and typeID (0 for external-mapping records) the
18466
- * seam for incremental semantic consumers (type index, roots registry,
18467
- * names skeleton). Must be synchronous and cheap; expensive work belongs on
18468
- * a demand queue, not the parse path.
18465
+ * its localID, expressID, typeID (0 for external-mapping records) and the
18466
+ * record's bytes as an un-materialised `(buffer, byteOffset, byteLength)`
18467
+ * window view the seam for incremental semantic consumers (type index,
18468
+ * roots registry, names skeleton). See {@link RecordEventHandler} for the
18469
+ * window-lifetime rule. Must be synchronous and cheap; expensive work
18470
+ * belongs on a demand queue, not the parse path.
18469
18471
  * @param onProgress Optional byte-cursor progress callback.
18470
18472
  * @return {BlockParseResult} The parsing result, including the index and result enum.
18471
18473
  */
@@ -18749,11 +18751,6 @@ var StepParser = class extends StepHeaderParser {
18749
18751
  if (!charws(SEMICOLON)) {
18750
18752
  return syntaxError();
18751
18753
  }
18752
- {
18753
- const recordLen = input.address - startElement2;
18754
- onRecordIndexed?.(topLevelCount, expressID, 0, input.buffer.subarray(input.cursor - recordLen, input.cursor));
18755
- }
18756
- ++topLevelCount;
18757
18754
  pushEntry({
18758
18755
  address: startElement2,
18759
18756
  length: input.address - startElement2,
@@ -18763,6 +18760,11 @@ var StepParser = class extends StepHeaderParser {
18763
18760
  inlineEntities: inlineElements,
18764
18761
  multiMapping: multiElements
18765
18762
  });
18763
+ {
18764
+ const recordLen = input.address - startElement2;
18765
+ onRecordIndexed?.(topLevelCount, expressID, 0, input.buffer, input.cursor - recordLen, recordLen);
18766
+ }
18767
+ ++topLevelCount;
18766
18768
  multiElements = void 0;
18767
18769
  continue;
18768
18770
  }
@@ -18842,11 +18844,6 @@ var StepParser = class extends StepHeaderParser {
18842
18844
  if (!charws(SEMICOLON)) {
18843
18845
  return syntaxError();
18844
18846
  }
18845
- {
18846
- const recordLen = input.address - startElement;
18847
- onRecordIndexed?.(topLevelCount, expressID, foundItem, input.buffer.subarray(input.cursor - recordLen, input.cursor));
18848
- }
18849
- ++topLevelCount;
18850
18847
  pushEntry({
18851
18848
  address: startElement,
18852
18849
  length: input.address - startElement,
@@ -18854,6 +18851,11 @@ var StepParser = class extends StepHeaderParser {
18854
18851
  expressID,
18855
18852
  inlineEntities: inlineElements
18856
18853
  });
18854
+ {
18855
+ const recordLen = input.address - startElement;
18856
+ onRecordIndexed?.(topLevelCount, expressID, foundItem, input.buffer, input.cursor - recordLen, recordLen);
18857
+ }
18858
+ ++topLevelCount;
18857
18859
  }
18858
18860
  return parseResult(ParseResult.INCOMPLETE);
18859
18861
  }
@@ -21695,9 +21697,17 @@ var StepModelBase = class _StepModelBase {
21695
21697
  * extract reads `IfcIndexedPolygonalFace.CoordIndex` as integers —
21696
21698
  * following those faces pinned ~78k records per PSB product. If
21697
21699
  * omitted, `!descend` children use this span path (test helper).
21700
+ * @param claimed Optional out-set of the IDs *this call* claimed and
21701
+ * awaited residency for — a strict subset of `seen`. When `seen` is
21702
+ * shared across concurrent walks (a batch of products sharing mapped
21703
+ * geometry), an ID another walk claimed is skipped here and is NOT
21704
+ * resident yet from this call's point of view: that walk's pin holds
21705
+ * the chunk once it lands, but its read is still in flight. Anything
21706
+ * that must synchronously read a record's bytes after this returns
21707
+ * has to iterate `claimed`, never `seen` (conway#526).
21698
21708
  * @return {Promise<Set<number>>} The local IDs that were visited.
21699
21709
  */
21700
- async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf) {
21710
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf, claimed) {
21701
21711
  if (!this.isSourceExternal) {
21702
21712
  return seen;
21703
21713
  }
@@ -21722,6 +21732,7 @@ var StepModelBase = class _StepModelBase {
21722
21732
  seen.add(id);
21723
21733
  this.pinByLocalID(id);
21724
21734
  wave.push(id);
21735
+ claimed?.add(id);
21725
21736
  }
21726
21737
  frontier.length = 0;
21727
21738
  if (wave.length === 0) {
@@ -88122,13 +88133,11 @@ async function buildIndexStreamingAsync(source, parser210, pool3, onRecordIndexe
88122
88133
  };
88123
88134
  }
88124
88135
  }
88125
- function buildColumnarIndexStreaming(source, parser210, pool3, onRecordIndexed) {
88126
- const sink = new ColumnarIndexSink();
88136
+ function buildColumnarIndexStreaming(source, parser210, pool3, onRecordIndexed, sink = new ColumnarIndexSink()) {
88127
88137
  const { header, result, stats } = buildIndexStreaming(source, parser210, pool3, onRecordIndexed, sink);
88128
88138
  return { header, columns: sink.finalize(), result, stats };
88129
88139
  }
88130
- async function buildColumnarIndexStreamingAsync(source, parser210, pool3, onRecordIndexed, onProgress, yieldIntervalMs) {
88131
- const sink = new ColumnarIndexSink();
88140
+ async function buildColumnarIndexStreamingAsync(source, parser210, pool3, onRecordIndexed, onProgress, yieldIntervalMs, sink = new ColumnarIndexSink()) {
88132
88141
  const { header, result, stats } = await buildIndexStreamingAsync(source, parser210, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs);
88133
88142
  return { header, columns: sink.finalize(), result, stats };
88134
88143
  }
@@ -93260,6 +93269,24 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93260
93269
  if (!this.model.isSourceExternal) {
93261
93270
  return seen ?? /* @__PURE__ */ new Set();
93262
93271
  }
93272
+ try {
93273
+ return await this.ensureResidentForProductExtractUnguarded_(localID, seen, leafSpans);
93274
+ } catch (error) {
93275
+ logger_default.error(`Error paging product localID ${localID} for extract: ${error instanceof Error ? error.message : String(error)}`);
93276
+ return seen ?? /* @__PURE__ */ new Set();
93277
+ }
93278
+ }
93279
+ /**
93280
+ * {@link ensureResidentForProductExtract} without the per-product
93281
+ * fault containment — split out so the guard cannot accidentally
93282
+ * swallow a failure raised by its own bookkeeping.
93283
+ *
93284
+ * @param localID The product's local ID.
93285
+ * @param seen Optional set to extend.
93286
+ * @param leafSpans Coalesced Faces[] pin ranges — caller unpins.
93287
+ * @return {Promise<Set<number>>} Pinned local IDs.
93288
+ */
93289
+ async ensureResidentForProductExtractUnguarded_(localID, seen, leafSpans) {
93263
93290
  const extra = [];
93264
93291
  const voids = this.productToVoidGeometryMap.get(localID);
93265
93292
  if (voids !== void 0) {
@@ -93277,7 +93304,8 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93277
93304
  const typeID = this.model.typeIDOf(id);
93278
93305
  return !isFace(id) && typeID !== entity_types_ifc_gen_default.IFCPOLYGONALFACESET && typeID !== entity_types_ifc_gen_default.IFCTRIANGULATEDFACESET;
93279
93306
  };
93280
- const visited = await this.model.ensureResidentClosureByLocalID(localID, extra, seen, descend, leafSpans, isFace);
93307
+ const claimed = /* @__PURE__ */ new Set();
93308
+ const visited = await this.model.ensureResidentClosureByLocalID(localID, extra, seen, descend, leafSpans, isFace, claimed);
93281
93309
  const styleExtra = [];
93282
93310
  for (const visitedID of visited) {
93283
93311
  const styled = this.materials.styledItemMap.get(visitedID);
@@ -93290,9 +93318,9 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93290
93318
  }
93291
93319
  }
93292
93320
  if (styleExtra.length > 0) {
93293
- await this.model.ensureResidentClosureByLocalID(localID, styleExtra, visited, descend, leafSpans, isFace);
93321
+ await this.model.ensureResidentClosureByLocalID(localID, styleExtra, visited, descend, leafSpans, isFace, claimed);
93294
93322
  }
93295
- await this.ensureResidentVisitedFacesetPayloads_(visited, leafSpans);
93323
+ await this.ensureResidentVisitedFacesetPayloads_(claimed, visited, leafSpans);
93296
93324
  return visited;
93297
93325
  }
93298
93326
  /**
@@ -93300,16 +93328,35 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
93300
93328
  * or more tight address spans. The generic closure visits the
93301
93329
  * faceset but does not scan Faces[].
93302
93330
  *
93303
- * @param visited Faceset local IDs already pinned (and the set to
93304
- * extend with Coordinates).
93331
+ * **Iterates `claimed`, not `visited`, and re-ensures before the
93332
+ * scan** both halves of conway#526. `referencedExpressIDs` is a
93333
+ * SYNCHRONOUS acquire of the faceset's own bytes, and when a batch
93334
+ * prefetches products concurrently through one shared `visited` set
93335
+ * (`extractGeometryBatchAsync`'s Promise.all over 8 products), the
93336
+ * closure walk skips any ID a sibling product already claimed. That
93337
+ * sibling has pinned the range but its read may still be in flight,
93338
+ * so scanning the shared set threw
93339
+ * `StepBufferNotResidentError: STEP source range [...] is not
93340
+ * resident` out of the prefetch and killed the whole load — a pin
93341
+ * reserves a chunk against eviction, it does not make it resident.
93342
+ * Iterating this call's own claims fixes that (and drops the
93343
+ * quadratic re-scan of every sibling's facesets); the `await` below
93344
+ * makes the guarantee local rather than inherited, since a resident
93345
+ * chunk costs only a microtask there.
93346
+ *
93347
+ * @param claimed Faceset local IDs this call claimed and awaited.
93348
+ * @param visited The (possibly shared) visited set — read for
93349
+ * Coordinates de-duplication and extended with the ones pinned here,
93350
+ * so the caller's unpin covers them.
93305
93351
  * @param leafSpans Coalesced Faces[] pin ranges — caller unpins.
93306
93352
  */
93307
- async ensureResidentVisitedFacesetPayloads_(visited, leafSpans) {
93308
- for (const localID of visited) {
93353
+ async ensureResidentVisitedFacesetPayloads_(claimed, visited, leafSpans) {
93354
+ for (const localID of claimed) {
93309
93355
  const typeID = this.model.typeIDOf(localID);
93310
93356
  if (typeID !== entity_types_ifc_gen_default.IFCPOLYGONALFACESET && typeID !== entity_types_ifc_gen_default.IFCTRIANGULATEDFACESET) {
93311
93357
  continue;
93312
93358
  }
93359
+ await this.model.ensureResidentByLocalID(localID);
93313
93360
  const refs = this.model.referencedExpressIDs(localID);
93314
93361
  if (refs.length === 0) {
93315
93362
  continue;
@@ -15943,7 +15943,7 @@ var ParsingBuffer = class {
15943
15943
  };
15944
15944
 
15945
15945
  // compiled/src/version/version.js
15946
- var versionString = "Conway v1.528.1490";
15946
+ var versionString = "Conway v1.530.1503";
15947
15947
 
15948
15948
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
15949
15949
  function pThreadsAllowed() {
@@ -18365,10 +18365,12 @@ var StepParser = class extends StepHeaderParser {
18365
18365
  * buffer; the callback may rebase the buffer's window in place. Must be
18366
18366
  * synchronous on this driver — a returned Promise is a caller bug.
18367
18367
  * @param onRecordIndexed Called as each top-level record is indexed, with
18368
- * its localID, expressID and typeID (0 for external-mapping records) the
18369
- * seam for incremental semantic consumers (type index, roots registry,
18370
- * names skeleton). Must be synchronous and cheap; expensive work belongs on
18371
- * a demand queue, not the parse path.
18368
+ * its localID, expressID, typeID (0 for external-mapping records) and the
18369
+ * record's bytes as an un-materialised `(buffer, byteOffset, byteLength)`
18370
+ * window view the seam for incremental semantic consumers (type index,
18371
+ * roots registry, names skeleton). See {@link RecordEventHandler} for the
18372
+ * window-lifetime rule. Must be synchronous and cheap; expensive work
18373
+ * belongs on a demand queue, not the parse path.
18372
18374
  * @param onProgress Optional byte-cursor progress callback.
18373
18375
  * @return {BlockParseResult} The parsing result, including the index and result enum.
18374
18376
  */
@@ -18652,11 +18654,6 @@ var StepParser = class extends StepHeaderParser {
18652
18654
  if (!charws(SEMICOLON)) {
18653
18655
  return syntaxError();
18654
18656
  }
18655
- {
18656
- const recordLen = input.address - startElement2;
18657
- onRecordIndexed?.(topLevelCount, expressID, 0, input.buffer.subarray(input.cursor - recordLen, input.cursor));
18658
- }
18659
- ++topLevelCount;
18660
18657
  pushEntry({
18661
18658
  address: startElement2,
18662
18659
  length: input.address - startElement2,
@@ -18666,6 +18663,11 @@ var StepParser = class extends StepHeaderParser {
18666
18663
  inlineEntities: inlineElements,
18667
18664
  multiMapping: multiElements
18668
18665
  });
18666
+ {
18667
+ const recordLen = input.address - startElement2;
18668
+ onRecordIndexed?.(topLevelCount, expressID, 0, input.buffer, input.cursor - recordLen, recordLen);
18669
+ }
18670
+ ++topLevelCount;
18669
18671
  multiElements = void 0;
18670
18672
  continue;
18671
18673
  }
@@ -18745,11 +18747,6 @@ var StepParser = class extends StepHeaderParser {
18745
18747
  if (!charws(SEMICOLON)) {
18746
18748
  return syntaxError();
18747
18749
  }
18748
- {
18749
- const recordLen = input.address - startElement;
18750
- onRecordIndexed?.(topLevelCount, expressID, foundItem, input.buffer.subarray(input.cursor - recordLen, input.cursor));
18751
- }
18752
- ++topLevelCount;
18753
18750
  pushEntry({
18754
18751
  address: startElement,
18755
18752
  length: input.address - startElement,
@@ -18757,6 +18754,11 @@ var StepParser = class extends StepHeaderParser {
18757
18754
  expressID,
18758
18755
  inlineEntities: inlineElements
18759
18756
  });
18757
+ {
18758
+ const recordLen = input.address - startElement;
18759
+ onRecordIndexed?.(topLevelCount, expressID, foundItem, input.buffer, input.cursor - recordLen, recordLen);
18760
+ }
18761
+ ++topLevelCount;
18760
18762
  }
18761
18763
  return parseResult(ParseResult.INCOMPLETE);
18762
18764
  }
@@ -21434,9 +21436,17 @@ var StepModelBase = class _StepModelBase {
21434
21436
  * extract reads `IfcIndexedPolygonalFace.CoordIndex` as integers —
21435
21437
  * following those faces pinned ~78k records per PSB product. If
21436
21438
  * omitted, `!descend` children use this span path (test helper).
21439
+ * @param claimed Optional out-set of the IDs *this call* claimed and
21440
+ * awaited residency for — a strict subset of `seen`. When `seen` is
21441
+ * shared across concurrent walks (a batch of products sharing mapped
21442
+ * geometry), an ID another walk claimed is skipped here and is NOT
21443
+ * resident yet from this call's point of view: that walk's pin holds
21444
+ * the chunk once it lands, but its read is still in flight. Anything
21445
+ * that must synchronously read a record's bytes after this returns
21446
+ * has to iterate `claimed`, never `seen` (conway#526).
21437
21447
  * @return {Promise<Set<number>>} The local IDs that were visited.
21438
21448
  */
21439
- async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf) {
21449
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf, claimed) {
21440
21450
  if (!this.isSourceExternal) {
21441
21451
  return seen;
21442
21452
  }
@@ -21461,6 +21471,7 @@ var StepModelBase = class _StepModelBase {
21461
21471
  seen.add(id);
21462
21472
  this.pinByLocalID(id);
21463
21473
  wave.push(id);
21474
+ claimed?.add(id);
21464
21475
  }
21465
21476
  frontier.length = 0;
21466
21477
  if (wave.length === 0) {
@@ -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.528.1490";
947
+ var versionString = "Conway v1.530.1503";
948
948
 
949
949
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
950
950
  var wasmType = "";
@@ -3540,10 +3540,12 @@ var StepParser = class extends StepHeaderParser {
3540
3540
  * buffer; the callback may rebase the buffer's window in place. Must be
3541
3541
  * synchronous on this driver — a returned Promise is a caller bug.
3542
3542
  * @param onRecordIndexed Called as each top-level record is indexed, with
3543
- * its localID, expressID and typeID (0 for external-mapping records) the
3544
- * seam for incremental semantic consumers (type index, roots registry,
3545
- * names skeleton). Must be synchronous and cheap; expensive work belongs on
3546
- * a demand queue, not the parse path.
3543
+ * its localID, expressID, typeID (0 for external-mapping records) and the
3544
+ * record's bytes as an un-materialised `(buffer, byteOffset, byteLength)`
3545
+ * window view the seam for incremental semantic consumers (type index,
3546
+ * roots registry, names skeleton). See {@link RecordEventHandler} for the
3547
+ * window-lifetime rule. Must be synchronous and cheap; expensive work
3548
+ * belongs on a demand queue, not the parse path.
3547
3549
  * @param onProgress Optional byte-cursor progress callback.
3548
3550
  * @return {BlockParseResult} The parsing result, including the index and result enum.
3549
3551
  */
@@ -3827,11 +3829,6 @@ var StepParser = class extends StepHeaderParser {
3827
3829
  if (!charws(SEMICOLON)) {
3828
3830
  return syntaxError();
3829
3831
  }
3830
- {
3831
- const recordLen = input.address - startElement2;
3832
- onRecordIndexed?.(topLevelCount, expressID, 0, input.buffer.subarray(input.cursor - recordLen, input.cursor));
3833
- }
3834
- ++topLevelCount;
3835
3832
  pushEntry({
3836
3833
  address: startElement2,
3837
3834
  length: input.address - startElement2,
@@ -3841,6 +3838,11 @@ var StepParser = class extends StepHeaderParser {
3841
3838
  inlineEntities: inlineElements,
3842
3839
  multiMapping: multiElements
3843
3840
  });
3841
+ {
3842
+ const recordLen = input.address - startElement2;
3843
+ onRecordIndexed?.(topLevelCount, expressID, 0, input.buffer, input.cursor - recordLen, recordLen);
3844
+ }
3845
+ ++topLevelCount;
3844
3846
  multiElements = void 0;
3845
3847
  continue;
3846
3848
  }
@@ -3920,11 +3922,6 @@ var StepParser = class extends StepHeaderParser {
3920
3922
  if (!charws(SEMICOLON)) {
3921
3923
  return syntaxError();
3922
3924
  }
3923
- {
3924
- const recordLen = input.address - startElement;
3925
- onRecordIndexed?.(topLevelCount, expressID, foundItem, input.buffer.subarray(input.cursor - recordLen, input.cursor));
3926
- }
3927
- ++topLevelCount;
3928
3925
  pushEntry({
3929
3926
  address: startElement,
3930
3927
  length: input.address - startElement,
@@ -3932,6 +3929,11 @@ var StepParser = class extends StepHeaderParser {
3932
3929
  expressID,
3933
3930
  inlineEntities: inlineElements
3934
3931
  });
3932
+ {
3933
+ const recordLen = input.address - startElement;
3934
+ onRecordIndexed?.(topLevelCount, expressID, foundItem, input.buffer, input.cursor - recordLen, recordLen);
3935
+ }
3936
+ ++topLevelCount;
3935
3937
  }
3936
3938
  return parseResult2(ParseResult.INCOMPLETE);
3937
3939
  }
@@ -5810,9 +5812,17 @@ var StepModelBase = class _StepModelBase {
5810
5812
  * extract reads `IfcIndexedPolygonalFace.CoordIndex` as integers —
5811
5813
  * following those faces pinned ~78k records per PSB product. If
5812
5814
  * omitted, `!descend` children use this span path (test helper).
5815
+ * @param claimed Optional out-set of the IDs *this call* claimed and
5816
+ * awaited residency for — a strict subset of `seen`. When `seen` is
5817
+ * shared across concurrent walks (a batch of products sharing mapped
5818
+ * geometry), an ID another walk claimed is skipped here and is NOT
5819
+ * resident yet from this call's point of view: that walk's pin holds
5820
+ * the chunk once it lands, but its read is still in flight. Anything
5821
+ * that must synchronously read a record's bytes after this returns
5822
+ * has to iterate `claimed`, never `seen` (conway#526).
5813
5823
  * @return {Promise<Set<number>>} The local IDs that were visited.
5814
5824
  */
5815
- async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf) {
5825
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set(), descend, leafSpans, spanLeaf, claimed) {
5816
5826
  if (!this.isSourceExternal) {
5817
5827
  return seen;
5818
5828
  }
@@ -5837,6 +5847,7 @@ var StepModelBase = class _StepModelBase {
5837
5847
  seen.add(id);
5838
5848
  this.pinByLocalID(id);
5839
5849
  wave.push(id);
5850
+ claimed?.add(id);
5840
5851
  }
5841
5852
  frontier.length = 0;
5842
5853
  if (wave.length === 0) {
@@ -72152,13 +72163,11 @@ async function buildIndexStreamingAsync(source, parser211, pool3, onRecordIndexe
72152
72163
  };
72153
72164
  }
72154
72165
  }
72155
- function buildColumnarIndexStreaming(source, parser211, pool3, onRecordIndexed) {
72156
- const sink = new ColumnarIndexSink();
72166
+ function buildColumnarIndexStreaming(source, parser211, pool3, onRecordIndexed, sink = new ColumnarIndexSink()) {
72157
72167
  const { header, result, stats } = buildIndexStreaming(source, parser211, pool3, onRecordIndexed, sink);
72158
72168
  return { header, columns: sink.finalize(), result, stats };
72159
72169
  }
72160
- async function buildColumnarIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, onProgress, yieldIntervalMs) {
72161
- const sink = new ColumnarIndexSink();
72170
+ async function buildColumnarIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, onProgress, yieldIntervalMs, sink = new ColumnarIndexSink()) {
72162
72171
  const { header, result, stats } = await buildIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs);
72163
72172
  return { header, columns: sink.finalize(), result, stats };
72164
72173
  }