@bldrs-ai/conway 1.509.1435 → 1.511.1441

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 (73) hide show
  1. package/compiled/examples/browser-bundled.cjs +410 -16
  2. package/compiled/examples/cli-bundled.cjs +544 -18
  3. package/compiled/examples/cli-step-bundled.cjs +312 -16
  4. package/compiled/examples/validator-bundled.cjs +410 -16
  5. package/compiled/src/compat/web-ifc/ifc_api.d.ts +33 -0
  6. package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
  7. package/compiled/src/compat/web-ifc/ifc_api.js +47 -0
  8. package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.js +29 -0
  9. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts +8 -0
  10. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts.map +1 -1
  11. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts +14 -0
  12. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts.map +1 -1
  13. package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.js +33 -0
  14. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts +7 -0
  15. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts.map +1 -1
  16. package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.js +16 -0
  17. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +64 -0
  18. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
  19. package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +293 -30
  20. package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.js +29 -0
  21. package/compiled/src/ifc/ifc_demand_extraction.test.js +12 -1
  22. package/compiled/src/ifc/ifc_geometry_extraction.d.ts +35 -0
  23. package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
  24. package/compiled/src/ifc/ifc_geometry_extraction.js +142 -2
  25. package/compiled/src/ifc/ifc_step_parser.d.ts +23 -1
  26. package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
  27. package/compiled/src/ifc/ifc_step_parser.js +23 -1
  28. package/compiled/src/ifc/ifc_stream_open.d.ts +28 -1
  29. package/compiled/src/ifc/ifc_stream_open.d.ts.map +1 -1
  30. package/compiled/src/ifc/ifc_stream_open.js +44 -1
  31. package/compiled/src/ifc/ifc_stream_open.test.js +10 -1
  32. package/compiled/src/index.d.ts +3 -2
  33. package/compiled/src/index.d.ts.map +1 -1
  34. package/compiled/src/index.js +3 -2
  35. package/compiled/src/namespace_surface.test.js +3 -0
  36. package/compiled/src/step/parsing/byte_source.d.ts +98 -0
  37. package/compiled/src/step/parsing/byte_source.d.ts.map +1 -1
  38. package/compiled/src/step/parsing/byte_source.js +71 -0
  39. package/compiled/src/step/parsing/byte_source.test.d.ts +2 -0
  40. package/compiled/src/step/parsing/byte_source.test.d.ts.map +1 -0
  41. package/compiled/src/step/parsing/byte_source.test.js +47 -0
  42. package/compiled/src/step/parsing/byte_source_node.d.ts +41 -0
  43. package/compiled/src/step/parsing/byte_source_node.d.ts.map +1 -0
  44. package/compiled/src/step/parsing/byte_source_node.js +54 -0
  45. package/compiled/src/step/parsing/express_ref_scan.d.ts +20 -0
  46. package/compiled/src/step/parsing/express_ref_scan.d.ts.map +1 -0
  47. package/compiled/src/step/parsing/express_ref_scan.js +91 -0
  48. package/compiled/src/step/parsing/express_ref_scan.test.d.ts +2 -0
  49. package/compiled/src/step/parsing/express_ref_scan.test.d.ts.map +1 -0
  50. package/compiled/src/step/parsing/express_ref_scan.test.js +28 -0
  51. package/compiled/src/step/parsing/step_parser.d.ts +7 -4
  52. package/compiled/src/step/parsing/step_parser.d.ts.map +1 -1
  53. package/compiled/src/step/parsing/step_parser.js +25 -6
  54. package/compiled/src/step/parsing/streaming_index_builder.d.ts +3 -3
  55. package/compiled/src/step/parsing/streaming_index_builder.d.ts.map +1 -1
  56. package/compiled/src/step/parsing/streaming_index_builder.js +27 -7
  57. package/compiled/src/step/parsing/streaming_index_builder_async.test.js +13 -1
  58. package/compiled/src/step/step_buffer_provider.d.ts +44 -0
  59. package/compiled/src/step/step_buffer_provider.d.ts.map +1 -1
  60. package/compiled/src/step/step_buffer_provider.js +56 -8
  61. package/compiled/src/step/step_buffer_provider.test.js +14 -0
  62. package/compiled/src/step/step_entity_base.d.ts +15 -0
  63. package/compiled/src/step/step_entity_base.d.ts.map +1 -1
  64. package/compiled/src/step/step_entity_base.js +33 -0
  65. package/compiled/src/step/step_model_base.d.ts +62 -0
  66. package/compiled/src/step/step_model_base.d.ts.map +1 -1
  67. package/compiled/src/step/step_model_base.js +136 -0
  68. package/compiled/src/stream/index.d.ts +3 -2
  69. package/compiled/src/stream/index.d.ts.map +1 -1
  70. package/compiled/src/stream/index.js +3 -2
  71. package/compiled/src/version/version.js +1 -1
  72. package/compiled/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +1 -1
