@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
@@ -15943,7 +15943,7 @@ var ParsingBuffer = class {
15943
15943
  };
15944
15944
 
15945
15945
  // compiled/src/version/version.js
15946
- var versionString = "Conway v1.509.1435";
15946
+ var versionString = "Conway v1.511.1441";
15947
15947
 
15948
15948
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
15949
15949
  function pThreadsAllowed() {
@@ -18348,7 +18348,9 @@ var StepParser = class extends StepHeaderParser {
18348
18348
  if (next.done === true) {
18349
18349
  return next.value;
18350
18350
  }
18351
- onProgress?.(next.value);
18351
+ if (typeof next.value === "number") {
18352
+ onProgress?.(next.value);
18353
+ }
18352
18354
  }
18353
18355
  }
18354
18356
  /**
@@ -18360,7 +18362,8 @@ var StepParser = class extends StepHeaderParser {
18360
18362
  *
18361
18363
  * @param input The input parsing buffer, positioned at the data section.
18362
18364
  * @param onRecordBoundary Called at each top-level record boundary with the
18363
- * buffer; the callback may rebase the buffer's window in place.
18365
+ * buffer; the callback may rebase the buffer's window in place. Must be
18366
+ * synchronous on this driver — a returned Promise is a caller bug.
18364
18367
  * @param onRecordIndexed Called as each top-level record is indexed, with
18365
18368
  * its localID, expressID and typeID (0 for external-mapping records) — the
18366
18369
  * seam for incremental semantic consumers (type index, roots registry,
@@ -18376,6 +18379,9 @@ var StepParser = class extends StepHeaderParser {
18376
18379
  if (next.done === true) {
18377
18380
  return next.value;
18378
18381
  }
18382
+ if (typeof next.value !== "number") {
18383
+ throw new Error("parseDataBlockStreamed: async window slide requires parseDataBlockStreamedAsync");
18384
+ }
18379
18385
  onProgress?.(next.value);
18380
18386
  }
18381
18387
  }
@@ -18388,7 +18394,9 @@ var StepParser = class extends StepHeaderParser {
18388
18394
  *
18389
18395
  * @param input The input parsing buffer, positioned at the data section.
18390
18396
  * @param onRecordBoundary Called at each top-level record boundary with the
18391
- * buffer; the callback may rebase the buffer's window in place.
18397
+ * buffer; the callback may rebase the buffer's window in place. May return
18398
+ * a Promise when the slide itself is I/O (an async ByteSource); this
18399
+ * driver awaits it before the next record is lexed.
18392
18400
  * @param onRecordIndexed Called as each top-level record is indexed — see
18393
18401
  * parseDataBlockStreamed.
18394
18402
  * @param onProgress Optional byte-cursor progress callback.
@@ -18405,6 +18413,10 @@ var StepParser = class extends StepHeaderParser {
18405
18413
  if (next.done === true) {
18406
18414
  return next.value;
18407
18415
  }
18416
+ if (typeof next.value !== "number") {
18417
+ await next.value;
18418
+ continue;
18419
+ }
18408
18420
  onProgress?.(next.value);
18409
18421
  if (Date.now() - lastYield >= yieldIntervalMs) {
18410
18422
  await yieldToEventLoop();
@@ -18432,7 +18444,9 @@ var StepParser = class extends StepHeaderParser {
18432
18444
  if (next.done === true) {
18433
18445
  return next.value;
18434
18446
  }
18435
- onProgress?.(next.value);
18447
+ if (typeof next.value === "number") {
18448
+ onProgress?.(next.value);
18449
+ }
18436
18450
  if (Date.now() - lastYield >= yieldIntervalMs) {
18437
18451
  await yieldToEventLoop();
18438
18452
  lastYield = Date.now();
@@ -18596,7 +18610,10 @@ var StepParser = class extends StepHeaderParser {
18596
18610
  if ((++parsedElementCount & PARSE_PROGRESS_ELEMENT_MASK) === 0) {
18597
18611
  yield input.cursor;
18598
18612
  }
18599
- onRecordBoundary?.(input);
18613
+ const slide = onRecordBoundary?.(input);
18614
+ if (slide !== void 0 && typeof slide.then === "function") {
18615
+ yield slide;
18616
+ }
18600
18617
  if (!charws(HASH2)) {
18601
18618
  if (tokenws(END_SECTION)) {
18602
18619
  return parseResult(ParseResult.COMPLETE);
@@ -20022,6 +20039,70 @@ var yargs_default = Yargs;
20022
20039
  // compiled/src/AP214E3_2010/ap214_command_line_main.js
20023
20040
  var import_fs = __toESM(require("fs"), 1);
20024
20041
 
20042
+ // compiled/src/step/parsing/express_ref_scan.js
20043
+ var HASH3 = 35;
20044
+ var QUOTE4 = 39;
20045
+ var DQUOTE = 34;
20046
+ var ZERO5 = 48;
20047
+ var NINE2 = 57;
20048
+ var SLASH3 = 47;
20049
+ var STAR2 = 42;
20050
+ function scanExpressRefs(bytes, from = 0, length = bytes.length - from) {
20051
+ const refs = [];
20052
+ const seen = /* @__PURE__ */ new Set();
20053
+ const end = Math.min(from + length, bytes.length);
20054
+ let cursor = from;
20055
+ while (cursor < end) {
20056
+ const byte = bytes[cursor];
20057
+ if (byte === QUOTE4) {
20058
+ cursor = skipQuoted_(bytes, cursor + 1, end, QUOTE4, true);
20059
+ continue;
20060
+ }
20061
+ if (byte === DQUOTE) {
20062
+ cursor = skipQuoted_(bytes, cursor + 1, end, DQUOTE, false);
20063
+ continue;
20064
+ }
20065
+ if (byte === SLASH3 && cursor + 1 < end && bytes[cursor + 1] === STAR2) {
20066
+ cursor += 2;
20067
+ while (cursor + 1 < end && !(bytes[cursor] === STAR2 && bytes[cursor + 1] === SLASH3)) {
20068
+ ++cursor;
20069
+ }
20070
+ cursor += 2;
20071
+ continue;
20072
+ }
20073
+ if (byte === HASH3) {
20074
+ let value = 0;
20075
+ let digit = cursor + 1;
20076
+ while (digit < end && bytes[digit] >= ZERO5 && bytes[digit] <= NINE2) {
20077
+ value = value * 10 + (bytes[digit] - ZERO5);
20078
+ ++digit;
20079
+ }
20080
+ if (digit > cursor + 1 && value !== 0 && !seen.has(value)) {
20081
+ seen.add(value);
20082
+ refs.push(value);
20083
+ }
20084
+ cursor = digit;
20085
+ continue;
20086
+ }
20087
+ ++cursor;
20088
+ }
20089
+ return refs;
20090
+ }
20091
+ function skipQuoted_(bytes, from, end, quote, doubledEscape) {
20092
+ let cursor = from;
20093
+ while (cursor < end) {
20094
+ if (bytes[cursor] === quote) {
20095
+ if (doubledEscape && cursor + 1 < end && bytes[cursor + 1] === quote) {
20096
+ cursor += 2;
20097
+ continue;
20098
+ }
20099
+ return cursor + 1;
20100
+ }
20101
+ ++cursor;
20102
+ }
20103
+ return end;
20104
+ }
20105
+
20025
20106
  // compiled/src/step/step_buffer_provider.js
