@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
@@ -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.509.1435";
14968
+ var versionString = "Conway v1.511.1441";
14969
14969
 
14970
14970
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
14971
14971
  function pThreadsAllowed() {
@@ -18440,7 +18440,9 @@ var StepParser = class extends StepHeaderParser {
18440
18440
  if (next.done === true) {
18441
18441
  return next.value;
18442
18442
  }
18443
- onProgress?.(next.value);
18443
+ if (typeof next.value === "number") {
18444
+ onProgress?.(next.value);
18445
+ }
18444
18446
  }
18445
18447
  }
18446
18448
  /**
@@ -18452,7 +18454,8 @@ var StepParser = class extends StepHeaderParser {
18452
18454
  *
18453
18455
  * @param input The input parsing buffer, positioned at the data section.
18454
18456
  * @param onRecordBoundary Called at each top-level record boundary with the
18455
- * buffer; the callback may rebase the buffer's window in place.
18457
+ * buffer; the callback may rebase the buffer's window in place. Must be
18458
+ * synchronous on this driver — a returned Promise is a caller bug.
18456
18459
  * @param onRecordIndexed Called as each top-level record is indexed, with
18457
18460
  * its localID, expressID and typeID (0 for external-mapping records) — the
18458
18461
  * seam for incremental semantic consumers (type index, roots registry,
@@ -18468,6 +18471,9 @@ var StepParser = class extends StepHeaderParser {
18468
18471
  if (next.done === true) {
18469
18472
  return next.value;
18470
18473
  }
18474
+ if (typeof next.value !== "number") {
18475
+ throw new Error("parseDataBlockStreamed: async window slide requires parseDataBlockStreamedAsync");
18476
+ }
18471
18477
  onProgress?.(next.value);
18472
18478
  }
18473
18479
  }
@@ -18480,7 +18486,9 @@ var StepParser = class extends StepHeaderParser {
18480
18486
  *
18481
18487
  * @param input The input parsing buffer, positioned at the data section.
18482
18488
  * @param onRecordBoundary Called at each top-level record boundary with the
18483
- * buffer; the callback may rebase the buffer's window in place.
18489
+ * buffer; the callback may rebase the buffer's window in place. May return
18490
+ * a Promise when the slide itself is I/O (an async ByteSource); this
18491
+ * driver awaits it before the next record is lexed.
18484
18492
  * @param onRecordIndexed Called as each top-level record is indexed — see
18485
18493
  * parseDataBlockStreamed.
18486
18494
  * @param onProgress Optional byte-cursor progress callback.
@@ -18497,6 +18505,10 @@ var StepParser = class extends StepHeaderParser {
18497
18505
  if (next.done === true) {
18498
18506
  return next.value;
18499
18507
  }
18508
+ if (typeof next.value !== "number") {
18509
+ await next.value;
18510
+ continue;
18511
+ }
18500
18512
  onProgress?.(next.value);
18501
18513
  if (Date.now() - lastYield >= yieldIntervalMs) {
18502
18514
  await yieldToEventLoop();
@@ -18524,7 +18536,9 @@ var StepParser = class extends StepHeaderParser {
18524
18536
  if (next.done === true) {
18525
18537
  return next.value;
18526
18538
  }
18527
- onProgress?.(next.value);
18539
+ if (typeof next.value === "number") {
18540
+ onProgress?.(next.value);
18541
+ }
18528
18542
  if (Date.now() - lastYield >= yieldIntervalMs) {
18529
18543
  await yieldToEventLoop();
18530
18544
  lastYield = Date.now();
@@ -18688,7 +18702,10 @@ var StepParser = class extends StepHeaderParser {
18688
18702
  if ((++parsedElementCount & PARSE_PROGRESS_ELEMENT_MASK) === 0) {
18689
18703
  yield input.cursor;
18690
18704
  }
18691
- onRecordBoundary?.(input);
18705
+ const slide = onRecordBoundary?.(input);
18706
+ if (slide !== void 0 && typeof slide.then === "function") {
18707
+ yield slide;
18708
+ }
18692
18709
  if (!charws(HASH2)) {
18693
18710
  if (tokenws(END_SECTION)) {
18694
18711
  return parseResult(ParseResult.COMPLETE);
@@ -20231,6 +20248,70 @@ var EntityTypesIfc;
20231
20248
  var EntityTypesIfcCount = 909;
20232
20249
  var entity_types_ifc_gen_default = EntityTypesIfc;
20233
20250
 
20251
+ // compiled/src/step/parsing/express_ref_scan.js
20252
+ var HASH3 = 35;
20253
+ var QUOTE4 = 39;
20254
+ var DQUOTE = 34;
20255
+ var ZERO5 = 48;
20256
+ var NINE2 = 57;
20257
+ var SLASH3 = 47;
20258
+ var STAR2 = 42;
20259
+ function scanExpressRefs(bytes, from = 0, length = bytes.length - from) {
20260
+ const refs = [];
20261
+ const seen = /* @__PURE__ */ new Set();
20262
+ const end = Math.min(from + length, bytes.length);
20263
+ let cursor = from;
20264
+ while (cursor < end) {
20265
+ const byte = bytes[cursor];
20266
+ if (byte === QUOTE4) {
20267
+ cursor = skipQuoted_(bytes, cursor + 1, end, QUOTE4, true);
20268
+ continue;
20269
+ }
20270
+ if (byte === DQUOTE) {
20271
+ cursor = skipQuoted_(bytes, cursor + 1, end, DQUOTE, false);
20272
+ continue;
20273
+ }
20274
+ if (byte === SLASH3 && cursor + 1 < end && bytes[cursor + 1] === STAR2) {
20275
+ cursor += 2;
20276
+ while (cursor + 1 < end && !(bytes[cursor] === STAR2 && bytes[cursor + 1] === SLASH3)) {
20277
+ ++cursor;
20278
+ }
20279
+ cursor += 2;
20280
+ continue;
20281
+ }
20282
+ if (byte === HASH3) {
20283
+ let value = 0;
20284
+ let digit = cursor + 1;
20285
+ while (digit < end && bytes[digit] >= ZERO5 && bytes[digit] <= NINE2) {
20286
+ value = value * 10 + (bytes[digit] - ZERO5);
20287
+ ++digit;
20288
+ }
20289
+ if (digit > cursor + 1 && value !== 0 && !seen.has(value)) {
20290
+ seen.add(value);
20291
+ refs.push(value);
20292
+ }
20293
+ cursor = digit;
20294
+ continue;
20295
+ }
20296
+ ++cursor;
20297
+ }
20298
+ return refs;
20299
+ }
20300
+ function skipQuoted_(bytes, from, end, quote, doubledEscape) {
20301
+ let cursor = from;
20302
+ while (cursor < end) {
20303
+ if (bytes[cursor] === quote) {
20304
+ if (doubledEscape && cursor + 1 < end && bytes[cursor + 1] === quote) {
20305
+ cursor += 2;
20306
+ continue;
20307
+ }
20308
+ return cursor + 1;
20309
+ }
20310
+ ++cursor;
20311
+ }
20312
+ return end;
20313
+ }
20314
+
20234
20315
  // compiled/src/step/step_buffer_provider.js
