@ifc-lite/export 2.9.4 → 3.0.1

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 (126) hide show
  1. package/dist/csv-cell.d.ts +101 -0
  2. package/dist/csv-cell.d.ts.map +1 -0
  3. package/dist/csv-cell.js +90 -0
  4. package/dist/csv-cell.js.map +1 -0
  5. package/dist/declared-property-type.d.ts +19 -8
  6. package/dist/declared-property-type.d.ts.map +1 -1
  7. package/dist/declared-property-type.js +31 -9
  8. package/dist/declared-property-type.js.map +1 -1
  9. package/dist/demesh-session.d.ts.map +1 -1
  10. package/dist/demesh-session.js +2 -1
  11. package/dist/demesh-session.js.map +1 -1
  12. package/dist/effective-index.js +1 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +9 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/lod1-generator.d.ts +0 -2
  18. package/dist/lod1-generator.d.ts.map +1 -1
  19. package/dist/lod1-generator.js +1 -1
  20. package/dist/lod1-generator.js.map +1 -1
  21. package/dist/merged-exporter.d.ts +19 -0
  22. package/dist/merged-exporter.d.ts.map +1 -1
  23. package/dist/merged-exporter.js +23 -41
  24. package/dist/merged-exporter.js.map +1 -1
  25. package/dist/parquet-exporter.d.ts +1 -3
  26. package/dist/parquet-exporter.d.ts.map +1 -1
  27. package/dist/parquet-exporter.js +1 -0
  28. package/dist/parquet-exporter.js.map +1 -1
  29. package/dist/property-value-serialization.d.ts +54 -0
  30. package/dist/property-value-serialization.d.ts.map +1 -0
  31. package/dist/property-value-serialization.js +134 -0
  32. package/dist/property-value-serialization.js.map +1 -0
  33. package/dist/reference-collector.d.ts +20 -0
  34. package/dist/reference-collector.d.ts.map +1 -1
  35. package/dist/reference-collector.js +46 -113
  36. package/dist/reference-collector.js.map +1 -1
  37. package/dist/schema-converter.d.ts.map +1 -1
  38. package/dist/schema-converter.js +46 -96
  39. package/dist/schema-converter.js.map +1 -1
  40. package/dist/source-ref-bounds.d.ts +3 -3
  41. package/dist/source-ref-bounds.js +3 -3
  42. package/dist/step-attribute-mutations.d.ts +49 -0
  43. package/dist/step-attribute-mutations.d.ts.map +1 -0
  44. package/dist/step-attribute-mutations.js +172 -0
  45. package/dist/step-attribute-mutations.js.map +1 -0
  46. package/dist/step-attribute-serializers.d.ts +41 -0
  47. package/dist/step-attribute-serializers.d.ts.map +1 -0
  48. package/dist/step-attribute-serializers.js +74 -0
  49. package/dist/step-attribute-serializers.js.map +1 -0
  50. package/dist/step-collection.d.ts +55 -0
  51. package/dist/step-collection.d.ts.map +1 -0
  52. package/dist/step-collection.js +126 -0
  53. package/dist/step-collection.js.map +1 -0
  54. package/dist/step-export-contexts.d.ts +61 -0
  55. package/dist/step-export-contexts.d.ts.map +1 -0
  56. package/dist/step-export-contexts.js +50 -0
  57. package/dist/step-export-contexts.js.map +1 -0
  58. package/dist/step-export-types.d.ts +262 -0
  59. package/dist/step-export-types.d.ts.map +1 -0
  60. package/dist/step-export-types.js +22 -0
  61. package/dist/step-export-types.js.map +1 -0
  62. package/dist/step-exporter.d.ts +23 -354
  63. package/dist/step-exporter.d.ts.map +1 -1
  64. package/dist/step-exporter.js +76 -1260
  65. package/dist/step-exporter.js.map +1 -1
  66. package/dist/step-geometry-types.d.ts +3 -0
  67. package/dist/step-geometry-types.d.ts.map +1 -0
  68. package/dist/step-geometry-types.js +73 -0
  69. package/dist/step-geometry-types.js.map +1 -0
  70. package/dist/step-georeferencing.d.ts +0 -10
  71. package/dist/step-georeferencing.d.ts.map +1 -1
  72. package/dist/step-georeferencing.js +46 -94
  73. package/dist/step-georeferencing.js.map +1 -1
  74. package/dist/step-header.d.ts +53 -0
  75. package/dist/step-header.d.ts.map +1 -0
  76. package/dist/step-header.js +63 -0
  77. package/dist/step-header.js.map +1 -0
  78. package/dist/step-map-unit.d.ts +90 -0
  79. package/dist/step-map-unit.d.ts.map +1 -0
  80. package/dist/step-map-unit.js +277 -0
  81. package/dist/step-map-unit.js.map +1 -0
  82. package/dist/step-omission-predicates.d.ts +51 -0
  83. package/dist/step-omission-predicates.d.ts.map +1 -0
  84. package/dist/step-omission-predicates.js +268 -0
  85. package/dist/step-omission-predicates.js.map +1 -0
  86. package/dist/step-overlay-attribute-overrides.d.ts +32 -0
  87. package/dist/step-overlay-attribute-overrides.d.ts.map +1 -0
  88. package/dist/step-overlay-attribute-overrides.js +83 -0
  89. package/dist/step-overlay-attribute-overrides.js.map +1 -0
  90. package/dist/step-overlay-entities.d.ts +64 -0
  91. package/dist/step-overlay-entities.d.ts.map +1 -0
  92. package/dist/step-overlay-entities.js +119 -0
  93. package/dist/step-overlay-entities.js.map +1 -0
  94. package/dist/step-pass-builder.d.ts +45 -0
  95. package/dist/step-pass-builder.d.ts.map +1 -0
  96. package/dist/step-pass-builder.js +249 -0
  97. package/dist/step-pass-builder.js.map +1 -0
  98. package/dist/step-property-set-collection.d.ts +24 -0
  99. package/dist/step-property-set-collection.d.ts.map +1 -0
  100. package/dist/step-property-set-collection.js +286 -0
  101. package/dist/step-property-set-collection.js.map +1 -0
  102. package/dist/step-property-set-generators.d.ts +20 -0
  103. package/dist/step-property-set-generators.d.ts.map +1 -0
  104. package/dist/step-property-set-generators.js +98 -0
  105. package/dist/step-property-set-generators.js.map +1 -0
  106. package/dist/step-property-set-index.d.ts +25 -0
  107. package/dist/step-property-set-index.d.ts.map +1 -0
  108. package/dist/step-property-set-index.js +69 -0
  109. package/dist/step-property-set-index.js.map +1 -0
  110. package/dist/step-property-set-readers.d.ts +158 -0
  111. package/dist/step-property-set-readers.d.ts.map +1 -0
  112. package/dist/step-property-set-readers.js +251 -0
  113. package/dist/step-property-set-readers.js.map +1 -0
  114. package/dist/step-property-sets.d.ts +2 -184
  115. package/dist/step-property-sets.d.ts.map +1 -1
  116. package/dist/step-property-sets.js +22 -674
  117. package/dist/step-property-sets.js.map +1 -1
  118. package/dist/step-serialization.d.ts +33 -52
  119. package/dist/step-serialization.d.ts.map +1 -1
  120. package/dist/step-serialization.js +69 -131
  121. package/dist/step-serialization.js.map +1 -1
  122. package/dist/step-source-iteration.d.ts +97 -0
  123. package/dist/step-source-iteration.d.ts.map +1 -0
  124. package/dist/step-source-iteration.js +178 -0
  125. package/dist/step-source-iteration.js.map +1 -0
  126. package/package.json +7 -7