20026
20107
  var DEFAULT_CHUNK_BYTES = 4 * 1024 * 1024;
20027
20108
  var DEFAULT_MAX_RESIDENT_CHUNKS = 16;
@@ -20202,7 +20283,7 @@ var WindowedStepBufferProvider = class {
20202
20283
  const firstChunk = Math.floor(address2 / chunkBytes);
20203
20284
  const lastChunk = Math.floor((address2 + Math.max(length, 1) - 1) / chunkBytes);
20204
20285
  for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
20205
- this.ensurePins_.set(chunkIndex, (this.ensurePins_.get(chunkIndex) ?? 0) + 1);
20286
+ this.addPin_(chunkIndex, 1);
20206
20287
  }
20207
20288
  try {
20208
20289
  const loads = [];
@@ -20245,15 +20326,63 @@ var WindowedStepBufferProvider = class {
20245
20326
  }
20246
20327
  } finally {
20247
20328
  for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
20248
- const pins = this.ensurePins_.get(chunkIndex) ?? 0;
20249
- if (pins <= 1) {
20250
- this.ensurePins_.delete(chunkIndex);
20251
- } else {
20252
- this.ensurePins_.set(chunkIndex, pins - 1);
20253
- }
20329
+ this.addPin_(chunkIndex, -1);
20254
20330
  }