20235
20316
  var DEFAULT_CHUNK_BYTES = 4 * 1024 * 1024;
20236
20317
  var DEFAULT_MAX_RESIDENT_CHUNKS = 16;
@@ -20411,7 +20492,7 @@ var WindowedStepBufferProvider = class {
20411
20492
  const firstChunk = Math.floor(address / chunkBytes);
20412
20493
  const lastChunk = Math.floor((address + Math.max(length, 1) - 1) / chunkBytes);
20413
20494
  for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
20414
- this.ensurePins_.set(chunkIndex, (this.ensurePins_.get(chunkIndex) ?? 0) + 1);
20495
+ this.addPin_(chunkIndex, 1);
20415
20496
  }
20416
20497
  try {
20417
20498
  const loads = [];
@@ -20454,15 +20535,63 @@ var WindowedStepBufferProvider = class {
20454
20535
  }
20455
20536
  } finally {
20456
20537
  for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
20457
- const pins = this.ensurePins_.get(chunkIndex) ?? 0;
20458
- if (pins <= 1) {
20459
- this.ensurePins_.delete(chunkIndex);
20460
- } else {
20461
- this.ensurePins_.set(chunkIndex, pins - 1);
20462
- }
20538
+ this.addPin_(chunkIndex, -1);
20463
20539
  }
20464
20540
  }
20465
20541
  }