@@ -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.509.1435";
33
+ var versionString = "Conway v1.511.1441";
34
34
 
35
35
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
36
36
  var wasmType = "";
@@ -2609,7 +2609,9 @@ var StepParser = class extends StepHeaderParser {
2609
2609
  if (next.done === true) {
2610
2610
  return next.value;
2611
2611
  }
2612
- onProgress?.(next.value);
2612
+ if (typeof next.value === "number") {
2613
+ onProgress?.(next.value);
2614
+ }
2613
2615
  }
2614
2616
  }
2615
2617
  /**
@@ -2621,7 +2623,8 @@ var StepParser = class extends StepHeaderParser {
2621
2623
  *
2622
2624
  * @param input The input parsing buffer, positioned at the data section.
2623
2625
  * @param onRecordBoundary Called at each top-level record boundary with the
2624
- * buffer; the callback may rebase the buffer's window in place.
2626
+ * buffer; the callback may rebase the buffer's window in place. Must be
2627
+ * synchronous on this driver — a returned Promise is a caller bug.
2625
2628
  * @param onRecordIndexed Called as each top-level record is indexed, with
2626
2629
  * its localID, expressID and typeID (0 for external-mapping records) — the
2627
2630
  * seam for incremental semantic consumers (type index, roots registry,
@@ -2637,6 +2640,9 @@ var StepParser = class extends StepHeaderParser {
2637
2640
  if (next.done === true) {
2638
2641
  return next.value;
2639
2642
  }
2643
+ if (typeof next.value !== "number") {
2644
+ throw new Error("parseDataBlockStreamed: async window slide requires parseDataBlockStreamedAsync");
2645
+ }
2640
2646
  onProgress?.(next.value);
2641
2647
  }
2642
2648
  }
@@ -2649,7 +2655,9 @@ var StepParser = class extends StepHeaderParser {
2649
2655
  *
2650
2656
  * @param input The input parsing buffer, positioned at the data section.
2651
2657
  * @param onRecordBoundary Called at each top-level record boundary with the
2652
- * buffer; the callback may rebase the buffer's window in place.
2658
+ * buffer; the callback may rebase the buffer's window in place. May return
2659
+ * a Promise when the slide itself is I/O (an async ByteSource); this
2660
+ * driver awaits it before the next record is lexed.
2653
2661
  * @param onRecordIndexed Called as each top-level record is indexed — see
2654
2662
  * parseDataBlockStreamed.
2655
2663
  * @param onProgress Optional byte-cursor progress callback.
@@ -2666,6 +2674,10 @@ var StepParser = class extends StepHeaderParser {
2666
2674
  if (next.done === true) {
2667
2675
  return next.value;
2668
2676
  }
2677
+ if (typeof next.value !== "number") {
2678
+ await next.value;
2679
+ continue;
2680
+ }
2669
2681
  onProgress?.(next.value);
2670
2682
  if (Date.now() - lastYield >= yieldIntervalMs) {
2671
2683
  await yieldToEventLoop();
@@ -2693,7 +2705,9 @@ var StepParser = class extends StepHeaderParser {
2693
2705
  if (next.done === true) {
2694
2706
  return next.value;
2695
2707
  }
2696
- onProgress?.(next.value);
2708
+ if (typeof next.value === "number") {
2709
+ onProgress?.(next.value);
2710
+ }
2697
2711
  if (Date.now() - lastYield >= yieldIntervalMs) {
2698
2712
  await yieldToEventLoop();
2699
2713
  lastYield = Date.now();
@@ -2857,7 +2871,10 @@ var StepParser = class extends StepHeaderParser {
2857
2871
  if ((++parsedElementCount & PARSE_PROGRESS_ELEMENT_MASK) === 0) {
2858
2872
  yield input2.cursor;
2859
2873
  }
2860
- onRecordBoundary?.(input2);
2874
+ const slide = onRecordBoundary?.(input2);
2875
+ if (slide !== void 0 && typeof slide.then === "function") {
2876
+ yield slide;
2877
+ }
2861
2878
  if (!charws(HASH2)) {
2862
2879
  if (tokenws(END_SECTION)) {
2863
2880
  return parseResult2(ParseResult.COMPLETE);
@@ -4400,6 +4417,70 @@ var EntityTypesIfc;
4400
4417
  var EntityTypesIfcCount = 909;
4401
4418
  var entity_types_ifc_gen_default = EntityTypesIfc;
4402
4419
 
4420
+ // compiled/src/step/parsing/express_ref_scan.js
4421
+ var HASH3 = 35;
4422
+ var QUOTE4 = 39;
4423
+ var DQUOTE = 34;
4424
+ var ZERO5 = 48;
4425
+ var NINE2 = 57;
4426
+ var SLASH3 = 47;
4427
+ var STAR2 = 42;
4428
+ function scanExpressRefs(bytes, from = 0, length = bytes.length - from) {
4429
+ const refs = [];
4430
+ const seen = /* @__PURE__ */ new Set();
4431
+ const end = Math.min(from + length, bytes.length);
4432
+ let cursor = from;
4433
+ while (cursor < end) {
4434
+ const byte = bytes[cursor];
4435
+ if (byte === QUOTE4) {
4436
+ cursor = skipQuoted_(bytes, cursor + 1, end, QUOTE4, true);
4437
+ continue;
4438
+ }
4439
+ if (byte === DQUOTE) {
4440
+ cursor = skipQuoted_(bytes, cursor + 1, end, DQUOTE, false);
4441
+ continue;
4442
+ }
4443
+ if (byte === SLASH3 && cursor + 1 < end && bytes[cursor + 1] === STAR2) {
4444
+ cursor += 2;
4445
+ while (cursor + 1 < end && !(bytes[cursor] === STAR2 && bytes[cursor + 1] === SLASH3)) {
4446
+ ++cursor;
4447
+ }
4448
+ cursor += 2;
4449
+ continue;
4450
+ }
4451
+ if (byte === HASH3) {
4452
+ let value = 0;
4453
+ let digit = cursor + 1;
4454
+ while (digit < end && bytes[digit] >= ZERO5 && bytes[digit] <= NINE2) {
4455
+ value = value * 10 + (bytes[digit] - ZERO5);
4456
+ ++digit;
4457
+ }
4458
+ if (digit > cursor + 1 && value !== 0 && !seen.has(value)) {
4459
+ seen.add(value);
4460
+ refs.push(value);
4461
+ }
4462
+ cursor = digit;
4463
+ continue;
4464
+ }
4465
+ ++cursor;
4466
+ }
4467
+ return refs;
4468
+ }
4469
+ function skipQuoted_(bytes, from, end, quote, doubledEscape) {
4470
+ let cursor = from;
4471
+ while (cursor < end) {
4472
+ if (bytes[cursor] === quote) {
4473
+ if (doubledEscape && cursor + 1 < end && bytes[cursor + 1] === quote) {
4474
+ cursor += 2;
4475
+ continue;
4476
+ }
4477
+ return cursor + 1;
4478
+ }
4479
+ ++cursor;
4480
+ }
4481
+ return end;
4482
+ }
4483
+
4403
4484
  // compiled/src/step/step_buffer_provider.js