@@ -0,0 +1,268 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ export function evaluateOmissionPredicates(pass, options, applyMutations, excludeGeometry, mutationView) {
5
+ /**
6
+ * "Does this model hold a record whose bytes this export cannot read?" —
7
+ * the one disjunct of {@link mayNameOmittedRefs} that is not already a
8
+ * value in hand, so it is a function and called last, behind `||`.
9
+ *
10
+ * Scans the EFFECTIVE index, and that is a requirement rather than an
11
+ * implementation detail: it has to cover the id space
12
+ * `isOmittedFromOutput` answers over, and an unreadable record can live in
13
+ * `deferredEntityIndex` — the secondary index `getCompleteEntityIndex`
14
+ * exists to merge — and nowhere in `entityIndex.byId`. Scanning `byId`,
15
+ * the obvious cheaper source, was measured to leave the gate false and
16
+ * ship the dangling ref; `relationship-filter-gate.test.ts` pins the
17
+ * merged scan behaviourally so that shortcut cannot come back as an
18
+ * optimisation.
19
+ *
20
+ * Reads the ref ITERATION yields — what the source-iteration pass's own
21
+ * skip reads — rather than re-asking `effective.get(id)` per id as
22
+ * `willBeEmitted` does, which on the largest files would cost a binary
23
+ * search and an allocation per entity and defeat the point of the gate.
24
+ * Every index here keeps the two in step by construction:
25
+ * `CompactEntityIndex` serves `get`, `has` and iteration from one pair of
26
+ * `Uint32Array`s, a `Map` trivially agrees, the merged deferred view is
27
+ * `byId.get ?? deferred.get` over `yield* byId; yield* deferred`, and
28
+ * `OverlayIndex` filters both by one tombstone set. An index whose `has`
29
+ * accepted an id its iteration never yields would defeat this — and would
30
+ * equally defeat the source-iteration pass's skip, so that file is broken
31
+ * either way; nothing in the repo builds one.
32
+ *
33
+ * Not short-circuited on `overlayActive`: an overlay-created record carries
34
+ * `(OVERLAY_BYTE_OFFSET, 0)` and so counts as unreadable here, which would
35
+ * make this always answer true once an overlay exists. Harmless —
36
+ * `overlayActive` is an earlier disjunct, so this never runs then — and
37
+ * correct if it ever did.
38
+ *
39
+ * ## Why a standalone pass rather than a value off the index
40
+ *
41
+ * Measured: 12.0 ms of a 470 ms export at 714,485 entities (2.55%), one
42
+ * call, whole index walked because a well-formed model gives it nothing to
43
+ * short-circuit on. The cheaper shape was prototyped and is 13x faster —
44
+ * `min(byteLength)` and `max(byteOffset + byteLength)` over
45
+ * `CompactEntityIndex`'s own `Uint32Array`s answer "is every ref readable
46
+ * within `extent`" exactly and allocation-free in 0.74 ms — and was not
47
+ * taken, because 11 ms does not buy what it costs.
48
+ *
49
+ * It could only stand in FRONT of this loop, never replace it:
50
+ * `EntityByIdIndex` is a structural type and plain `Map`s satisfy it
51
+ * (`synthetic-data-store.ts` builds one), so the walk stays for those. That
52
+ * makes it a second implementation of one predicate across a package
53
+ * boundary — the defect class #2637, #2668 and this gate are all instances
54
+ * of. And storing it at construction is the invariant
55
+ * `source-ref-bounds.ts` exists to delete: `CompactEntityIndex` is built by
56
+ * its builder, by `compactEntityIndexFromColumns` in the transport, and by
57
+ * embedders, so a value one producer writes is a value the next can skip,
58
+ * whereas testing the ref where it is READ cannot be bypassed. If 2.5% ever
59
+ * has to go, the safe shape is a memoized derivation the index computes
60
+ * from its own arrays on demand — not a field set at build time.
61
+ */
62
+ const hasAnyUnreadableSourceRef = () => {
63
+ for (const [, ref] of pass.effective) {
64
+ if (!pass.isReadableSourceRef(ref))
65
+ return true;
66
+ }
67
+ return false;
68
+ };
69
+ // If delta only, only export modified entities. Overlay-created entities
70
+ // also count — without this, `createEntity()`-only edits would silently
71
+ // drop out of delta exports.
72
+ const overlayNewEntityCount = (mutationView
73
+ && applyMutations
74
+ && typeof mutationView.getNewEntities === 'function') ? mutationView.getNewEntities().length : 0;
75
+ // Georef-only deltas (newGeorefLines populated but no entity changes) must
76
+ // still produce a non-empty DATA section.
77
+ if (
78
+ // `=== true`, matching `step-pass-builder.ts`'s two reads of the same
79
+ // option. A plain-JS caller passing `deltaOnly: 1` would otherwise have
80
+ // this module call the export a delta while the predicates there call it
81
+ // a full export -- the same divergence this file argues against for
82
+ // `visibleOnly` a few paragraphs down.
83
+ options.deltaOnly === true
84
+ && pass.modifiedEntities.size === 0
85
+ && overlayNewEntityCount === 0
86
+ && pass.newGeorefLines.length === 0) {
87
+ const emptyContent = new TextEncoder().encode(pass.buildHeader(0) + 'DATA;\nENDSEC;\nEND-ISO-10303-21;\n');
88
+ return {
89
+ kind: 'short-circuit',
90
+ result: {
91
+ content: emptyContent,
92
+ stats: {
93
+ entityCount: 0,
94
+ newEntityCount: 0,
95
+ modifiedEntityCount: 0,
96
+ fileSize: emptyContent.byteLength,
97
+ warnings: pass.warnings,
98
+ },
99
+ },
100
+ };
101
+ }
102
+ /**
103
+ * "May a line this export writes name `#id`?" — the single predicate both
104
+ * relationship-line filter sites consume, derived from `willBeEmitted`
105
+ * rather than from a second list kept in step with it by hand.
106
+ *
107
+ * DERIVED, not identical, and the gaps are named below rather than glossed:
108
+ * a scope qualifier for ids the file never had, and `deltaOnly`, where
109
+ * `willBeEmitted` answers `true` for a source record whose line this export
110
+ * does not write at all (the source-iteration pass is skipped wholesale in
111
+ * that mode). Nor does this make the CLOSURE WALK agree with either: the
112
+ * walk keeps `isRefExcludedDuringClosureWalk` and diverges from this
113
+ * predicate for an unreadable source ref — see the note on that predicate,
114
+ * and the "walk and output predicates diverge" test.
115
+ *
116
+ * The hand-kept second list is the bug this replaces. `willBeEmitted` recognises
117
+ * seven reasons a line never lands — outside the closure, hidden product,
118
+ * tombstoned, never existed, unreadable source ref (#2491), geometry
119
+ * excluded by options, and the `deltaOnly` carve-out — while the filter
120
+ * used to consume `(hiddenProductIds !== null && hiddenProductIds.has(id))
121
+ * || effective.isDeleted(id)`, which answered for two: hidden product, and
122
+ * tombstoned. Notably NOT "never existed" — that one is deliberately out of
123
+ * scope for the filter even now, for the reason under the qualifier heading
124
+ * below. The gap was live: on a PLAIN full export, with no `visibleOnly`,
125
+ * no deletions and no overlay, an unreadable ref made the source-iteration
126
+ * pass skip an entity's line while an `IFCREL*` naming it shipped verbatim,
127
+ * dangling.
128
+ *
129
+ * Deriving the filter from `willBeEmitted` is also what fixed the
130
+ * `mayNameExcludedRefs` gate that stands in front of both call sites. That
131
+ * gate used to be a SECOND, shorter enumeration of the same reasons
132
+ * (hidden products exist, or an overlay is active) and answered `false` for
133
+ * exactly the unreadable-ref export above, so the filter never ran at all.
134
+ * It is now {@link mayNameOmittedRefs} — see there for why a gate is kept
135
+ * at all (running the filter on every `IFCREL*` line costs +13% of a
136
+ * 714k-entity export) and for the enumeration it has to cover.
137
+ *
138
+ * ## The one qualifier on top of `willBeEmitted`
139
+ *
140
+ * `willBeEmitted` answers NO for an id neither the file nor the session
141
+ * ever had, which is right for its own job — nothing GENERATED may name an
142
+ * id that does not exist. It is the wrong answer for rewriting a SOURCE
143
+ * line, and the difference is whose bug it is. A `#999` already sitting in
144
+ * a relationship's `OwnerHistory` slot in the input file is a dangling ref
145
+ * this export did not create and cannot repair; `filterHiddenRefsFromRelationshipLine`
146
+ * withholds a whole relationship when an excluded id is in a bare scalar,
147
+ * so treating it as an exclusion would DELETE a visible element's pset over
148
+ * somebody else's corrupt file. That is the harm #2637 was about, and
149
+ * `step-exporter.test.ts` states the position out loud: a pre-existing
150
+ * dangling ref is out of scope and ships as it arrived.
151
+ *
152
+ * So the filter asks the narrower question: is `#id` an entity this model
153
+ * HAS, that this export is nonetheless not writing? `effective.has` is
154
+ * false for a tombstone, hence the explicit `isDeleted` arm — deleting an
155
+ * entity IS this session's doing and must be filtered.
156
+ *
157
+ * This is a scope qualifier, not a second enumeration of omission reasons:
158
+ * an eighth reason added to `willBeEmitted` still reaches the filter with
159
+ * no edit here.
160
+ *
161
+ * ## What the filter can and cannot reach
162
+ *
163
+ * Only `IFCREL*` lines. A `#N` named from a product's `Representation` or
164
+ * `ObjectPlacement` slot is not touched, so `includeGeometry:false` — a
165
+ * reason `willBeEmitted` does answer for — produces the same dangling refs
166
+ * with this predicate as without it. Measured on `tests/models/AB22.ifc`:
167
+ * 80 dangling refs before and after, output byte-identical but for the
168
+ * header timestamp.
169
+ *
170
+ * ## Withholding is not free
171
+ *
172
+ * When the omitted id sits in a single-valued slot, or is a set's only
173
+ * member, `filterHiddenRefsFromRelationshipLine` withholds the WHOLE
174
+ * relationship — so an entity that relationship also named loses the
175
+ * association, on a plain full export with no options set. That is why the
176
+ * call sites push {@link relationshipWithheldWarning}.
177
+ *
178
+ * See `unreadable-ref-dangling.test.ts` for the reproduction. #2637 is the
179
+ * prior instance of this class, which took seven rounds because the same
180
+ * decision was recomputed per call site.
181
+ */
182
+ const isOmittedFromOutput = (id) => (pass.effective.has(id) || pass.effective.isDeleted(id)) && !pass.willBeEmitted(id);
183
+ /**
184
+ * "Can ANY id be omitted from this export at all?" — the precondition both
185
+ * `IFCREL*` filter sites are gated on, so the common export pays nothing.
186
+ *
187
+ * ## Why a gate exists
188
+ *
189
+ * Running `filterHiddenRefsFromRelationshipLine` on every `IFCREL*` line
190
+ * costs a re-parse of that line's attribute list, and a large model is
191
+ * mostly relationships. Measured on `tests/models/ara3d/schependomlaan.ifc`
192
+ * (714,485 entities, 21 interleaved reps in randomised order): 463 ms
193
+ * median with this gate false versus 523 ms filtering unconditionally,
194
+ * **+13%**. That is a real price paid on every export to protect a state
195
+ * most exports are not in. With the gate, the same export is 475 ms, +2.7%,
196
+ * all of it the fourth disjunct's one pass.
197
+ *
198
+ * ## Why THIS gate, and not the one that shipped before
199
+ *
200
+ * The gate this replaces was a second, hand-kept enumeration of "reasons an
201
+ * entity might be excluded", and it went stale exactly as such lists do: it
202
+ * named hidden products and the overlay and knew nothing about an unreadable
203
+ * source ref, so the bug this branch fixes reached the output with the
204
+ * filter switched off. A cheap gate is safe only as an OVER-APPROXIMATION of
205
+ * `isOmittedFromOutput` that can be checked against `willBeEmitted` branch
206
+ * by branch — so every branch is listed, with the disjunct that covers it:
207
+ *
208
+ * | `willBeEmitted` answers NO at | covered by |
209
+ * |----------------------------------------------|-------------------------------|
210
+ * | `allowedEntityIds !== null && !has(id)` | `allowedEntityIds !== null` |
211
+ * | `!ref`, because the overlay tombstoned `id` | `overlayActive` |
212
+ * | overlay-created, geometry excluded | `overlayActive` |
213
+ * | `!isReadableSourceRef(ref)` | `hasAnyUnreadableSourceRef()` |
214
+ * | source-backed, geometry excluded | `excludeGeometry` |
215
+ * | `!ref`, because `id` never existed | out of scope (below) |
216
+ * | `!ref` while `has(id)` is TRUE | nothing (below) |
217
+ *
218
+ * "Never existed" needs no disjunct: `isOmittedFromOutput`'s own
219
+ * `(has || isDeleted)` qualifier already drops it, deliberately — a
220
+ * pre-existing dangling ref in somebody else's file is not this export's to
221
+ * repair (see that predicate's note).
222
+ *
223
+ * The last row is a real hole and is stated rather than hidden: an index
224
+ * that answers `has(id)` for an id its iteration never yields makes
225
+ * `isOmittedFromOutput` true with no disjunct true. It needs an index whose
226
+ * `has`, `get` and iteration disagree, which nothing in the repo builds and
227
+ * which would already break the source-iteration pass's own skip — see
228
+ * {@link hasAnyUnreadableSourceRef}, which rests on the same agreement.
229
+ *
230
+ * Three of the four disjuncts are reads of values this export already
231
+ * computed. Only the fourth costs anything, and it short-circuits: `||`
232
+ * evaluates it solely when the other three are false, i.e. only for an
233
+ * export that has nothing else to filter for.
234
+ *
235
+ * ## The two spellings that are deliberately NOT the obvious ones
236
+ *
237
+ * `allowedEntityIds !== null`, not `options.visibleOnly === true`. Not the
238
+ * same test: the closure is built under `if (options.visibleOnly &&
239
+ * ctx.dataStore.source)` in `step-collection.ts` — quoted rather than cited
240
+ * by line, because the line moved by three in the very commit that added
241
+ * this reference — which is TRUTHY rather than `=== true`, and which
242
+ * is a SECOND read of the caller's object. A plain-JS caller of this
243
+ * published package passing `visibleOnly: 1` — or a `get visibleOnly()` that
244
+ * answers `true` once — built the closure while the gate read false and
245
+ * shipped a relationship naming an entity outside it. Executed, not
246
+ * reasoned: 192 of an 800-case sweep over `visibleOnly`/`hidden`/`isolated`
247
+ * combinations shipped a dangling ref against the `=== true` spelling, 0
248
+ * against this one. Reading the state the walk PRODUCED cannot disagree with
249
+ * the walk, whatever `options` says afterwards.
250
+ *
251
+ * It is also wider than the `hiddenProductIds.size > 0` the old gate used: a
252
+ * closure exists whenever `visibleOnly` was requested, even with nothing
253
+ * hidden, and can exclude an entity the roots simply never reach. No fixture
254
+ * has produced that case, so the widening is defensive — but a gate that is
255
+ * true too often costs speed on a rare path, while one that is false too
256
+ * rarely ships a corrupt file, and this one costs nothing.
257
+ *
258
+ * `overlayActive` and `excludeGeometry` are the SAME consts the effective
259
+ * index and `isGeometryExcluded` are built from — one read of `options` per
260
+ * question, shared — so those two cannot disagree with the predicate either.
261
+ */
262
+ const mayNameOmittedRefs = pass.allowedEntityIds !== null
263
+ || pass.overlayActive
264
+ || excludeGeometry
265
+ || hasAnyUnreadableSourceRef();
266
+ return { kind: 'continue', isOmittedFromOutput, mayNameOmittedRefs };
267
+ }
268
+ //# sourceMappingURL=step-omission-predicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-omission-predicates.js","sourceRoot":"","sources":["../src/step-omission-predicates.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAqD/D,MAAM,UAAU,0BAA0B,CACxC,IAAgB,EAChB,OAA0B,EAC1B,cAAuB,EACvB,eAAwB,EACxB,YAAwC;IAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwDG;IACH,MAAM,yBAAyB,GAAG,GAAY,EAAE;QAC9C,KAAK,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QAClD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,yEAAyE;IACzE,wEAAwE;IACxE,6BAA6B;IAC7B,MAAM,qBAAqB,GAAG,CAC5B,YAAY;WACT,cAAc;WACd,OAAO,YAAY,CAAC,cAAc,KAAK,UAAU,CACrD,CAAC,CAAC,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,2EAA2E;IAC3E,0CAA0C;IAC1C;IACE,sEAAsE;IACtE,wEAAwE;IACxE,yEAAyE;IACzE,oEAAoE;IACpE,uCAAuC;IACvC,OAAO,CAAC,SAAS,KAAK,IAAI;WACvB,IAAI,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC;WAChC,qBAAqB,KAAK,CAAC;WAC3B,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EACnC,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,qCAAqC,CAAC,CAAC;QAC3G,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE;gBACN,OAAO,EAAE,YAAY;gBACrB,KAAK,EAAE;oBACL,WAAW,EAAE,CAAC;oBACd,cAAc,EAAE,CAAC;oBACjB,mBAAmB,EAAE,CAAC;oBACtB,QAAQ,EAAE,YAAY,CAAC,UAAU;oBACjC,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB;aACF;SACF,CAAC;IACJ,CAAC;IAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+EG;IACH,MAAM,mBAAmB,GAAG,CAAC,EAAU,EAAW,EAAE,CAClD,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8EG;IACH,MAAM,kBAAkB,GACtB,IAAI,CAAC,gBAAgB,KAAK,IAAI;WAC3B,IAAI,CAAC,aAAa;WAClB,eAAe;WACf,yBAAyB,EAAE,CAAC;IACjC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;AACvE,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Apply overlay attribute + positional overrides to an OVERLAY-CREATED
3
+ * entity's argument list (#2006) — the sibling of `step-attribute-
4
+ * mutations.ts`'s source-buffer pipeline that shares its two serialize
5
+ * helpers (`step-attribute-serializers.ts`). Split out of
6
+ * `step-attribute-mutations.ts` (#3184).
7
+ */
8
+ import type { IfcAttributeValue } from '@ifc-lite/parser';
9
+ import type { IfcSchemaVersion } from './schema-converter.js';
10
+ /**
11
+ * Apply overlay attribute + positional overrides to an OVERLAY-CREATED
12
+ * entity's argument list (#2006).
13
+ *
14
+ * Distinct from {@link applyAttributeMutations} / {@link applyPositionalMutations},
15
+ * which rewrite a line read out of the source buffer. Here the whole line is
16
+ * ours: it was serialized moments ago from the creation payload, so the
17
+ * argument list is the authoring payload's, not the file's. That difference
18
+ * is why this PADS — `entity_create` takes whatever positional list the
19
+ * caller passes, so a wall authored with three arguments still has a real
20
+ * `Tag` slot at index 7, and dropping the edit because the payload was short
21
+ * would be the very data loss this fixes. The source-buffer path must not
22
+ * pad: there a short line means a different schema, and growing a record we
23
+ * did not author would corrupt it.
24
+ *
25
+ * Named and positional overrides resolve to a slot index up front and share
26
+ * ONE padding rule. Two padding rules on one record is how the next bug
27
+ * starts, and the argument for padding — the class is fixed at creation time,
28
+ * so a short payload is partial authoring — never depended on which of the
29
+ * two APIs queued the edit.
30
+ */
31
+ export declare function applyOverlayEntityOverrides(argsText: string, entityType: string, attributeOverrides: Map<string, string> | null, positionalOverrides: Map<number, IfcAttributeValue> | null, schemaVersion: IfcSchemaVersion, onRejected?: (attrName: string, value: string) => void): string;
32
+ //# sourceMappingURL=step-overlay-attribute-overrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-overlay-attribute-overrides.d.ts","sourceRoot":"","sources":["../src/step-overlay-attribute-overrides.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAK1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EAC9C,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,EAC1D,aAAa,EAAE,gBAAgB,EAC/B,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACrD,MAAM,CAoER"}
@@ -0,0 +1,83 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { getAttributeNamesAcrossSchemas } from '@ifc-lite/parser';
5
+ import { splitTopLevelArgs } from './step-argument-parser.js';
6
+ import { getRealTypedSlots } from './attribute-real-slots.js';
7
+ import { serializeNamedAttribute, serializePositionalOverride } from './step-attribute-serializers.js';
8
+ /**
9
+ * Apply overlay attribute + positional overrides to an OVERLAY-CREATED
10
+ * entity's argument list (#2006).
11
+ *
12
+ * Distinct from {@link applyAttributeMutations} / {@link applyPositionalMutations},
13
+ * which rewrite a line read out of the source buffer. Here the whole line is
14
+ * ours: it was serialized moments ago from the creation payload, so the
15
+ * argument list is the authoring payload's, not the file's. That difference
16
+ * is why this PADS — `entity_create` takes whatever positional list the
17
+ * caller passes, so a wall authored with three arguments still has a real
18
+ * `Tag` slot at index 7, and dropping the edit because the payload was short
19
+ * would be the very data loss this fixes. The source-buffer path must not
20
+ * pad: there a short line means a different schema, and growing a record we
21
+ * did not author would corrupt it.
22
+ *
23
+ * Named and positional overrides resolve to a slot index up front and share
24
+ * ONE padding rule. Two padding rules on one record is how the next bug
25
+ * starts, and the argument for padding — the class is fixed at creation time,
26
+ * so a short payload is partial authoring — never depended on which of the
27
+ * two APIs queued the edit.
28
+ */
29
+ export function applyOverlayEntityOverrides(argsText, entityType, attributeOverrides, positionalOverrides, schemaVersion, onRejected) {
30
+ const args = argsText.length > 0 ? splitTopLevelArgs(argsText) : [];
31
+ const attrNames = getAttributeNamesAcrossSchemas(entityType);
32
+ const named = [];
33
+ for (const [attrName, value] of attributeOverrides ?? []) {
34
+ const index = attrNames.indexOf(attrName);
35
+ if (index >= 0)
36
+ named.push([index, value]);
37
+ }
38
+ // Grow to the class's FULL declared arity as soon as any override names a
39
+ // declared slot the creation payload never reached. Growing only as far as
40
+ // the edited slot would emit eight arguments for an IfcWall that declares
41
+ // nine: this parser tolerates the truncated record, a schema-validating
42
+ // consumer rejects the file.
43
+ //
44
+ // An index PAST the declared layout is not a slot at all, so it cannot
45
+ // justify growing the record and stays dropped — as does any override on a
46
+ // class neither schema source knows, where there is no arity to grow to.
47
+ let needsPad = named.some(([index]) => index >= args.length);
48
+ if (!needsPad && positionalOverrides) {
49
+ for (const [index] of positionalOverrides) {
50
+ if (index >= args.length && index < attrNames.length) {
51
+ needsPad = true;
52
+ break;
53
+ }
54
+ }
55
+ }
56
+ if (needsPad) {
57
+ while (args.length < attrNames.length)
58
+ args.push('$');
59
+ }
60
+ // Every `named` index is < attrNames.length by construction, and padding
61
+ // has taken args.length to at least that, so each one lands.
62
+ const realSlots = getRealTypedSlots(entityType, schemaVersion);
63
+ for (const [index, value] of named) {
64
+ const serialized = serializeNamedAttribute(entityType, index, value, args[index], realSlots);
65
+ // Overlay-created entities take the same rejection: a non-numeric REAL is
66
+ // invalid STEP whoever authored the record. The slot keeps the `$` this
67
+ // path padded it with, rather than gaining a quoted string.
68
+ if (serialized === null) {
69
+ onRejected?.(attrNames[index] ?? `#${index}`, value);
70
+ continue;
71
+ }
72
+ args[index] = serialized;
73
+ }
74
+ if (positionalOverrides && positionalOverrides.size > 0) {
75
+ for (const [index, value] of positionalOverrides) {
76
+ if (index < 0 || index >= args.length)
77
+ continue;
78
+ args[index] = serializePositionalOverride(entityType, index, value, args[index], realSlots, schemaVersion);
79
+ }
80
+ }
81
+ return args.join(',');
82
+ }
83
+ //# sourceMappingURL=step-overlay-attribute-overrides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-overlay-attribute-overrides.js","sourceRoot":"","sources":["../src/step-overlay-attribute-overrides.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The overlay-created-entities phase of `StepExporter.export()` (#2475 step
3
+ * 2e): the pass that writes one line per entity the OVERLAY created
4
+ * (`store.addEntity` / `mutationView.createEntity`), as opposed to
5
+ * `step-source-iteration.ts`, which writes lines for records the SOURCE file
6
+ * already holds.
7
+ *
8
+ * It runs last among the four output passes — after georeferencing
9
+ * (`step-georeferencing.ts`), property/quantity-set generation
10
+ * (`step-property-sets.ts`) and source iteration — and reads what two of them
11
+ * wrote: `pass.modifiedAttributes` (attribute-collection phase, applied here
12
+ * as an overlay-created entity's post-create edits) and
13
+ * `pass.overlayTypeOwnedPsets` (`step-property-sets.ts`, a created TYPE
14
+ * object's generated pset ids, arriving as one more positional override).
15
+ * Nothing downstream of this phase reads anything it writes except the
16
+ * settle/assemble tail, so it moves cleanly.
17
+ *
18
+ * What is injected rather than read off the pass, and why: `isGeometryEntity`
19
+ * is shared with the setup closure and with `step-source-iteration.ts`.
20
+ * `relationshipWithheldWarning` is the same pure string formatter
21
+ * `step-source-iteration.ts` already injects, not duplicated here.
22
+ * `applyOverlayEntityOverrides` is injected as `OverlayEntitiesContext`'s
23
+ * field even though, since #2475's "remaining private helpers" step, it is
24
+ * an ordinary export of `step-overlay-attribute-overrides.ts` this file could
25
+ * import directly — kept as an injected field for symmetry with the other two and
26
+ * because `StepExporter.overlayEntitiesContext()` is still the one
27
+ * construction site for this context, exactly as before that step.
28
+ *
29
+ * `mutationView` is passed through as-is (not narrowed to an interface of the
30
+ * three optional methods this phase calls) because the original code guards
31
+ * every one of them with its own `typeof x === 'function'` check rather than
32
+ * assuming the shape — a real-store `MutablePropertyView` versus a bare mock
33
+ * used by some tests. Narrowing the type here would not remove those checks
34
+ * without also changing what a test double is allowed to omit.
35
+ */
36
+ import type { IfcAttributeValue } from '@ifc-lite/parser';
37
+ import type { MutablePropertyView } from '@ifc-lite/mutations';
38
+ import type { ExportPass, StepExportOptions } from './step-exporter.js';
39
+ /**
40
+ * The exporter state this phase cannot read off the {@link ExportPass}.
41
+ */
42
+ export interface OverlayEntitiesContext {
43
+ readonly mutationView: MutablePropertyView | null;
44
+ /** `step-overlay-attribute-overrides.ts`'s `applyOverlayEntityOverrides`: resolves
45
+ * named/positional overrides against the overlay-created entity's
46
+ * EFFECTIVE class. */
47
+ readonly applyOverlayEntityOverrides: (argsText: string, entityType: string, attributeOverrides: Map<string, string> | null, positionalOverrides: Map<number, IfcAttributeValue> | null, schemaVersion: ExportPass['sourceSchema'], onRejected?: (attrName: string, value: string) => void) => string;
48
+ /** `isGeometryEntity` (`step-geometry-types.ts`), also read by the setup closure and by
49
+ * `step-source-iteration.ts`. */
50
+ readonly isGeometryEntity: (type: string) => boolean;
51
+ /** The `relationshipWithheldWarning` message builder (`step-exporter.ts`):
52
+ * a pure string formatter with no pass dependency, injected here as it is
53
+ * into `step-source-iteration.ts`. */
54
+ readonly relationshipWithheldWarning: (expressId: number, type: string) => string;
55
+ }
56
+ /**
57
+ * Add overlay-created entities (store.addEntity / mutationView.createEntity).
58
+ * Apply the same filters as the source-iteration pass so newly-created
59
+ * beams/slabs don't smuggle their geometry helpers (IfcCartesianPoint,
60
+ * IfcExtrudedAreaSolid, etc.) past `includeGeometry:false` /
61
+ * `exportPropertiesOnly()` modes.
62
+ */
63
+ export declare function writeOverlayCreatedEntities(pass: ExportPass, options: StepExportOptions, excludeGeometry: boolean, applyMutations: boolean, mayNameOmittedRefs: boolean, isOmittedFromOutput: (id: number) => boolean, ctx: OverlayEntitiesContext): void;
64
+ //# sourceMappingURL=step-overlay-entities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-overlay-entities.d.ts","sourceRoot":"","sources":["../src/step-overlay-entities.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAM/D,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClD;;2BAEuB;IACvB,QAAQ,CAAC,2BAA2B,EAAE,CACpC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EAC9C,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,EAC1D,aAAa,EAAE,UAAU,CAAC,cAAc,CAAC,EACzC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,KACnD,MAAM,CAAC;IACZ;sCACkC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD;;2CAEuC;IACvC,QAAQ,CAAC,2BAA2B,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnF;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,iBAAiB,EAC1B,eAAe,EAAE,OAAO,EACxB,cAAc,EAAE,OAAO,EACvB,kBAAkB,EAAE,OAAO,EAC3B,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,EAC5C,GAAG,EAAE,sBAAsB,GAC1B,IAAI,CA4HN"}
@@ -0,0 +1,119 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { filterHiddenRefsFromRelationshipLine } from './reference-collector.js';
5
+ import { convertStepLine } from './schema-converter.js';
6
+ import { retypeArgTokens } from './retype.js';
7
+ import { HAS_PROPERTY_SETS_SLOT } from './type-owned-psets.js';
8
+ import { serializeEntityArgs, serializeAttributeSlot } from './attribute-real-slots.js';
9
+ /**
10
+ * Add overlay-created entities (store.addEntity / mutationView.createEntity).
11
+ * Apply the same filters as the source-iteration pass so newly-created
12
+ * beams/slabs don't smuggle their geometry helpers (IfcCartesianPoint,
13
+ * IfcExtrudedAreaSolid, etc.) past `includeGeometry:false` /
14
+ * `exportPropertiesOnly()` modes.
15
+ */
16
+ export function writeOverlayCreatedEntities(pass, options, excludeGeometry, applyMutations, mayNameOmittedRefs, isOmittedFromOutput, ctx) {
17
+ if (!ctx.mutationView
18
+ || !applyMutations
19
+ || typeof ctx.mutationView.getNewEntities !== 'function') {
20
+ return;
21
+ }
22
+ const getTypeMut = typeof ctx.mutationView.getEntityTypeMutation === 'function'
23
+ ? ctx.mutationView.getEntityTypeMutation.bind(ctx.mutationView)
24
+ : null;
25
+ for (const entity of ctx.mutationView.getNewEntities()) {
26
+ // A retyped overlay entity keeps its AUTHORED type on `entity.type`
27
+ // (the overlay typeMutation is the source of truth for the effective
28
+ // class). Resolve the effective class, then re-lay-out the authored
29
+ // attributes from the authored layout up to it.
30
+ const typeMut = getTypeMut ? getTypeMut(entity.expressId) : null;
31
+ const effectiveType = typeMut?.newType ?? entity.type;
32
+ // STEP requires UPPERCASE entity type tokens; the upper-case happens
33
+ // here at the file-format boundary.
34
+ const upperType = effectiveType.toUpperCase();
35
+ if (excludeGeometry && ctx.isGeometryEntity(upperType)) {
36
+ continue;
37
+ }
38
+ if (pass.allowedEntityIds !== null && !pass.allowedEntityIds.has(entity.expressId)) {
39
+ continue;
40
+ }
41
+ // Re-lay-out by name against the effective class (identity for
42
+ // compatible layouts). Runs whenever a retype intent exists — even a
43
+ // same-class retype, which carries a PredefinedType override
44
+ // (e.g. setEntityType(id, 'IfcColumn', 'PILASTER')).
45
+ let argsText;
46
+ if (typeMut) {
47
+ // Serialize against the AUTHORED layout (`entity.type`); retypeArgTokens
48
+ // then re-lays the tokens out by name up to the effective class.
49
+ const srcTokens = entity.attributes.map((value, i) => serializeAttributeSlot(entity.type, i, value, pass.sourceSchema));
50
+ const { tokens } = retypeArgTokens(srcTokens, entity.type, effectiveType, typeMut.predefinedType ?? null, pass.sourceSchema);
51
+ argsText = tokens.join(',');
52
+ }
53
+ else {
54
+ argsText = serializeEntityArgs(entity.type, entity.attributes, pass.sourceSchema);
55
+ }
56
+ // Edits made AFTER the create live in the overlay, never in the
57
+ // authored payload (#2006). The source-iteration pass applies them to
58
+ // source records via applyAttributeMutations / applyPositionalMutations;
59
+ // an overlay-created entity has no source record, so without this it was
60
+ // written from its creation payload alone and every later
61
+ // `setAttribute` / `setPositionalAttribute` was silently dropped on
62
+ // save — data loss with no error and no warning.
63
+ //
64
+ // Order mirrors the source pass: retype (above) -> named attributes ->
65
+ // positional overrides, all resolved against the EFFECTIVE class.
66
+ const attributeOverrides = pass.modifiedAttributes.get(entity.expressId) ?? null;
67
+ const queuedPositional = typeof ctx.mutationView.getPositionalMutationsForEntity === 'function'
68
+ ? ctx.mutationView.getPositionalMutationsForEntity(entity.expressId)
69
+ : null;
70
+ // A created TYPE object owns its psets through HasPropertySets, and the
71
+ // ids of the psets this export generated are only known now — so they
72
+ // arrive as one more slot override rather than through the overlay.
73
+ // `has`, not `??`, for the same reason `overlaySlotValue` gives: the
74
+ // stored value is deliberately null when the resolved list is empty.
75
+ const positionalOverrides = pass.overlayTypeOwnedPsets.has(entity.expressId)
76
+ ? new Map(queuedPositional).set(HAS_PROPERTY_SETS_SLOT, pass.overlayTypeOwnedPsets.get(entity.expressId) ?? null)
77
+ : queuedPositional;
78
+ if ((attributeOverrides && attributeOverrides.size > 0)
79
+ || (positionalOverrides && positionalOverrides.size > 0)) {
80
+ argsText = ctx.applyOverlayEntityOverrides(argsText, upperType, attributeOverrides, positionalOverrides, pass.sourceSchema,
81
+ // Overlay-created entities report a rejected REAL edit exactly as
82
+ // source-backed ones do. Without this the slot was kept and NOTHING
83
+ // was said - the silent discard this whole change exists to
84
+ // prevent, surviving in the one path that had no test.
85
+ (attr, value) => pass.warnings.push(`entity #${entity.expressId}: attribute ${attr} not written - ` +
86
+ `${JSON.stringify(value)} is not a number and the slot is REAL-typed`));
87
+ }
88
+ let line = `#${entity.expressId}=${upperType}(${argsText});`;
89
+ // Same gap as the source-iteration pass, for an overlay-authored
90
+ // relationship instead of a parsed one (#2398).
91
+ //
92
+ // `mayNameOmittedRefs` is provably TRUE wherever this line executes:
93
+ // the block enclosing this pass requires `ctx.mutationView` and
94
+ // `applyMutations`, which is `pass.overlayActive`, which is one of the
95
+ // gate's own disjuncts. Spelled out anyway so both filter sites read the
96
+ // same — the previous gate's failure was one site's condition drifting
97
+ // from what the filter needed, and a pass reachable without an overlay
98
+ // would otherwise silently need the gate re-derived here.
99
+ if (mayNameOmittedRefs && upperType.startsWith('IFCREL')) {
100
+ line = filterHiddenRefsFromRelationshipLine(line, isOmittedFromOutput);
101
+ if (line === null) {
102
+ pass.warnings.push(ctx.relationshipWithheldWarning(entity.expressId, upperType));
103
+ continue;
104
+ }
105
+ }
106
+ if (pass.converting) {
107
+ const converted = convertStepLine(line, pass.sourceSchema, pass.schema, options.guidRandom);
108
+ if (converted !== null) {
109
+ pass.entities.push(converted);
110
+ pass.newEntityCount++;
111
+ }
112
+ }
113
+ else {
114
+ pass.entities.push(line);
115
+ pass.newEntityCount++;
116
+ }
117
+ }
118
+ }
119
+ //# sourceMappingURL=step-overlay-entities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-overlay-entities.js","sourceRoot":"","sources":["../src/step-overlay-entities.ts"],"names":[],"mappings":"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"}