20542
+ /**
20543
+ * Hold a range against LRU eviction until {@link unpinRange}.
20544
+ *
20545
+ * @param address Absolute start of the range.
20546
+ * @param length Length of the range.
20547
+ */
20548
+ pinRange(address, length) {
20549
+ for (const chunkIndex of this.chunkSpan_(address, length)) {
20550
+ this.addPin_(chunkIndex, 1);
20551
+ }
20552
+ }
20553
+ /**
20554
+ * Drop one {@link pinRange} hold.
20555
+ *
20556
+ * @param address Absolute start of the range.
20557
+ * @param length Length of the range.
20558
+ */
20559
+ unpinRange(address, length) {
20560
+ for (const chunkIndex of this.chunkSpan_(address, length)) {
20561
+ this.addPin_(chunkIndex, -1);
20562
+ }
20563
+ }
20564
+ /**
20565
+ * Chunk indices covering `[address, address + length)`.
20566
+ *
20567
+ * @param address Absolute start.
20568
+ * @param length Length in bytes.
20569
+ * @return {number[]} Inclusive chunk indices.
20570
+ */
20571
+ chunkSpan_(address, length) {
20572
+ const chunkBytes = this.chunkBytes_;
20573
+ const firstChunk = Math.floor(address / chunkBytes);
20574
+ const lastChunk = Math.floor((address + Math.max(length, 1) - 1) / chunkBytes);
20575
+ const span = [];
20576
+ for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
20577
+ span.push(chunkIndex);
20578
+ }
20579
+ return span;
20580
+ }
20581
+ /**
20582
+ * Adjust a chunk's pin count, deleting the entry at zero.
20583
+ *
20584
+ * @param chunkIndex The chunk.
20585
+ * @param delta +1 pin or -1 unpin.
20586
+ */
20587
+ addPin_(chunkIndex, delta) {
20588
+ const pins = (this.ensurePins_.get(chunkIndex) ?? 0) + delta;
20589
+ if (pins <= 0) {
20590
+ this.ensurePins_.delete(chunkIndex);
20591
+ } else {
20592
+ this.ensurePins_.set(chunkIndex, pins);
20593
+ }
20594
+ }
20466
20595
  };
20467
20596
 
20468
20597
  // compiled/src/step/parsing/step_vtable_builder.js