4404
4485
  var DEFAULT_CHUNK_BYTES = 4 * 1024 * 1024;
4405
4486
  var DEFAULT_MAX_RESIDENT_CHUNKS = 16;
@@ -4580,7 +4661,7 @@ var WindowedStepBufferProvider = class {
4580
4661
  const firstChunk = Math.floor(address / chunkBytes);
4581
4662
  const lastChunk = Math.floor((address + Math.max(length, 1) - 1) / chunkBytes);
4582
4663
  for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
4583
- this.ensurePins_.set(chunkIndex, (this.ensurePins_.get(chunkIndex) ?? 0) + 1);
4664
+ this.addPin_(chunkIndex, 1);
4584
4665
  }
4585
4666
  try {
4586
4667
  const loads = [];
@@ -4623,15 +4704,63 @@ var WindowedStepBufferProvider = class {
4623
4704
  }
4624
4705
  } finally {
4625
4706
  for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
4626
- const pins = this.ensurePins_.get(chunkIndex) ?? 0;
4627
- if (pins <= 1) {
4628
- this.ensurePins_.delete(chunkIndex);
4629
- } else {
4630
- this.ensurePins_.set(chunkIndex, pins - 1);
4631
- }
4707
+ this.addPin_(chunkIndex, -1);
4632
4708
  }