20255
20331
  }
20256
20332
  }
20333
+ /**
20334
+ * Hold a range against LRU eviction until {@link unpinRange}.
20335
+ *
20336
+ * @param address Absolute start of the range.
20337
+ * @param length Length of the range.
20338
+ */
20339
+ pinRange(address2, length) {
20340
+ for (const chunkIndex of this.chunkSpan_(address2, length)) {
20341
+ this.addPin_(chunkIndex, 1);
20342
+ }
20343
+ }
20344
+ /**
20345
+ * Drop one {@link pinRange} hold.
20346
+ *
20347
+ * @param address Absolute start of the range.
20348
+ * @param length Length of the range.
20349
+ */
20350
+ unpinRange(address2, length) {
20351
+ for (const chunkIndex of this.chunkSpan_(address2, length)) {
20352
+ this.addPin_(chunkIndex, -1);
20353
+ }
20354
+ }
20355
+ /**
20356
+ * Chunk indices covering `[address, address + length)`.
20357
+ *
20358
+ * @param address Absolute start.
20359
+ * @param length Length in bytes.
20360
+ * @return {number[]} Inclusive chunk indices.
20361
+ */
20362
+ chunkSpan_(address2, length) {
20363
+ const chunkBytes = this.chunkBytes_;
20364
+ const firstChunk = Math.floor(address2 / chunkBytes);
20365
+ const lastChunk = Math.floor((address2 + Math.max(length, 1) - 1) / chunkBytes);
20366
+ const span = [];
20367
+ for (let chunkIndex = firstChunk; chunkIndex <= lastChunk; ++chunkIndex) {
20368
+ span.push(chunkIndex);
20369
+ }
20370
+ return span;
20371
+ }
20372
+ /**
20373
+ * Adjust a chunk's pin count, deleting the entry at zero.
20374
+ *
20375
+ * @param chunkIndex The chunk.
20376
+ * @param delta +1 pin or -1 unpin.
20377
+ */
20378
+ addPin_(chunkIndex, delta) {
20379
+ const pins = (this.ensurePins_.get(chunkIndex) ?? 0) + delta;
20380
+ if (pins <= 0) {
20381
+ this.ensurePins_.delete(chunkIndex);
20382
+ } else {
20383
+ this.ensurePins_.set(chunkIndex, pins);
20384
+ }
20385
+ }
20257
20386
  };
20258
20387
 
20259
20388
  // compiled/src/step/parsing/step_vtable_builder.js