@@ -20532,7 +20661,7 @@ var StepVtableBuilder = class {
20532
20661
  var OPEN_PAREN3 = parsing_constants_default.OPEN_PAREN;
20533
20662
  var CLOSE_PAREN3 = parsing_constants_default.CLOSE_PAREN;
20534
20663
  var COMMA3 = parsing_constants_default.COMMA;
20535
- var ZERO5 = parsing_constants_default.ZERO;
20664
+ var ZERO6 = parsing_constants_default.ZERO;
20536
20665
  var WHITESPACE4 = parsing_constants_default.WHITE_SPACE_SET;
20537
20666
  var Uint32Sink = class {
20538
20667
  /**
@@ -20648,7 +20777,7 @@ function extractUnsignedIntegerListAt(buffer, cursor, endCursor, sink) {
20648
20777
  return void 0;
20649
20778
  }
20650
20779
  }
20651
- const digit = buffer[cursor] - ZERO5;
20780
+ const digit = buffer[cursor] - ZERO6;
20652
20781
  if (digit < 0 || digit > 9) {
20653
20782
  sink.truncate(marked);
20654
20783
  return void 0;
@@ -20656,7 +20785,7 @@ function extractUnsignedIntegerListAt(buffer, cursor, endCursor, sink) {
20656
20785
  let value = digit;
20657
20786
  ++cursor;
20658
20787
  while (cursor < endCursor) {
20659
- const next = buffer[cursor] - ZERO5;
20788
+ const next = buffer[cursor] - ZERO6;
20660
20789
  if (next < 0 || next > 9) {
20661
20790
  break;
20662
20791
  }
@@ -21451,6 +21580,141 @@ var StepModelBase = class {
21451
21580
  }
21452
21581
  await this.ensureResidentByLocalID(localID);
21453
21582
  }
21583
+ /**
21584
+ * Page in the byte ranges a record *and every `#ref` reachable from
21585
+ * its text* will need, so a following synchronous extract of that
21586
+ * subgraph succeeds on a windowed source. Inverse relationships
21587
+ * (not written in the record) are not discovered — callers that
21588
+ * need them (rel-voids, styled items) pass their local IDs as
21589
+ * `extraLocalIDs`.
21590
+ *
21591
+ * No-op while the source is fully resident.
21592
+ *
21593
+ * @param localID Seed record.
21594
+ * @param extraLocalIDs Additional seeds (voids, styles, materials).
21595
+ * @param seen Optional set to extend (also skips already-pinned IDs).
21596
+ * @return {Promise<Set<number>>} The local IDs that were visited.
21597
+ */
21598
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set()) {
21599
+ if (!this.isSourceExternal) {
21600
+ return seen;
21601
+ }
21602
+ const stack = [localID];
21603
+ if (extraLocalIDs !== void 0) {
21604
+ for (const extra of extraLocalIDs) {
21605
+ stack.push(extra);
21606
+ }
21607
+ }
21608
+ while (stack.length > 0) {
21609
+ const id = stack.pop();
21610
+ if (seen.has(id) || id >= this.count_) {
21611
+ continue;
21612
+ }
21613
+ seen.add(id);
21614
+ await this.ensureResidentByLocalID(id);
21615
+ this.pinByLocalID(id);
21616
+ for (const expressID of this.scanRecordRefs_(id)) {
21617
+ const referenced = this.expressIDMap_.get(expressID);
21618
+ if (referenced !== void 0 && !seen.has(referenced)) {
21619
+ stack.push(referenced);
21620
+ }
21621
+ }
21622
+ }
21623
+ return seen;
21624
+ }
21625
+ /**
21626
+ * Hold a record's source range against windowed LRU eviction until
21627
+ * {@link unpinByLocalID}. No-op while the source is fully resident.
21628
+ *
21629
+ * @param localID The record to pin.
21630
+ */
21631
+ pinByLocalID(localID) {
21632
+ if (!this.isSourceExternal || localID >= this.count_) {
21633
+ return;
21634
+ }
21635
+ this.bufferProvider_.pinRange?.(this.address_[localID], this.length_[localID]);
21636
+ }
21637
+ /**
21638
+ * Drop one {@link pinByLocalID} hold.
21639
+ *
21640
+ * @param localID The record to unpin.
21641
+ */
21642
+ unpinByLocalID(localID) {
21643
+ if (!this.isSourceExternal || localID >= this.count_) {
21644
+ return;
21645
+ }
21646
+ this.bufferProvider_.unpinRange?.(this.address_[localID], this.length_[localID]);
21647
+ }
21648
+ /**
21649
+ * Unpin every local ID in `localIDs` (best-effort; missing IDs are
21650
+ * ignored). Use after {@link ensureResidentClosureByLocalID}, which
21651
+ * pins as it walks.
21652
+ *
21653
+ * @param localIDs The records to unpin.
21654
+ */
21655
+ unpinLocalIDs(localIDs) {
21656
+ for (const localID of localIDs) {
21657
+ this.unpinByLocalID(localID);
21658
+ }
21659
+ }
21660
+ /**
21661
+ * Express-ID twin of {@link ensureResidentClosureByLocalID}. Unknown
21662
+ * IDs resolve to an empty visit set.
21663
+ *
21664
+ * @param expressID Seed record.
21665
+ * @param extraLocalIDs Additional seeds (local IDs).
21666
+ * @return {Promise<Set<number>>} The local IDs that were visited.
21667
+ */
21668
+ async ensureResidentClosureByExpressID(expressID, extraLocalIDs, seen) {
21669
+ if (!this.isSourceExternal) {
21670
+ return seen ?? /* @__PURE__ */ new Set();
21671
+ }
21672
+ const localID = this.expressIDMap_.get(expressID);
21673
+ if (localID === void 0) {
21674
+ return seen ?? /* @__PURE__ */ new Set();
21675
+ }
21676
+ return this.ensureResidentClosureByLocalID(localID, extraLocalIDs, seen);
21677
+ }
21678
+ /**
21679
+ * Express IDs referenced by a resident record (`#<id>` tokens in its
21680
+ * text, including multi-mapped class records). The record itself
21681
+ * must already be resident.
21682
+ *
21683
+ * @param localID The record to scan.
21684
+ * @return {number[]} Distinct referenced express IDs.
21685
+ */
21686
+ referencedExpressIDs(localID) {
21687
+ return this.scanRecordRefs_(localID);
21688
+ }
21689
+ /**
21690
+ * Scan a resident record's bytes for `#<expressID>` references.
21691
+ *
21692
+ * @param localID The record to scan.
21693
+ * @return {number[]} Distinct referenced express IDs.
21694
+ */
21695
+ scanRecordRefs_(localID) {
21696
+ const address = this.address_[localID];
21697
+ const length = this.length_[localID];
21698
+ const acquisition = this.bufferProvider_.acquire(address, length);
21699
+ const viewStart = address - acquisition.offset;
21700
+ const refs = scanExpressRefs(acquisition.buffer, viewStart, length);
21701
+ const complex = this.complexEntries_?.get(localID);
21702
+ if (complex?.multiMapping === void 0) {
21703
+ return refs;
21704
+ }
21705
+ const seen = new Set(refs);
21706
+ for (const mapped of complex.multiMapping) {
21707
+ const mappedAcquisition = this.bufferProvider_.acquire(mapped.address, mapped.length);
21708
+ const mappedStart = mapped.address - mappedAcquisition.offset;
21709
+ for (const expressID of scanExpressRefs(mappedAcquisition.buffer, mappedStart, mapped.length)) {
21710
+ if (!seen.has(expressID)) {
21711
+ seen.add(expressID);
21712
+ refs.push(expressID);
21713
+ }
21714
+ }
21715
+ }
21716
+ return refs;
21717
+ }
21454
21718
  /**
21455
21719
  * Get the number of elements/entities in this model.
21456
21720
  *
@@ -26607,6 +26871,38 @@ var StepEntityBase = class {
26607
26871
  }
26608
26872
  return value;
26609
26873
  }
26874
+ /**
26875
+ * Resolve a single reference field to its local ID without
26876
+ * constructing the target entity. Prep maps that only store
26877
+ * `Item.localID` (IfcStyledItem → faceset) must not hydrate the
26878
+ * target: on a windowed source that would pin the faceset record
26879
+ * (often megabytes) for every styled item at once.
26880
+ *
26881
+ * @param offset The offset in the vtable to extract from.
26882
+ * @param baseOffset The base offset of the class in the vtable.
26883
+ * @param depth The depth in the inheritance hierarchy.
26884
+ * @param optional Is this an optional field?
26885
+ * @return {number | null} The referenced local ID, or null if the
26886
+ * field is unset/optional-null.
26887
+ */
26888
+ extractReferenceLocalID(offset, baseOffset, depth, optional) {
26889
+ const [cursor, endCursor, buffer] = this.getOffsetAndEndCursor(offset, baseOffset, depth);
26890
+ const expressID = stepExtractReference(buffer, cursor, endCursor);
26891
+ if (expressID !== void 0) {
26892
+ return this.model.resolveExpressID(expressID) ?? null;
26893
+ }
26894
+ const inline = this.model.getInlineElementByAddress(extractInlineElementAddress(buffer, cursor, endCursor));
26895
+ if (inline !== void 0) {
26896
+ return inline.localID;
26897
+ }
26898
+ if (!optional) {
26899
+ throw new Error("Value in STEP was incorrectly typed");
26900
+ }
26901
+ if (!this.model.nullOnErrors && stepExtractOptional(buffer, cursor, endCursor) !== null) {
26902
+ throw new Error("Value in STEP was incorrectly typed");
26903
+ }
26904
+ return null;
26905
+ }
26610
26906
  /**
26611
26907
  *
26612
26908
  * @param buffer
@@ -87347,11 +87643,88 @@ function buildIndexStreaming(source, parser210, pool3, onRecordIndexed, sink) {
87347
87643
  }
87348
87644
  }
87349
87645
  var MIN_WINDOW = 4 * 1024;
87646
+ async function readSource(source, offset, length, into, intoOffset) {
87647
+ return await source.read(offset, length, into, intoOffset);
87648
+ }
87649
+ async function buildIndexStreamingAsync(source, parser210, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs) {
87650
+ const fileSize = source.byteLength;
87651
+ let windowBytes = Math.max(pool3, MIN_WINDOW);
87652
+ for (; ; ) {
87653
+ const window2 = new Uint8Array(windowBytes);
87654
+ let windowStartFile = 0;
87655
+ let windowLen = await readSource(source, 0, windowBytes, window2, 0);
87656
+ let bytesRead = windowLen;
87657
+ const input = new ParsingBuffer(window2, 0, windowLen);
87658
+ const [header, headerResult] = parser210.parseHeader(input);
87659
+ if (headerResult !== ParseResult.COMPLETE) {
87660
+ return {
87661
+ header,
87662
+ elements: [],
87663
+ result: headerResult,
87664
+ stats: { pool: pool3, windowBytes, slides: 0, maxRecordLen: 0, bytesRead }
87665
+ };
87666
+ }
87667
+ const slideThreshold = windowBytes >> 1;
87668
+ let slides = 0;
87669
+ let maxRecordLen = 0;
87670
+ let prevBoundaryFile = windowStartFile + input.cursor;
87671
+ const onRecordBoundary = (buffer) => {
87672
+ const cursor = buffer.cursor;
87673
+ const recordFileStart = windowStartFile + cursor;
87674
+ const recordLen = recordFileStart - prevBoundaryFile;
87675
+ if (recordLen > maxRecordLen) {
87676
+ maxRecordLen = recordLen;
87677
+ }
87678
+ prevBoundaryFile = recordFileStart;
87679
+ if (windowStartFile + windowLen >= fileSize) {
87680
+ return;
87681
+ }
87682
+ if (cursor < slideThreshold) {
87683
+ return;
87684
+ }
87685
+ const tail = windowLen - cursor;
87686
+ window2.copyWithin(0, cursor, windowLen);
87687
+ const want = windowBytes - tail;
87688
+ const fillAt = windowStartFile + windowLen;
87689
+ return readSource(source, fillAt, want, window2, tail).then((got) => {
87690
+ bytesRead += got;
87691
+ windowLen = tail + got;
87692
+ windowStartFile = recordFileStart;
87693
+ buffer.rebaseWindow(window2, 0, windowLen, windowStartFile);
87694
+ ++slides;
87695
+ });
87696
+ };
87697
+ const progressTick = onProgress !== void 0 ? (cursor) => onProgress(windowStartFile + cursor) : void 0;
87698
+ const [index, result] = await parser210.parseDataBlockStreamedAsync(input, onRecordBoundary, onRecordIndexed, progressTick, sink, yieldIntervalMs);
87699
+ const stoppedShort = result !== ParseResult.COMPLETE;
87700
+ const notAtEof = windowStartFile + windowLen < fileSize;
87701
+ if (stoppedShort && notAtEof) {
87702
+ windowBytes *= 2;
87703
+ sink?.reset();
87704
+ continue;
87705
+ }
87706
+ const lastRecordLen = windowStartFile + input.cursor - prevBoundaryFile;
87707
+ if (lastRecordLen > maxRecordLen) {
87708
+ maxRecordLen = lastRecordLen;
87709
+ }
87710
+ return {
87711
+ header,
87712
+ elements: index.elements,
87713
+ result,
87714
+ stats: { pool: pool3, windowBytes, slides, maxRecordLen, bytesRead }
87715
+ };
87716
+ }
87717
+ }
87350
87718
  function buildColumnarIndexStreaming(source, parser210, pool3, onRecordIndexed) {
87351
87719
  const sink = new ColumnarIndexSink();
87352
87720
  const { header, result, stats } = buildIndexStreaming(source, parser210, pool3, onRecordIndexed, sink);
87353
87721
  return { header, columns: sink.finalize(), result, stats };
87354
87722
  }
87723
+ async function buildColumnarIndexStreamingAsync(source, parser210, pool3, onRecordIndexed, onProgress, yieldIntervalMs) {
87724
+ const sink = new ColumnarIndexSink();
87725
+ const { header, result, stats } = await buildIndexStreamingAsync(source, parser210, pool3, onRecordIndexed, sink, onProgress, yieldIntervalMs);
87726
+ return { header, columns: sink.finalize(), result, stats };
87727
+ }
87355
87728
 
87356
87729
  // compiled/src/ifc/ifc_step_parser.js
87357
87730
  var DEFAULT_STREAM_POOL_BYTES = 1024 * 1024;
@@ -87421,6 +87794,27 @@ var IfcStepParser = class extends StepParser {
87421
87794
  const provider = new WindowedStepBufferProvider(store, opts?.chunkBytes, opts?.maxResidentChunks);
87422
87795
  return [result, new IfcStepModel(void 0, columns, provider)];
87423
87796
  }
87797
+ /**
87798
+ * Cooperative twin of {@link parseStreamToModel}: the index build
87799
+ * yields to the event loop and can fill windows from an async
87800
+ * {@link ReadableByteSource} (an OPFS `File.slice()` store). The
87801
+ * model is still windowed — geometry extract must
87802
+ * `ensureResident` first.
87803
+ *
87804
+ * @param source Sync or async byte source feeding the parse.
87805
+ * @param store Async external store backing the windowed model.
87806
+ * @param opts Optional window sizing plus parse progress.
87807
+ * @return {Promise<[ParseResult, IfcStepModel | undefined]>} The
87808
+ * parse result and the windowed model.
87809
+ */
87810
+ async parseStreamToModelAsync(source, store, opts) {
87811
+ if (store.byteLength !== source.byteLength) {
87812
+ throw new Error(`Streaming store byteLength ${store.byteLength} does not match source byteLength ${source.byteLength}`);
87813
+ }
87814
+ const { columns, result } = await buildColumnarIndexStreamingAsync(source, this, opts?.pool ?? DEFAULT_STREAM_POOL_BYTES, void 0, opts?.onProgress);
87815
+ const provider = new WindowedStepBufferProvider(store, opts?.chunkBytes, opts?.maxResidentChunks);
87816
+ return [result, new IfcStepModel(void 0, columns, provider)];
87817
+ }
87424
87818
  };