4633
4709
  }
4634
4710
  }
4711
+ /**
4712
+ * Hold a range against LRU eviction until {@link unpinRange}.
4713
+ *
4714
+ * @param address Absolute start of the range.
4715
+ * @param length Length of the range.
4716
+ */
4717
+ pinRange(address, length) {
4718
+ for (const chunkIndex of this.chunkSpan_(address, length)) {
4719
+ this.addPin_(chunkIndex, 1);
4720
+ }
4721
+ }
4722
+ /**
4723
+ * Drop one {@link pinRange} hold.
4724
+ *
4725
+ * @param address Absolute start of the range.
4726
+ * @param length Length of the range.
4727
+ */
4728
+ unpinRange(address, length) {
4729
+ for (const chunkIndex of this.chunkSpan_(address, length)) {
4730
+ this.addPin_(chunkIndex, -1);
4731
+ }
4732
+ }
4733
+ /**
4734
+ * Chunk indices covering `[address, address + length)`.
4735
+ *
4736
+ * @param address Absolute start.
4737
+ * @param length Length in bytes.
4738
+ * @return {number[]} Inclusive chunk indices.
4739
+ */
4740
+ chunkSpan_(address, length) {
4741
+ const chunkBytes = this.chunkBytes_;
4742
+ const firstChunk = Math.floor(address / chunkBytes);
4743
+ const lastChunk = Math.floor((address + Math.max(length, 1) - 1) / chunkBytes);
4744
+ const span = [];
4745
+ for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
4746
+ span.push(chunkIndex);
4747
+ }
4748
+ return span;
4749
+ }
4750
+ /**
4751
+ * Adjust a chunk's pin count, deleting the entry at zero.
4752
+ *
4753
+ * @param chunkIndex The chunk.
4754
+ * @param delta +1 pin or -1 unpin.
4755
+ */
4756
+ addPin_(chunkIndex, delta) {
4757
+ const pins = (this.ensurePins_.get(chunkIndex) ?? 0) + delta;
4758
+ if (pins <= 0) {
4759
+ this.ensurePins_.delete(chunkIndex);
4760
+ } else {
4761
+ this.ensurePins_.set(chunkIndex, pins);
4762
+ }
4763
+ }
4635
4764
  };
4636
4765
 
4637
4766
  // compiled/src/step/parsing/step_vtable_builder.js