@@ -20323,7 +20452,7 @@ var StepVtableBuilder = class {
20323
20452
  var OPEN_PAREN3 = parsing_constants_default.OPEN_PAREN;
20324
20453
  var CLOSE_PAREN3 = parsing_constants_default.CLOSE_PAREN;
20325
20454
  var COMMA3 = parsing_constants_default.COMMA;
20326
- var ZERO5 = parsing_constants_default.ZERO;
20455
+ var ZERO6 = parsing_constants_default.ZERO;
20327
20456
  var WHITESPACE4 = parsing_constants_default.WHITE_SPACE_SET;
20328
20457
  function extractIntegerArrayAt(buffer, cursor, endCursor, sink) {
20329
20458
  const marked = sink.length;
@@ -20392,7 +20521,7 @@ function extractUnsignedIntegerListAt(buffer, cursor, endCursor, sink) {
20392
20521
  return void 0;
20393
20522
  }
20394
20523
  }
20395
- const digit = buffer[cursor] - ZERO5;
20524
+ const digit = buffer[cursor] - ZERO6;
20396
20525
  if (digit < 0 || digit > 9) {
20397
20526
  sink.truncate(marked);
20398
20527
  return void 0;
@@ -20400,7 +20529,7 @@ function extractUnsignedIntegerListAt(buffer, cursor, endCursor, sink) {
20400
20529
  let value = digit;
20401
20530
  ++cursor;
20402
20531
  while (cursor < endCursor) {
20403
- const next = buffer[cursor] - ZERO5;
20532
+ const next = buffer[cursor] - ZERO6;
20404
20533
  if (next < 0 || next > 9) {
20405
20534
  break;
20406
20535
  }
@@ -21195,6 +21324,141 @@ var StepModelBase = class {
21195
21324
  }
21196
21325
  await this.ensureResidentByLocalID(localID);
21197
21326
  }
21327
+ /**
21328
+ * Page in the byte ranges a record *and every `#ref` reachable from
21329
+ * its text* will need, so a following synchronous extract of that
21330
+ * subgraph succeeds on a windowed source. Inverse relationships
21331
+ * (not written in the record) are not discovered — callers that
21332
+ * need them (rel-voids, styled items) pass their local IDs as
21333
+ * `extraLocalIDs`.
21334
+ *
21335
+ * No-op while the source is fully resident.
21336
+ *
21337
+ * @param localID Seed record.
21338
+ * @param extraLocalIDs Additional seeds (voids, styles, materials).
21339
+ * @param seen Optional set to extend (also skips already-pinned IDs).
21340
+ * @return {Promise<Set<number>>} The local IDs that were visited.
21341
+ */
21342
+ async ensureResidentClosureByLocalID(localID, extraLocalIDs, seen = /* @__PURE__ */ new Set()) {
21343
+ if (!this.isSourceExternal) {
21344
+ return seen;
21345
+ }
21346
+ const stack = [localID];
21347
+ if (extraLocalIDs !== void 0) {
21348
+ for (const extra of extraLocalIDs) {
21349
+ stack.push(extra);
21350
+ }
21351
+ }
21352
+ while (stack.length > 0) {
21353
+ const id = stack.pop();
21354
+ if (seen.has(id) || id >= this.count_) {
21355
+ continue;
21356
+ }
21357
+ seen.add(id);
21358
+ await this.ensureResidentByLocalID(id);
21359
+ this.pinByLocalID(id);
21360
+ for (const expressID of this.scanRecordRefs_(id)) {
21361
+ const referenced = this.expressIDMap_.get(expressID);
21362
+ if (referenced !== void 0 && !seen.has(referenced)) {
21363
+ stack.push(referenced);
21364
+ }
21365
+ }
21366
+ }
21367
+ return seen;
21368
+ }
21369
+ /**
21370
+ * Hold a record's source range against windowed LRU eviction until
21371
+ * {@link unpinByLocalID}. No-op while the source is fully resident.
21372
+ *
21373
+ * @param localID The record to pin.
21374
+ */
21375
+ pinByLocalID(localID) {
21376
+ if (!this.isSourceExternal || localID >= this.count_) {
21377
+ return;
21378
+ }
21379
+ this.bufferProvider_.pinRange?.(this.address_[localID], this.length_[localID]);
21380
+ }
21381
+ /**
21382
+ * Drop one {@link pinByLocalID} hold.
21383
+ *
21384
+ * @param localID The record to unpin.
21385
+ */
21386
+ unpinByLocalID(localID) {
21387
+ if (!this.isSourceExternal || localID >= this.count_) {
21388
+ return;
21389
+ }
21390
+ this.bufferProvider_.unpinRange?.(this.address_[localID], this.length_[localID]);
21391
+ }
21392
+ /**
21393
+ * Unpin every local ID in `localIDs` (best-effort; missing IDs are
21394
+ * ignored). Use after {@link ensureResidentClosureByLocalID}, which
21395
+ * pins as it walks.
21396
+ *
21397
+ * @param localIDs The records to unpin.
21398
+ */
21399
+ unpinLocalIDs(localIDs) {
21400
+ for (const localID of localIDs) {
21401
+ this.unpinByLocalID(localID);
21402
+ }
21403
+ }
21404
+ /**
21405
+ * Express-ID twin of {@link ensureResidentClosureByLocalID}. Unknown
21406
+ * IDs resolve to an empty visit set.
21407
+ *
21408
+ * @param expressID Seed record.
21409
+ * @param extraLocalIDs Additional seeds (local IDs).
21410
+ * @return {Promise<Set<number>>} The local IDs that were visited.
21411
+ */
21412
+ async ensureResidentClosureByExpressID(expressID, extraLocalIDs, seen) {
21413
+ if (!this.isSourceExternal) {
21414
+ return seen ?? /* @__PURE__ */ new Set();
21415
+ }
21416
+ const localID = this.expressIDMap_.get(expressID);
21417
+ if (localID === void 0) {
21418
+ return seen ?? /* @__PURE__ */ new Set();
21419
+ }
21420
+ return this.ensureResidentClosureByLocalID(localID, extraLocalIDs, seen);
21421
+ }
21422
+ /**
21423
+ * Express IDs referenced by a resident record (`#<id>` tokens in its
21424
+ * text, including multi-mapped class records). The record itself
21425
+ * must already be resident.
21426
+ *
21427
+ * @param localID The record to scan.
21428
+ * @return {number[]} Distinct referenced express IDs.
21429
+ */
21430
+ referencedExpressIDs(localID) {
21431
+ return this.scanRecordRefs_(localID);
21432
+ }
21433
+ /**
21434
+ * Scan a resident record's bytes for `#<expressID>` references.
21435
+ *
21436
+ * @param localID The record to scan.
21437
+ * @return {number[]} Distinct referenced express IDs.
21438
+ */
21439
+ scanRecordRefs_(localID) {
21440
+ const address2 = this.address_[localID];
21441
+ const length = this.length_[localID];
21442
+ const acquisition = this.bufferProvider_.acquire(address2, length);
21443
+ const viewStart = address2 - acquisition.offset;
21444
+ const refs = scanExpressRefs(acquisition.buffer, viewStart, length);
21445
+ const complex = this.complexEntries_?.get(localID);
21446
+ if (complex?.multiMapping === void 0) {
21447
+ return refs;
21448
+ }
21449
+ const seen = new Set(refs);
21450
+ for (const mapped of complex.multiMapping) {
21451
+ const mappedAcquisition = this.bufferProvider_.acquire(mapped.address, mapped.length);
21452
+ const mappedStart = mapped.address - mappedAcquisition.offset;
21453
+ for (const expressID of scanExpressRefs(mappedAcquisition.buffer, mappedStart, mapped.length)) {
21454
+ if (!seen.has(expressID)) {
21455
+ seen.add(expressID);
21456
+ refs.push(expressID);
21457
+ }
21458
+ }
21459
+ }
21460
+ return refs;
21461
+ }
21198
21462
  /**
21199
21463
  * Get the number of elements/entities in this model.
21200
21464
  *
@@ -22534,6 +22798,38 @@ var StepEntityBase = class {
22534
22798
  }
22535
22799
  return value;
22536
22800
  }
22801
+ /**
22802
+ * Resolve a single reference field to its local ID without
22803
+ * constructing the target entity. Prep maps that only store
22804
+ * `Item.localID` (IfcStyledItem → faceset) must not hydrate the
22805
+ * target: on a windowed source that would pin the faceset record
22806
+ * (often megabytes) for every styled item at once.
22807
+ *
22808
+ * @param offset The offset in the vtable to extract from.
22809
+ * @param baseOffset The base offset of the class in the vtable.
22810
+ * @param depth The depth in the inheritance hierarchy.
22811
+ * @param optional Is this an optional field?
22812
+ * @return {number | null} The referenced local ID, or null if the
22813
+ * field is unset/optional-null.
22814
+ */
22815
+ extractReferenceLocalID(offset, baseOffset, depth, optional) {
22816
+ const [cursor, endCursor, buffer] = this.getOffsetAndEndCursor(offset, baseOffset, depth);
22817
+ const expressID = stepExtractReference(buffer, cursor, endCursor);
22818
+ if (expressID !== void 0) {
22819
+ return this.model.resolveExpressID(expressID) ?? null;
22820
+ }
22821
+ const inline = this.model.getInlineElementByAddress(extractInlineElementAddress(buffer, cursor, endCursor));
22822
+ if (inline !== void 0) {
22823
+ return inline.localID;
22824
+ }
22825
+ if (!optional) {
22826
+ throw new Error("Value in STEP was incorrectly typed");
22827
+ }
22828
+ if (!this.model.nullOnErrors && stepExtractOptional(buffer, cursor, endCursor) !== null) {
22829
+ throw new Error("Value in STEP was incorrectly typed");
22830
+ }
22831
+ return null;
22832
+ }
22537
22833
  /**
22538
22834
  *
22539
22835
  * @param buffer