87425
87819
  IfcStepParser.Instance = new IfcStepParser();
87426
87820
  var ifc_step_parser_default = IfcStepParser;
@@ -91946,8 +92340,9 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
91946
92340
  const styledItems = this.model.types(IfcStyledItem);
91947
92341
  for (const styledItem of styledItems) {
91948
92342
  try {
91949
- if (styledItem.Item !== null) {
91950
- this.materials.styledItemMap.set(styledItem.Item.localID, styledItem.localID);
92343
+ const itemLocalID = styledItem.extractReferenceLocalID(0, 0, 1, true);
92344
+ if (itemLocalID !== null) {
92345
+ this.materials.styledItemMap.set(itemLocalID, styledItem.localID);
91951
92346
  }
91952
92347
  } catch (ex) {
91953
92348
  this.handleMapPrepError_("IfcStyledItem", styledItem.expressID, ex);
@@ -92203,6 +92598,137 @@ var IfcGeometryExtraction = class _IfcGeometryExtraction {
92203
92598
  this.extractProductGeometry(element);
92204
92599
  return true;
92205
92600
  }
92601
+ /**
92602
+ * Page in every record {@link prepareDemandExtraction} will read, so
92603
+ * the relationship sweeps succeed on a windowed source. Call before
92604
+ * the first prepare on a model whose source is external.
92605
+ *
92606
+ * Pins the prep-type records themselves (and a 1-hop of product /
92607
+ * material records the relationship getters hydrate). Does **not**
92608
+ * BFS-pin styled-item Item closures — those are facesets, and
92609
+ * walking them here pulled most of the file into the windowed LRU
92610
+ * before the first product extract.
92611
+ *
92612
+ * @return {Promise<Set<number>>} Pinned local IDs — caller must
92613
+ * {@link StepModelBase.unpinLocalIDs} after prepare/extract.
92614
+ */
92615
+ async ensureResidentForDemandPrep() {
92616
+ const pinned = /* @__PURE__ */ new Set();
92617
+ if (!this.model.isSourceExternal) {
92618
+ return pinned;
92619
+ }
92620
+ for (const type of [IfcProject, IfcMaterialDefinitionRepresentation]) {
92621
+ for (const expressID of this.model.expressIDsOfTypes(type)) {
92622
+ await this.model.ensureResidentClosureByExpressID(expressID, void 0, pinned);
92623
+ }
92624
+ }
92625
+ const recordOnlyTypes = [
92626
+ IfcRelAssociatesMaterial,
92627
+ IfcRelVoidsElement,
92628
+ IfcStyledItem,
92629
+ IfcRelAggregates
92630
+ ];
92631
+ for (const type of recordOnlyTypes) {
92632
+ for (const expressID of this.model.expressIDsOfTypes(type)) {
92633
+ const localID = this.model.resolveExpressID(expressID);
92634
+ if (localID === void 0 || pinned.has(localID)) {
92635
+ continue;
92636
+ }
92637
+ await this.model.ensureResidentByLocalID(localID);
92638
+ this.model.pinByLocalID(localID);
92639
+ pinned.add(localID);
92640
+ }
92641
+ }
92642
+ const oneHopTypes = [
92643
+ IfcRelAssociatesMaterial,
92644
+ IfcRelVoidsElement,
92645
+ IfcRelAggregates
92646
+ ];
92647
+ for (const type of oneHopTypes) {
92648
+ for (const expressID of this.model.expressIDsOfTypes(type)) {
92649
+ const localID = this.model.resolveExpressID(expressID);
92650
+ if (localID === void 0) {
92651
+ continue;
92652
+ }
92653
+ for (const refExpressID of this.model.referencedExpressIDs(localID)) {
92654
+ const refLocalID = this.model.resolveExpressID(refExpressID);
92655
+ if (refLocalID === void 0 || pinned.has(refLocalID)) {
92656
+ continue;
92657
+ }
92658
+ await this.model.ensureResidentByLocalID(refLocalID);
92659
+ this.model.pinByLocalID(refLocalID);
92660
+ pinned.add(refLocalID);
92661
+ }
92662
+ }
92663
+ }
92664
+ return pinned;
92665
+ }
92666
+ /**
92667
+ * Page in the `#ref` closure a per-product extract will acquire,
92668
+ * including this product's voids, bound materials and styled items
92669
+ * (those are inverse — not written in the product record).
92670
+ *
92671
+ * @param localID The product's local ID.
92672
+ * @return {Promise<Set<number>>} Pinned local IDs — caller must unpin
92673
+ * after extract.
92674
+ */
92675
+ async ensureResidentForProductExtract(localID, seen) {
92676
+ if (!this.model.isSourceExternal) {
92677
+ return seen ?? /* @__PURE__ */ new Set();
92678
+ }
92679
+ const extra = [];
92680
+ const voids = this.productToVoidGeometryMap.get(localID);
92681
+ if (voids !== void 0) {
92682
+ extra.push(...voids);
92683
+ }
92684
+ const material = this.materials.relMaterialsMap.get(localID);
92685
+ if (material !== void 0) {
92686
+ extra.push(material);
92687
+ }
92688
+ const visited = await this.model.ensureResidentClosureByLocalID(localID, extra, seen);
92689
+ const styleExtra = [];
92690
+ for (const visitedID of visited) {
92691
+ const styled = this.materials.styledItemMap.get(visitedID);
92692
+ if (styled !== void 0) {
92693
+ styleExtra.push(styled);
92694
+ }
92695
+ const definition = this.materials.materialDefinitionsMap.get(visitedID);
92696
+ if (definition !== void 0) {
92697
+ styleExtra.push(definition);
92698
+ }
92699
+ }
92700
+ if (styleExtra.length > 0) {
92701
+ await this.model.ensureResidentClosureByLocalID(localID, styleExtra, visited);
92702
+ }
92703
+ return visited;
92704
+ }
92705
+ /**
92706
+ * Page in the records {@link extractRelAggregateGeometry} will
92707
+ * acquire: the relationship, the relating product (and its voids)
92708
+ * and every related product.
92709
+ *
92710
+ * @param relAggregate The relationship to prefetch.
92711
+ * @return {Promise<Set<number>>} Pinned local IDs — caller must unpin
92712
+ * after extract.
92713
+ */
92714
+ async ensureResidentForAggregateExtract(relAggregate) {
92715
+ if (!this.model.isSourceExternal) {
92716
+ return /* @__PURE__ */ new Set();
92717
+ }
92718
+ const pinned = await this.model.ensureResidentClosureByLocalID(relAggregate.localID);
92719
+ const relating = relAggregate.RelatingObject;
92720
+ if (relating !== null && relating !== void 0) {
92721
+ await this.ensureResidentForProductExtract(relating.localID, pinned);
92722
+ }
92723
+ const related = relAggregate.RelatedObjects;
92724
+ if (related === null || related === void 0) {
92725
+ return pinned;
92726
+ }
92727
+ for (const product of related) {
92728
+ await this.ensureResidentForProductExtract(product.localID, pinned);
92729
+ }
92730
+ return pinned;
92731
+ }
92206
92732
  /**
92207
92733
  * The whole-model walk's SECOND product pass, shared with the demand
92208
92734
  * pump: for every IfcRelAggregates, re-extract the related products