@@ -4701,7 +4830,7 @@ var StepVtableBuilder = class {
4701
4830
  var OPEN_PAREN3 = parsing_constants_default.OPEN_PAREN;
4702
4831
  var CLOSE_PAREN3 = parsing_constants_default.CLOSE_PAREN;
4703
4832
  var COMMA3 = parsing_constants_default.COMMA;
4704
- var ZERO5 = parsing_constants_default.ZERO;
4833
+ var ZERO6 = parsing_constants_default.ZERO;
4705
4834
  var WHITESPACE4 = parsing_constants_default.WHITE_SPACE_SET;
4706
4835
  function extractIntegerArrayAt(buffer, cursor, endCursor, sink) {
4707
4836
  const marked = sink.length;
@@ -4770,7 +4899,7 @@ function extractUnsignedIntegerListAt(buffer, cursor, endCursor, sink) {
4770
4899
  return void 0;
4771
4900
  }
4772
4901
  }
4773
- const digit = buffer[cursor] - ZERO5;
4902
+ const digit = buffer[cursor] - ZERO6;
4774
4903
  if (digit < 0 || digit > 9) {
4775
4904
  sink.truncate(marked);
4776
4905
  return void 0;
@@ -4778,7 +4907,7 @@ function extractUnsignedIntegerListAt(buffer, cursor, endCursor, sink) {
4778
4907
  let value = digit;
4779
4908
  ++cursor;
4780
4909
  while (cursor < endCursor) {
4781
- const next = buffer[cursor] - ZERO5;
4910
+ const next = buffer[cursor] - ZERO6;
4782
4911
  if (next < 0 || next > 9) {
4783
4912
  break;
4784
4913
  }
@@ -5573,6 +5702,141 @@ var StepModelBase = class {
5573
5702
  }
5574
5703
  await this.ensureResidentByLocalID(localID);
5575
5704
  }
5705
+ /**
5706
+ * Page in the byte ranges a record *and every `#ref` reachable from
5707
+ * its text* will need, so a following synchronous extract of that
5708
+ * subgraph succeeds on a windowed source. Inverse relationships
5709
+ * (not written in the record) are not discovered — callers that
5710
+ * need them (rel-voids, styled items) pass their local IDs as
5711
+ * `extraLocalIDs`.
5712
+ *
5713
+ * No-op while the source is fully resident.
5714
+ *
5715
+ * @param localID Seed record.
5716
+ * @param extraLocalIDs Additional seeds (voids, styles, materials).
5717
+ * @param seen Optional set to extend (also skips already-pinned IDs).
5718
+ * @return {Promise<Set<number>>} The local IDs that were visited.
5719
+ */
5720
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set()) {
5721
+ if (!this.isSourceExternal) {
5722
+ return seen;
5723
+ }
5724
+ const stack = [localID];
5725
+ if (extraLocalIDs !== void 0) {
5726
+ for (const extra of extraLocalIDs) {
5727
+ stack.push(extra);
5728
+ }
5729
+ }
5730
+ while (stack.length > 0) {
5731
+ const id = stack.pop();
5732
+ if (seen.has(id) || id >= this.count_) {
5733
+ continue;
5734
+ }
5735
+ seen.add(id);
5736
+ await this.ensureResidentByLocalID(id);
5737
+ this.pinByLocalID(id);
5738
+ for (const expressID of this.scanRecordRefs_(id)) {
5739
+ const referenced = this.expressIDMap_.get(expressID);
5740
+ if (referenced !== void 0 && !seen.has(referenced)) {
5741
+ stack.push(referenced);
5742
+ }
5743
+ }
5744
+ }
5745
+ return seen;
5746
+ }
5747
+ /**
5748
+ * Hold a record's source range against windowed LRU eviction until
5749
+ * {@link unpinByLocalID}. No-op while the source is fully resident.
5750
+ *
5751
+ * @param localID The record to pin.
5752
+ */
5753
+ pinByLocalID(localID) {
5754
+ if (!this.isSourceExternal || localID >= this.count_) {
5755
+ return;
5756
+ }
5757
+ this.bufferProvider_.pinRange?.(this.address_[localID], this.length_[localID]);
5758
+ }
5759
+ /**
5760
+ * Drop one {@link pinByLocalID} hold.
5761
+ *
5762
+ * @param localID The record to unpin.
5763
+ */
5764
+ unpinByLocalID(localID) {
5765
+ if (!this.isSourceExternal || localID >= this.count_) {
5766
+ return;
5767
+ }
5768
+ this.bufferProvider_.unpinRange?.(this.address_[localID], this.length_[localID]);
5769
+ }
5770
+ /**
5771
+ * Unpin every local ID in `localIDs` (best-effort; missing IDs are
5772
+ * ignored). Use after {@link ensureResidentClosureByLocalID}, which
5773
+ * pins as it walks.
5774
+ *
5775
+ * @param localIDs The records to unpin.
5776
+ */
5777
+ unpinLocalIDs(localIDs) {
5778
+ for (const localID of localIDs) {
5779
+ this.unpinByLocalID(localID);
5780
+ }
5781
+ }
5782
+ /**
5783
+ * Express-ID twin of {@link ensureResidentClosureByLocalID}. Unknown
5784
+ * IDs resolve to an empty visit set.
5785
+ *
5786
+ * @param expressID Seed record.
5787
+ * @param extraLocalIDs Additional seeds (local IDs).
5788
+ * @return {Promise<Set<number>>} The local IDs that were visited.
5789
+ */
5790
+ async ensureResidentClosureByExpressID(expressID, extraLocalIDs, seen) {
5791
+ if (!this.isSourceExternal) {
5792
+ return seen ?? /* @__PURE__ */ new Set();
5793
+ }
5794
+ const localID = this.expressIDMap_.get(expressID);
5795
+ if (localID === void 0) {
5796
+ return seen ?? /* @__PURE__ */ new Set();
5797
+ }
5798
+ return this.ensureResidentClosureByLocalID(localID, extraLocalIDs, seen);
5799
+ }
5800
+ /**
5801
+ * Express IDs referenced by a resident record (`#<id>` tokens in its
5802
+ * text, including multi-mapped class records). The record itself
5803
+ * must already be resident.
5804
+ *
5805
+ * @param localID The record to scan.
5806
+ * @return {number[]} Distinct referenced express IDs.
5807
+ */
5808
+ referencedExpressIDs(localID) {
5809
+ return this.scanRecordRefs_(localID);
5810
+ }
5811
+ /**
5812
+ * Scan a resident record's bytes for `#<expressID>` references.
5813
+ *
5814
+ * @param localID The record to scan.
5815
+ * @return {number[]} Distinct referenced express IDs.
5816
+ */
5817
+ scanRecordRefs_(localID) {
5818
+ const address = this.address_[localID];
5819
+ const length = this.length_[localID];
5820
+ const acquisition = this.bufferProvider_.acquire(address, length);
5821
+ const viewStart = address - acquisition.offset;
5822
+ const refs = scanExpressRefs(acquisition.buffer, viewStart, length);
5823
+ const complex = this.complexEntries_?.get(localID);
5824
+ if (complex?.multiMapping === void 0) {
5825
+ return refs;
5826
+ }
5827
+ const seen = new Set(refs);
5828
+ for (const mapped of complex.multiMapping) {
5829
+ const mappedAcquisition = this.bufferProvider_.acquire(mapped.address, mapped.length);
5830
+ const mappedStart = mapped.address - mappedAcquisition.offset;
5831
+ for (const expressID of scanExpressRefs(mappedAcquisition.buffer, mappedStart, mapped.length)) {
5832
+ if (!seen.has(expressID)) {
5833
+ seen.add(expressID);
5834
+ refs.push(expressID);
5835
+ }
5836
+ }
5837
+ }
5838
+ return refs;
5839
+ }
5576
5840
  /**
5577
5841
  * Get the number of elements/entities in this model.
5578
5842
  *
@@ -10729,6 +10993,38 @@ var StepEntityBase = class {
10729
10993
  }
10730
10994
  return value;
10731
10995
  }
10996
+ /**
10997
+ * Resolve a single reference field to its local ID without
10998
+ * constructing the target entity. Prep maps that only store
10999
+ * `Item.localID` (IfcStyledItem → faceset) must not hydrate the
11000
+ * target: on a windowed source that would pin the faceset record
11001
+ * (often megabytes) for every styled item at once.
11002
+ *
11003
+ * @param offset The offset in the vtable to extract from.
11004
+ * @param baseOffset The base offset of the class in the vtable.
11005
+ * @param depth The depth in the inheritance hierarchy.
11006
+ * @param optional Is this an optional field?
11007
+ * @return {number | null} The referenced local ID, or null if the
11008
+ * field is unset/optional-null.
11009
+ */
11010
+ extractReferenceLocalID(offset, baseOffset, depth, optional) {
11011
+ const [cursor, endCursor, buffer] = this.getOffsetAndEndCursor(offset, baseOffset, depth);
11012
+ const expressID = stepExtractReference(buffer, cursor, endCursor);
11013
+ if (expressID !== void 0) {
11014
+ return this.model.resolveExpressID(expressID) ?? null;
11015
+ }
11016
+ const inline = this.model.getInlineElementByAddress(extractInlineElementAddress(buffer, cursor, endCursor));
11017
+ if (inline !== void 0) {
11018
+ return inline.localID;
11019
+ }
11020
+ if (!optional) {
11021
+ throw new Error("Value in STEP was incorrectly typed");
11022
+ }
11023
+ if (!this.model.nullOnErrors && stepExtractOptional(buffer, cursor, endCursor) !== null) {
11024
+ throw new Error("Value in STEP was incorrectly typed");
11025
+ }
11026
+ return null;
11027
+ }
10732
11028
  /**
10733
11029
  *
10734
11030
  * @param buffer
@@ -71438,11 +71734,88 @@ function buildIndexStreaming(source, parser211, pool3, onRecordIndexed, sink) {
71438
71734
  }
71439
71735
  }
71440
71736
  var MIN_WINDOW = 4 * 1024;
71737
+ async function readSource(source, offset, length, into, intoOffset) {
71738
+ return await source.read(offset, length, into, intoOffset);
71739
+ }
71740
+ async function buildIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs) {
71741
+ const fileSize = source.byteLength;
71742
+ let windowBytes = Math.max(pool3, MIN_WINDOW);
71743
+ for (; ; ) {
71744
+ const window2 = new Uint8Array(windowBytes);
71745
+ let windowStartFile = 0;
71746
+ let windowLen = await readSource(source, 0, windowBytes, window2, 0);
71747
+ let bytesRead = windowLen;
71748
+ const input2 = new ParsingBuffer(window2, 0, windowLen);
71749
+ const [header, headerResult] = parser211.parseHeader(input2);
71750
+ if (headerResult !== ParseResult.COMPLETE) {
71751
+ return {
71752
+ header,
71753
+ elements: [],
71754
+ result: headerResult,
71755
+ stats: { pool: pool3, windowBytes, slides: 0, maxRecordLen: 0, bytesRead }
71756
+ };
71757
+ }
71758
+ const slideThreshold = windowBytes >> 1;
71759
+ let slides = 0;
71760
+ let maxRecordLen = 0;
71761
+ let prevBoundaryFile = windowStartFile + input2.cursor;
71762
+ const onRecordBoundary = (buffer) => {
71763
+ const cursor = buffer.cursor;
71764
+ const recordFileStart = windowStartFile + cursor;
71765
+ const recordLen = recordFileStart - prevBoundaryFile;
71766
+ if (recordLen > maxRecordLen) {
71767
+ maxRecordLen = recordLen;
71768
+ }
71769
+ prevBoundaryFile = recordFileStart;
71770
+ if (windowStartFile + windowLen >= fileSize) {
71771
+ return;
71772
+ }
71773
+ if (cursor < slideThreshold) {
71774
+ return;
71775
+ }
71776
+ const tail = windowLen - cursor;
71777
+ window2.copyWithin(0, cursor, windowLen);
71778
+ const want = windowBytes - tail;
71779
+ const fillAt = windowStartFile + windowLen;
71780
+ return readSource(source, fillAt, want, window2, tail).then((got) => {
71781
+ bytesRead += got;
71782
+ windowLen = tail + got;
71783
+ windowStartFile = recordFileStart;
71784
+ buffer.rebaseWindow(window2, 0, windowLen, windowStartFile);
71785
+ ++slides;
71786
+ });
71787
+ };
71788
+ const progressTick = onProgress !== void 0 ? (cursor) => onProgress(windowStartFile + cursor) : void 0;
71789
+ const [index, result] = await parser211.parseDataBlockStreamedAsync(input2, onRecordBoundary, onRecordIndexed, progressTick, sink, yieldIntervalMs);
71790
+ const stoppedShort = result !== ParseResult.COMPLETE;
71791
+ const notAtEof = windowStartFile + windowLen < fileSize;
71792
+ if (stoppedShort && notAtEof) {
71793
+ windowBytes *= 2;
71794
+ sink?.reset();
71795
+ continue;
71796
+ }
71797
+ const lastRecordLen = windowStartFile + input2.cursor - prevBoundaryFile;
71798
+ if (lastRecordLen > maxRecordLen) {
71799
+ maxRecordLen = lastRecordLen;
71800
+ }
71801
+ return {
71802
+ header,
71803
+ elements: index.elements,
71804
+ result,
71805
+ stats: { pool: pool3, windowBytes, slides, maxRecordLen, bytesRead }
71806
+ };
71807
+ }
71808
+ }
71441
71809
  function buildColumnarIndexStreaming(source, parser211, pool3, onRecordIndexed) {
71442
71810
  const sink = new ColumnarIndexSink();
71443
71811
  const { header, result, stats } = buildIndexStreaming(source, parser211, pool3, onRecordIndexed, sink);
71444
71812
  return { header, columns: sink.finalize(), result, stats };
71445
71813
  }
71814
+ async function buildColumnarIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, onProgress, yieldIntervalMs) {
71815
+ const sink = new ColumnarIndexSink();
71816
+ const { header, result, stats } = await buildIndexStreamingAsync(source, parser211, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs);
71817
+ return { header, columns: sink.finalize(), result, stats };
71818
+ }
71446
71819
 
71447
71820
  // compiled/src/ifc/ifc_step_parser.js
71448
71821
  var DEFAULT_STREAM_POOL_BYTES = 1024 * 1024;
@@ -71512,6 +71885,27 @@ var IfcStepParser = class extends StepParser {
71512
71885
  const provider = new WindowedStepBufferProvider(store, opts?.chunkBytes, opts?.maxResidentChunks);
71513
71886
  return [result, new IfcStepModel(void 0, columns, provider)];
71514
71887
  }
71888
+ /**
71889
+ * Cooperative twin of {@link parseStreamToModel}: the index build
71890
+ * yields to the event loop and can fill windows from an async
71891
+ * {@link ReadableByteSource} (an OPFS `File.slice()` store). The
71892
+ * model is still windowed — geometry extract must
71893
+ * `ensureResident` first.
71894
+ *
71895
+ * @param source Sync or async byte source feeding the parse.
71896
+ * @param store Async external store backing the windowed model.
71897
+ * @param opts Optional window sizing plus parse progress.
71898
+ * @return {Promise<[ParseResult, IfcStepModel | undefined]>} The
71899
+ * parse result and the windowed model.
71900
+ */
71901
+ async parseStreamToModelAsync(source, store, opts) {
71902
+ if (store.byteLength !== source.byteLength) {
71903
+ throw new Error(`Streaming store byteLength ${store.byteLength} does not match source byteLength ${source.byteLength}`);
71904
+ }
71905
+ const { columns, result } = await buildColumnarIndexStreamingAsync(source, this, opts?.pool ?? DEFAULT_STREAM_POOL_BYTES, void 0, opts?.onProgress);
71906
+ const provider = new WindowedStepBufferProvider(store, opts?.chunkBytes, opts?.maxResidentChunks);
71907
+ return [result, new IfcStepModel(void 0, columns, provider)];
71908
+ }
71515
71909
  };
71516
71910
  IfcStepParser.Instance = new IfcStepParser();
71517
71911
  var ifc_step_parser_default = IfcStepParser;