@ifc-lite/export 3.0.0 → 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 (97) hide show
  1. package/dist/declared-property-type.d.ts +19 -8
  2. package/dist/declared-property-type.d.ts.map +1 -1
  3. package/dist/declared-property-type.js +31 -9
  4. package/dist/declared-property-type.js.map +1 -1
  5. package/dist/merged-exporter.d.ts +19 -0
  6. package/dist/merged-exporter.d.ts.map +1 -1
  7. package/dist/merged-exporter.js +1 -1
  8. package/dist/merged-exporter.js.map +1 -1
  9. package/dist/parquet-exporter.d.ts +1 -3
  10. package/dist/parquet-exporter.d.ts.map +1 -1
  11. package/dist/parquet-exporter.js +1 -0
  12. package/dist/parquet-exporter.js.map +1 -1
  13. package/dist/property-value-serialization.d.ts +54 -0
  14. package/dist/property-value-serialization.d.ts.map +1 -0
  15. package/dist/property-value-serialization.js +134 -0
  16. package/dist/property-value-serialization.js.map +1 -0
  17. package/dist/reference-collector.d.ts +20 -0
  18. package/dist/reference-collector.d.ts.map +1 -1
  19. package/dist/reference-collector.js +45 -112
  20. package/dist/reference-collector.js.map +1 -1
  21. package/dist/schema-converter.d.ts.map +1 -1
  22. package/dist/schema-converter.js +46 -96
  23. package/dist/schema-converter.js.map +1 -1
  24. package/dist/source-ref-bounds.d.ts +3 -3
  25. package/dist/source-ref-bounds.js +3 -3
  26. package/dist/step-attribute-mutations.d.ts +0 -42
  27. package/dist/step-attribute-mutations.d.ts.map +1 -1
  28. package/dist/step-attribute-mutations.js +17 -147
  29. package/dist/step-attribute-mutations.js.map +1 -1
  30. package/dist/step-attribute-serializers.d.ts +41 -0
  31. package/dist/step-attribute-serializers.d.ts.map +1 -0
  32. package/dist/step-attribute-serializers.js +74 -0
  33. package/dist/step-attribute-serializers.js.map +1 -0
  34. package/dist/step-collection.d.ts +2 -2
  35. package/dist/step-collection.d.ts.map +1 -1
  36. package/dist/step-collection.js +2 -1
  37. package/dist/step-collection.js.map +1 -1
  38. package/dist/step-export-contexts.d.ts +5 -7
  39. package/dist/step-export-contexts.d.ts.map +1 -1
  40. package/dist/step-export-contexts.js +6 -7
  41. package/dist/step-export-contexts.js.map +1 -1
  42. package/dist/step-exporter.d.ts +3 -3
  43. package/dist/step-exporter.js +4 -4
  44. package/dist/step-exporter.js.map +1 -1
  45. package/dist/step-geometry-types.d.ts +1 -17
  46. package/dist/step-geometry-types.d.ts.map +1 -1
  47. package/dist/step-geometry-types.js +51 -35
  48. package/dist/step-geometry-types.js.map +1 -1
  49. package/dist/step-georeferencing.d.ts +0 -10
  50. package/dist/step-georeferencing.d.ts.map +1 -1
  51. package/dist/step-georeferencing.js +46 -94
  52. package/dist/step-georeferencing.js.map +1 -1
  53. package/dist/step-header.d.ts +11 -1
  54. package/dist/step-header.d.ts.map +1 -1
  55. package/dist/step-header.js +0 -7
  56. package/dist/step-header.js.map +1 -1
  57. package/dist/step-map-unit.d.ts +90 -0
  58. package/dist/step-map-unit.d.ts.map +1 -0
  59. package/dist/step-map-unit.js +277 -0
  60. package/dist/step-map-unit.js.map +1 -0
  61. package/dist/step-overlay-attribute-overrides.d.ts +32 -0
  62. package/dist/step-overlay-attribute-overrides.d.ts.map +1 -0
  63. package/dist/step-overlay-attribute-overrides.js +83 -0
  64. package/dist/step-overlay-attribute-overrides.js.map +1 -0
  65. package/dist/step-overlay-entities.d.ts +3 -3
  66. package/dist/step-pass-builder.d.ts +4 -3
  67. package/dist/step-pass-builder.d.ts.map +1 -1
  68. package/dist/step-pass-builder.js.map +1 -1
  69. package/dist/step-property-set-collection.d.ts +24 -0
  70. package/dist/step-property-set-collection.d.ts.map +1 -0
  71. package/dist/step-property-set-collection.js +286 -0
  72. package/dist/step-property-set-collection.js.map +1 -0
  73. package/dist/step-property-set-generators.d.ts +20 -0
  74. package/dist/step-property-set-generators.d.ts.map +1 -0
  75. package/dist/step-property-set-generators.js +98 -0
  76. package/dist/step-property-set-generators.js.map +1 -0
  77. package/dist/step-property-set-index.d.ts +25 -0
  78. package/dist/step-property-set-index.d.ts.map +1 -0
  79. package/dist/step-property-set-index.js +69 -0
  80. package/dist/step-property-set-index.js.map +1 -0
  81. package/dist/step-property-set-readers.d.ts +158 -0
  82. package/dist/step-property-set-readers.d.ts.map +1 -0
  83. package/dist/step-property-set-readers.js +251 -0
  84. package/dist/step-property-set-readers.js.map +1 -0
  85. package/dist/step-property-sets.d.ts +2 -186
  86. package/dist/step-property-sets.d.ts.map +1 -1
  87. package/dist/step-property-sets.js +22 -674
  88. package/dist/step-property-sets.js.map +1 -1
  89. package/dist/step-serialization.d.ts +17 -51
  90. package/dist/step-serialization.d.ts.map +1 -1
  91. package/dist/step-serialization.js +19 -128
  92. package/dist/step-serialization.js.map +1 -1
  93. package/dist/step-source-iteration.d.ts +7 -5
  94. package/dist/step-source-iteration.d.ts.map +1 -1
  95. package/dist/step-source-iteration.js +3 -3
  96. package/dist/step-source-iteration.js.map +1 -1
  97. package/package.json +6 -6
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Read-side state and helpers for the property-set and quantity-set phases
3
+ * of `StepExporter.export()` (#2475 steps 2b and 2c), split out of
4
+ * `step-property-sets.ts` (#3184): the {@link PropertySetContext} the
5
+ * generation/collection phases run against, the per-entity STEP-text
6
+ * readers those phases share (`entityLineText` and everything parsed from
7
+ * it), and the owner-history resolution the generators call for every
8
+ * record they emit.
9
+ *
10
+ * `isReadableSourceRef` on {@link PropertySetContext} is the exporter's OWN
11
+ * reader rather than the pass's: these readers are also reached from
12
+ * `buildRelDefinesByPropertiesIndex` (`step-property-set-index.ts`) and from
13
+ * `retainSharedAtoms`, neither of which has a pass in hand.
14
+ */
15
+ import type { IfcDataStore, EntityExtractor } from '@ifc-lite/parser';
16
+ import type { MutablePropertyView } from '@ifc-lite/mutations';
17
+ import { type RandomSource } from '@ifc-lite/encoding';
18
+ import { createSourceRefReader } from './source-ref-bounds.js';
19
+ import { type EffectiveEntityIndex } from './effective-index.js';
20
+ import type { IfcSchemaVersion } from './schema-converter.js';
21
+ import type { SourceLineMutations } from './step-exporter.js';
22
+ /**
23
+ * The exporter state these phases cannot read off the {@link ExportPass}.
24
+ *
25
+ * `isReadableSourceRef` is the exporter's OWN reader rather than the pass's:
26
+ * the byte readers below are also reached from
27
+ * {@link buildRelDefinesByPropertiesIndex} and from `retainSharedAtoms`,
28
+ * neither of which has a pass in hand. Both readers are built by
29
+ * `createSourceRefReader` over the same `dataStore.source`.
30
+ */
31
+ export interface PropertySetContext {
32
+ readonly dataStore: IfcDataStore;
33
+ readonly entityExtractor: EntityExtractor | null;
34
+ readonly mutationView: MutablePropertyView | null;
35
+ readonly isReadableSourceRef: ReturnType<typeof createSourceRefReader>;
36
+ /** `() => this.nextExpressId++` on the exporter. */
37
+ readonly allocateExpressId: () => number;
38
+ readonly ownerHistory: OwnerHistoryCache;
39
+ /** `step-attribute-mutations.ts`'s `applySourceLineMutations`: the ONE
40
+ * pipeline the source-iteration pass and the type-object rewrite share, so
41
+ * it belongs to neither phase and is injected. */
42
+ readonly applySourceLineMutations: (expressId: number, entityText: string, recordType: string, attributeMutations: Map<string, string> | undefined, sourceSchema: IfcSchemaVersion, overlayActive: boolean, onRejected?: (attrName: string, value: string) => void) => SourceLineMutations;
43
+ }
44
+ /**
45
+ * The two owner-history memos the generators share, in one object so the
46
+ * exporter can hand them over by reference. Both are per-EXPORT rather than
47
+ * per-exporter; `StepExporter.export()` owns the reset and explains why.
48
+ */
49
+ export interface OwnerHistoryCache {
50
+ /** Lazily-resolved fallback `#id` of an IfcOwnerHistory that survives the
51
+ * current export closure (or `$` when the file has none). */
52
+ fallbackRef: string | undefined;
53
+ /** Per-host cache of an element's own OwnerHistory ref (`#id` or null). */
54
+ readonly byEntity: Map<number, string | null>;
55
+ }
56
+ /**
57
+ * The source STEP text of an entity's line, or `null` when there are no bytes
58
+ * to read.
59
+ *
60
+ * The byte check is on the RANGE, not on `dataStore.source`. `source` is a
61
+ * MANDATORY accessor — `EMPTY_SOURCE_BYTES` is how "this model kept no bytes"
62
+ * is spelled (server-parsed, synthetic, GLB and point-cloud stores all have
63
+ * one) — so the `!ctx.dataStore.source` guard the five readers below used to
64
+ * carry never fired. It was also redundant: a zero-length range decodes to
65
+ * `''`, which fails every regex those readers run, so they already answered
66
+ * "nothing" for a sourceless store. Scoping the check to the range is what
67
+ * makes the guard live without changing a single answer, the same shape and
68
+ * for the same reason as `reference-collector.ts` (#2339).
69
+ *
70
+ * An OVERLAY-created entity never reaches here: every caller resolves its id
71
+ * through `dataStore.entityIndex.byId`, which holds source records only
72
+ * (`effective-index.ts` synthesises the overlay refs on its own side and
73
+ * writes nothing back), so an overlay id is already `undefined` at the
74
+ * lookup and is served by the callers' documented "not a source record"
75
+ * path. That is why an early return is safe HERE and is NOT safe at the
76
+ * visible-only closure in `export` — see the comment there.
77
+ *
78
+ * ## Why `isReadableSourceRef` and not `byteLength === 0`
79
+ *
80
+ * An out-of-range ref does NOT degrade to "no match" here. `decodeUtf8`
81
+ * clamps the range it cannot address, and the clamped window is still a
82
+ * window over real file bytes — so these readers answer from somebody
83
+ * ELSE's record. `source-ref-bounds.ts` (#2491) carries the measured
84
+ * account of both shapes and of why "a clamped, empty decode already yields
85
+ * no match" is false; it is not restated here, because an argument kept in
86
+ * two files is an argument that has to stay true in two files.
87
+ *
88
+ * The consequence specific to THIS site is that the wrong answer is acted
89
+ * on. `retainSharedAtoms` un-skips every id `getPropertyIdsInSet` returns,
90
+ * so a member list read out of the wrong record un-skips the wrong atoms;
91
+ * and the source-iteration pass already refuses to emit a record whose ref
92
+ * fails `isReadableSourceRef` (see the `continue` in `export`), so before
93
+ * this gate these readers were making decisions on behalf of a container
94
+ * that the same export had decided not to write. Gating them on the same
95
+ * predicate is what makes the two passes agree.
96
+ *
97
+ * The degradation is the one the exporter already handles: a record with no
98
+ * emittable bytes, generating nothing and named by nothing. It costs one
99
+ * answer that used to be right by luck — an overrunning ref on the file's
100
+ * LAST record clamps back to exactly that record's text — but that record
101
+ * is one the emission pass drops anyway, so keeping the answer only kept
102
+ * the disagreement.
103
+ */
104
+ export declare function entityLineText(ctx: PropertySetContext, entityId: number): string | null;
105
+ /**
106
+ * Get the name of a property set by parsing the entity
107
+ */
108
+ export declare function getPropertySetName(ctx: PropertySetContext, psetId: number): string | null;
109
+ /**
110
+ * Get the name of an element quantity set by parsing the entity
111
+ */
112
+ export declare function getElementQuantityName(ctx: PropertySetContext, entityId: number): string | null;
113
+ /**
114
+ * Get IDs of properties in a property set
115
+ */
116
+ export declare function getPropertyIdsInSet(ctx: PropertySetContext, psetId: number): number[];
117
+ /**
118
+ * The full HasPropertySets id list of a type object, from whichever authority
119
+ * owns the record.
120
+ *
121
+ * Slot 5 is `HasPropertySets` on every `IfcTypeObject` subtype. For a source
122
+ * record the list is parsed out of the file; for an overlay-created type it is
123
+ * read off the authored payload, where a reference is the documented `'#42'`
124
+ * string form. Reading only the source made every pset on a created
125
+ * `IfcWallType` look unowned, which is how it ended up on an occurrence
126
+ * relation instead (#2012).
127
+ */
128
+ export declare function getTypeOwnedHasPropertySetIds(ctx: PropertySetContext, entityId: number, effective: EffectiveEntityIndex): number[];
129
+ /**
130
+ * Resolve a STEP reference to an existing IfcOwnerHistory for the
131
+ * IfcPropertySet / IfcRelDefinesByProperties / IfcElementQuantity entities we
132
+ * generate for `hostEntityId`'s mutations. OwnerHistory is optional in IFC4 but
133
+ * MANDATORY in IFC2X3 (IfcRoot.OwnerHistory), so emitting `$` yields an invalid
134
+ * IFC2X3 file that strict readers (e.g. BIM Vision) reject.
135
+ *
136
+ * Prefer the host element's OWN owner history, then any owner history that
137
+ * survives this export, then `$` only when none does.
138
+ *
139
+ * "Survives" is `willBeEmitted`, the same predicate that decides whether the
140
+ * host itself may have psets generated for it. A reference is a reference: it
141
+ * is no more acceptable to point an emitted `IfcPropertySet` at an owner
142
+ * history the session deleted than at a host it deleted. This used to consult
143
+ * only the `visibleOnly` closure, so an overlay-created OwnerHistory that was
144
+ * later deleted still got referenced — a dangling `#N`, reached through the
145
+ * one attribute the generators fill in for themselves.
146
+ */
147
+ export declare function resolveOwnerHistoryRef(ctx: PropertySetContext, hostEntityId: number, willBeEmitted: (id: number) => boolean): string;
148
+ /**
149
+ * Generate a new IFC GlobalId (22 character base64). `random` is the
150
+ * export's optional seeded source (`StepExportOptions.guidRandom`);
151
+ * undefined keeps the default random path.
152
+ */
153
+ export declare function generateGlobalId(random?: RandomSource): string;
154
+ /**
155
+ * Find a unit entity ID by name (simplified - returns null for now)
156
+ */
157
+ export declare function findUnitId(ctx: PropertySetContext, unitName: string, effective: EffectiveEntityIndex): number | null;
158
+ //# sourceMappingURL=step-property-set-readers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-property-set-readers.d.ts","sourceRoot":"","sources":["../src/step-property-set-readers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,251 @@
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 { generateIfcGuid } from '@ifc-lite/encoding';
5
+ import { decodeRange } from './source-ref-bounds.js';
6
+ import { findLengthUnitReference, normalizeMapUnitName } from './step-map-unit.js';
7
+ import { authoredEntityRefs } from './effective-index.js';
8
+ import { HAS_PROPERTY_SETS_SLOT } from './type-owned-psets.js';
9
+ /** `OwnerHistory` is slot 1 on every `IfcRoot` subtype, all schemas. */
10
+ const OWNER_HISTORY_SLOT = 1;
11
+ /**
12
+ * The source STEP text of an entity's line, or `null` when there are no bytes
13
+ * to read.
14
+ *
15
+ * The byte check is on the RANGE, not on `dataStore.source`. `source` is a
16
+ * MANDATORY accessor — `EMPTY_SOURCE_BYTES` is how "this model kept no bytes"
17
+ * is spelled (server-parsed, synthetic, GLB and point-cloud stores all have
18
+ * one) — so the `!ctx.dataStore.source` guard the five readers below used to
19
+ * carry never fired. It was also redundant: a zero-length range decodes to
20
+ * `''`, which fails every regex those readers run, so they already answered
21
+ * "nothing" for a sourceless store. Scoping the check to the range is what
22
+ * makes the guard live without changing a single answer, the same shape and
23
+ * for the same reason as `reference-collector.ts` (#2339).
24
+ *
25
+ * An OVERLAY-created entity never reaches here: every caller resolves its id
26
+ * through `dataStore.entityIndex.byId`, which holds source records only
27
+ * (`effective-index.ts` synthesises the overlay refs on its own side and
28
+ * writes nothing back), so an overlay id is already `undefined` at the
29
+ * lookup and is served by the callers' documented "not a source record"
30
+ * path. That is why an early return is safe HERE and is NOT safe at the
31
+ * visible-only closure in `export` — see the comment there.
32
+ *
33
+ * ## Why `isReadableSourceRef` and not `byteLength === 0`
34
+ *
35
+ * An out-of-range ref does NOT degrade to "no match" here. `decodeUtf8`
36
+ * clamps the range it cannot address, and the clamped window is still a
37
+ * window over real file bytes — so these readers answer from somebody
38
+ * ELSE's record. `source-ref-bounds.ts` (#2491) carries the measured
39
+ * account of both shapes and of why "a clamped, empty decode already yields
40
+ * no match" is false; it is not restated here, because an argument kept in
41
+ * two files is an argument that has to stay true in two files.
42
+ *
43
+ * The consequence specific to THIS site is that the wrong answer is acted
44
+ * on. `retainSharedAtoms` un-skips every id `getPropertyIdsInSet` returns,
45
+ * so a member list read out of the wrong record un-skips the wrong atoms;
46
+ * and the source-iteration pass already refuses to emit a record whose ref
47
+ * fails `isReadableSourceRef` (see the `continue` in `export`), so before
48
+ * this gate these readers were making decisions on behalf of a container
49
+ * that the same export had decided not to write. Gating them on the same
50
+ * predicate is what makes the two passes agree.
51
+ *
52
+ * The degradation is the one the exporter already handles: a record with no
53
+ * emittable bytes, generating nothing and named by nothing. It costs one
54
+ * answer that used to be right by luck — an overrunning ref on the file's
55
+ * LAST record clamps back to exactly that record's text — but that record
56
+ * is one the emission pass drops anyway, so keeping the answer only kept
57
+ * the disagreement.
58
+ */
59
+ /*
60
+ * Exported for `entity-line-text-bounds.test.ts`, which is the only pin on
61
+ * where this range STARTS: every pattern the readers below run is unanchored
62
+ * or `$`-anchored, so no public export path can see the first byte move
63
+ * (#2497). That test used to reach the method by casting a `StepExporter` to
64
+ * an interface of its privates; an export is the same access, stated.
65
+ */
66
+ export function entityLineText(ctx, entityId) {
67
+ const entityRef = ctx.dataStore.entityIndex.byId.get(entityId);
68
+ if (!entityRef || !ctx.isReadableSourceRef(entityRef))
69
+ return null;
70
+ return decodeRange(ctx.dataStore.source, entityRef.byteOffset, entityRef.byteOffset + entityRef.byteLength);
71
+ }
72
+ /**
73
+ * Get the name of a property set by parsing the entity
74
+ */
75
+ export function getPropertySetName(ctx, psetId) {
76
+ const entityText = entityLineText(ctx, psetId);
77
+ if (entityText === null)
78
+ return null;
79
+ // Parse: IFCPROPERTYSET('guid',$,'Name',$,...) - Name is 3rd argument
80
+ const match = entityText.match(/IFCPROPERTYSET\s*\([^,]*,[^,]*,'([^']*)'/i);
81
+ if (!match)
82
+ return null;
83
+ return match[1];
84
+ }
85
+ /**
86
+ * Get the name of an element quantity set by parsing the entity
87
+ */
88
+ export function getElementQuantityName(ctx, entityId) {
89
+ const entityText = entityLineText(ctx, entityId);
90
+ if (entityText === null)
91
+ return null;
92
+ // Parse: IFCELEMENTQUANTITY('guid',$,'Name',...) - Name is 3rd argument
93
+ const match = entityText.match(/IFCELEMENTQUANTITY\s*\([^,]*,[^,]*,'([^']*)'/i);
94
+ if (!match)
95
+ return null;
96
+ return match[1];
97
+ }
98
+ /**
99
+ * Get IDs of properties in a property set
100
+ */
101
+ export function getPropertyIdsInSet(ctx, psetId) {
102
+ const entityText = entityLineText(ctx, psetId);
103
+ if (entityText === null)
104
+ return [];
105
+ // Parse: IFCPROPERTYSET(...,(#prop1,#prop2,...)); - Last argument is properties list
106
+ const match = entityText.match(/\(\s*(#[^)]+)\s*\)\s*\)\s*;$/);
107
+ if (!match)
108
+ return [];
109
+ const propsList = match[1];
110
+ const ids = [];
111
+ const refMatches = propsList.matchAll(/#(\d+)/g);
112
+ for (const m of refMatches) {
113
+ ids.push(parseInt(m[1], 10));
114
+ }
115
+ return ids;
116
+ }
117
+ /**
118
+ * The full HasPropertySets id list of a type object, from whichever authority
119
+ * owns the record.
120
+ *
121
+ * Slot 5 is `HasPropertySets` on every `IfcTypeObject` subtype. For a source
122
+ * record the list is parsed out of the file; for an overlay-created type it is
123
+ * read off the authored payload, where a reference is the documented `'#42'`
124
+ * string form. Reading only the source made every pset on a created
125
+ * `IfcWallType` look unowned, which is how it ended up on an occurrence
126
+ * relation instead (#2012).
127
+ */
128
+ export function getTypeOwnedHasPropertySetIds(ctx, entityId, effective) {
129
+ if (effective.isOverlayCreated(entityId)) {
130
+ const authored = ctx.mutationView?.getNewEntity(entityId)?.attributes?.[HAS_PROPERTY_SETS_SLOT];
131
+ return authoredEntityRefs(overlaySlotValue(ctx, entityId, HAS_PROPERTY_SETS_SLOT, authored));
132
+ }
133
+ if (!ctx.entityExtractor)
134
+ return [];
135
+ const entityRef = ctx.dataStore.entityIndex.byId.get(entityId);
136
+ if (!entityRef)
137
+ return [];
138
+ const entity = ctx.entityExtractor.extractEntity(entityRef);
139
+ const hasPropertySets = entity?.attributes?.[HAS_PROPERTY_SETS_SLOT];
140
+ if (!Array.isArray(hasPropertySets))
141
+ return [];
142
+ return hasPropertySets.filter((value) => typeof value === 'number');
143
+ }
144
+ /**
145
+ * The overlay's answer for one positional slot of an overlay-created entity,
146
+ * falling back to the creation payload only when the overlay has NOTHING to
147
+ * say about that slot.
148
+ *
149
+ * **Ask `Map.has`, never `??`.** `setPositionalAttribute(id, slot, null)` is
150
+ * an explicit "clear this slot", and its value is `null`, so `??` reads the
151
+ * overlay's answer as an absence and reinstates the authored one. That is the
152
+ * same overlay-versus-buffer confusion this whole change is about, one
153
+ * attribute wide: an explicit null IS the overlay's answer, and the overlay is
154
+ * the authority. Cleared OwnerHistory came back as the authored reference, and
155
+ * a cleared `HasPropertySets` resurrected the list the user had removed.
156
+ */
157
+ function overlaySlotValue(ctx, entityId, slot, authored) {
158
+ const overrides = ctx.mutationView?.getPositionalMutationsForEntity(entityId);
159
+ if (!overrides?.has(slot))
160
+ return authored;
161
+ const value = overrides.get(slot);
162
+ // `Map.get` widens to `| undefined`, which `has` has already ruled out. A
163
+ // slot explicitly set to nothing serializes as `$`, i.e. null.
164
+ return value === undefined ? null : value;
165
+ }
166
+ /**
167
+ * Read an element's own OwnerHistory reference (`#id`), or null when the
168
+ * element omits one (`$`) or cannot be parsed. OwnerHistory is the second
169
+ * attribute of every IfcRoot subtype, immediately after the GlobalId string.
170
+ */
171
+ function getOwnerHistoryRefOfEntity(ctx, entityId) {
172
+ const cached = ctx.ownerHistory.byEntity.get(entityId);
173
+ if (cached !== undefined)
174
+ return cached;
175
+ let result = null;
176
+ // An overlay-created host has no source line to read, but it does have an
177
+ // authored OwnerHistory in slot 1 — reading only the buffer sent every
178
+ // generated pset on a created entity to the file's first owner history
179
+ // instead of the one the caller named (#2012).
180
+ const overlay = ctx.mutationView?.getNewEntity(entityId);
181
+ if (overlay) {
182
+ const refs = authoredEntityRefs(overlaySlotValue(ctx, entityId, OWNER_HISTORY_SLOT, overlay.attributes[OWNER_HISTORY_SLOT]));
183
+ result = refs.length > 0 ? `#${refs[0]}` : null;
184
+ ctx.ownerHistory.byEntity.set(entityId, result);
185
+ return result;
186
+ }
187
+ const entityRef = ctx.dataStore.entityIndex.byId.get(entityId);
188
+ // Readability rather than presence, as everywhere else (#2491). A clamped
189
+ // decode would match nothing here, so this is tidiness rather than a bug —
190
+ // but the gates in this file agree on one predicate now.
191
+ if (entityRef && ctx.isReadableSourceRef(entityRef)) {
192
+ const entityText = decodeRange(ctx.dataStore.source, entityRef.byteOffset, entityRef.byteOffset + entityRef.byteLength);
193
+ // #ID=IFCWALL('GlobalId',#owner,...): GlobalId is a quoted STEP string
194
+ // (doubled '' escapes); OwnerHistory is the ref/`$` right after it.
195
+ const match = entityText.match(/=\s*IFC\w+\s*\(\s*'(?:[^']|'')*'\s*,\s*#(\d+)/i);
196
+ if (match)
197
+ result = `#${match[1]}`;
198
+ }
199
+ ctx.ownerHistory.byEntity.set(entityId, result);
200
+ return result;
201
+ }
202
+ /**
203
+ * Resolve a STEP reference to an existing IfcOwnerHistory for the
204
+ * IfcPropertySet / IfcRelDefinesByProperties / IfcElementQuantity entities we
205
+ * generate for `hostEntityId`'s mutations. OwnerHistory is optional in IFC4 but
206
+ * MANDATORY in IFC2X3 (IfcRoot.OwnerHistory), so emitting `$` yields an invalid
207
+ * IFC2X3 file that strict readers (e.g. BIM Vision) reject.
208
+ *
209
+ * Prefer the host element's OWN owner history, then any owner history that
210
+ * survives this export, then `$` only when none does.
211
+ *
212
+ * "Survives" is `willBeEmitted`, the same predicate that decides whether the
213
+ * host itself may have psets generated for it. A reference is a reference: it
214
+ * is no more acceptable to point an emitted `IfcPropertySet` at an owner
215
+ * history the session deleted than at a host it deleted. This used to consult
216
+ * only the `visibleOnly` closure, so an overlay-created OwnerHistory that was
217
+ * later deleted still got referenced — a dangling `#N`, reached through the
218
+ * one attribute the generators fill in for themselves.
219
+ */
220
+ export function resolveOwnerHistoryRef(ctx, hostEntityId, willBeEmitted) {
221
+ const own = getOwnerHistoryRefOfEntity(ctx, hostEntityId);
222
+ if (own !== null) {
223
+ const ownId = parseInt(own.slice(1), 10);
224
+ if (willBeEmitted(ownId))
225
+ return own;
226
+ }
227
+ if (ctx.ownerHistory.fallbackRef === undefined) {
228
+ // Source-only: the fallback is a best-effort "some owner history the file
229
+ // still has", and the host's OWN history above is the path that resolves
230
+ // an overlay-created one.
231
+ const ids = ctx.dataStore.entityIndex.byType.get('IFCOWNERHISTORY') ?? [];
232
+ const surviving = ids.find((id) => willBeEmitted(id));
233
+ ctx.ownerHistory.fallbackRef = surviving !== undefined ? `#${surviving}` : '$';
234
+ }
235
+ return ctx.ownerHistory.fallbackRef;
236
+ }
237
+ /**
238
+ * Generate a new IFC GlobalId (22 character base64). `random` is the
239
+ * export's optional seeded source (`StepExportOptions.guidRandom`);
240
+ * undefined keeps the default random path.
241
+ */
242
+ export function generateGlobalId(random) {
243
+ return generateIfcGuid(random);
244
+ }
245
+ /**
246
+ * Find a unit entity ID by name (simplified - returns null for now)
247
+ */
248
+ export function findUnitId(ctx, unitName, effective) {
249
+ return findLengthUnitReference(normalizeMapUnitName(unitName), effective, { dataStore: ctx.dataStore, entityExtractor: ctx.entityExtractor });
250
+ }
251
+ //# sourceMappingURL=step-property-set-readers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-property-set-readers.js","sourceRoot":"","sources":["../src/step-property-set-readers.ts"],"names":[],"mappings":"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"}
@@ -1,189 +1,5 @@
1
- /**
2
- * The property-set and quantity-set phases of `StepExporter.export()` (#2475
3
- * steps 2b and 2c), and the type-object `HasPropertySets` rewrite that sits
4
- * between them.
5
- *
6
- * ## Why one module, and why the rewrite is not its own function
7
- *
8
- * - `generatedTypeOwnedPsetIds` is written by the property-set generation loop
9
- * and read by the rewrite loop. `ExportPass` documents it as the binding
10
- * that "is read in one phase only", and it stays that way here precisely
11
- * because both loops live inside
12
- * {@link generatePropertyAndQuantitySetEntities}: it is a plain local, not a
13
- * field on the pass and not an argument threaded between two functions.
14
- * - {@link getTypeOwnedHasPropertySetIds} is the rewrite's own helper, but its
15
- * only call site is the property-set COLLECTION loop, which fills
16
- * `pass.typeOwnedPsetIdsByEntity` from its result.
17
- * - `getPropertySetName` is called from the collection loop twice and from the
18
- * rewrite once.
19
- * - The three generation loops run pset → rewrite → qset, and `export()`
20
- * flushes `pass.rewrittenEntityLines` (the rewrite's output) only after the
21
- * qset loop. That order is load-bearing and is kept exactly as it was.
22
- *
23
- * ## What stayed behind, and why these two exports exist
24
- *
25
- * `retainSharedAtoms` belongs to neither phase and runs between them, so it
26
- * stays on the exporter — which is why {@link getPropertyIdsInSet} is exported
27
- * rather than module-private, the same shape step 2a used for
28
- * `findLengthUnitReference`. {@link buildRelDefinesByPropertiesIndex} is
29
- * exported for the mirror-image reason: the collection loops consume its
30
- * `byEntity` half, but `export()` reads `relatedByRel` for the deleted-host
31
- * relationship sweep, which is neither phase's work.
32
- * `step-attribute-mutations.ts`'s `applySourceLineMutations` (moved off
33
- * `StepExporter` by #2475's "remaining private helpers" step) is shared with
34
- * the source-iteration pass, so it is injected on the context rather than
35
- * imported directly.
36
- *
37
- * ## The state these phases cannot read off the pass
38
- *
39
- * {@link PropertySetContext}, following `step-georeferencing.ts:GeorefContext`.
40
- * `allocateExpressId` is the exporter's own `nextExpressId++`: all six
41
- * allocation sites in an export live in the two generators below, and the
42
- * counter is shared with the georeferencing phase, so it is injected as a
43
- * callback rather than hoisted onto the pass. The owner-history memos are
44
- * injected BY REFERENCE and stay owned by the exporter, because their reset is
45
- * an `export()`-level statement whose comment explains why they are per-export;
46
- * storing them here would move that reset out of the method that documents it.
47
- *
48
- * Every helper below takes `ctx` FIRST; the two phase entry points take it
49
- * LAST, as `applyGeoreferencingMutations` does. The split is not aesthetic —
50
- * the two generators end in optional parameters, which a trailing `ctx` cannot
51
- * follow.
52
- */
53
- import type { IfcDataStore } from '@ifc-lite/parser';
54
- import { EntityExtractor } from '@ifc-lite/parser';
55
- import type { MutablePropertyView } from '@ifc-lite/mutations';
56
- import { createSourceRefReader } from './source-ref-bounds.js';
57
- import type { IfcSchemaVersion } from './schema-converter.js';
58
- import type { ExportPass, SourceLineMutations, StepExportOptions } from './step-exporter.js';
59
- /**
60
- * The two owner-history memos the generators share, in one object so the
61
- * exporter can hand them over by reference. Both are per-EXPORT rather than
62
- * per-exporter; `StepExporter.export()` owns the reset and explains why.
63
- */
64
- export interface OwnerHistoryCache {
65
- /** Lazily-resolved fallback `#id` of an IfcOwnerHistory that survives the
66
- * current export closure (or `$` when the file has none). */
67
- fallbackRef: string | undefined;
68
- /** Per-host cache of an element's own OwnerHistory ref (`#id` or null). */
69
- readonly byEntity: Map<number, string | null>;
70
- }
71
- /**
72
- * The exporter state these phases cannot read off the {@link ExportPass}.
73
- *
74
- * `isReadableSourceRef` is the exporter's OWN reader rather than the pass's:
75
- * the byte readers below are also reached from
76
- * {@link buildRelDefinesByPropertiesIndex} and from `retainSharedAtoms`,
77
- * neither of which has a pass in hand. Both readers are built by
78
- * `createSourceRefReader` over the same `dataStore.source`.
79
- */
80
- export interface PropertySetContext {
81
- readonly dataStore: IfcDataStore;
82
- readonly entityExtractor: EntityExtractor | null;
83
- readonly mutationView: MutablePropertyView | null;
84
- readonly isReadableSourceRef: ReturnType<typeof createSourceRefReader>;
85
- /** `() => this.nextExpressId++` on the exporter. */
86
- readonly allocateExpressId: () => number;
87
- readonly ownerHistory: OwnerHistoryCache;
88
- /** `step-attribute-mutations.ts`'s `applySourceLineMutations`: the ONE
89
- * pipeline the source-iteration pass and the type-object rewrite share, so
90
- * it belongs to neither phase and is injected. */
91
- readonly applySourceLineMutations: (expressId: number, entityText: string, recordType: string, attributeMutations: Map<string, string> | undefined, sourceSchema: IfcSchemaVersion, overlayActive: boolean, onRejected?: (attrName: string, value: string) => void) => SourceLineMutations;
92
- }
93
- /** The mutation groupings `export()` builds before the collection phase runs. */
94
- export interface PropertyMutationGroups {
95
- readonly entityPropMutations: Map<number, Set<string>>;
96
- readonly entityQuantMutations: Map<number, Set<string>>;
97
- readonly relDefinesByEntity: Map<number, Array<{
98
- relId: number;
99
- psetId: number;
100
- }>>;
101
- }
102
- /**
103
- * Build a one-shot reverse index of every IfcRelDefinesByProperties in
104
- * the source: for each related entity, list the rels and property/quantity
105
- * sets that reference it. Used by the export pre-pass so the per-entity
106
- * "find owning rels" step is O(K) rather than O(N) per modified entity.
107
- *
108
- * `relatedByRel` is the same walk read the other way round, so the deleted-host
109
- * sweep costs nothing extra.
110
- */
111
- export declare function buildRelDefinesByPropertiesIndex(ctx: PropertySetContext): {
112
- byEntity: Map<number, Array<{
113
- relId: number;
114
- psetId: number;
115
- }>>;
116
- relatedByRel: Map<number, number[]>;
117
- };
118
- /**
119
- * The source STEP text of an entity's line, or `null` when there are no bytes
120
- * to read.
121
- *
122
- * The byte check is on the RANGE, not on `dataStore.source`. `source` is a
123
- * MANDATORY accessor — `EMPTY_SOURCE_BYTES` is how "this model kept no bytes"
124
- * is spelled (server-parsed, synthetic, GLB and point-cloud stores all have
125
- * one) — so the `!ctx.dataStore.source` guard the five readers below used to
126
- * carry never fired. It was also redundant: a zero-length range decodes to
127
- * `''`, which fails every regex those readers run, so they already answered
128
- * "nothing" for a sourceless store. Scoping the check to the range is what
129
- * makes the guard live without changing a single answer, the same shape and
130
- * for the same reason as `reference-collector.ts` (#2339).
131
- *
132
- * An OVERLAY-created entity never reaches here: every caller resolves its id
133
- * through `dataStore.entityIndex.byId`, which holds source records only
134
- * (`effective-index.ts` synthesises the overlay refs on its own side and
135
- * writes nothing back), so an overlay id is already `undefined` at the
136
- * lookup and is served by the callers' documented "not a source record"
137
- * path. That is why an early return is safe HERE and is NOT safe at the
138
- * visible-only closure in `export` — see the comment there.
139
- *
140
- * ## Why `isReadableSourceRef` and not `byteLength === 0`
141
- *
142
- * An out-of-range ref does NOT degrade to "no match" here. `decodeUtf8`
143
- * clamps the range it cannot address, and the clamped window is still a
144
- * window over real file bytes — so these readers answer from somebody
145
- * ELSE's record. `source-ref-bounds.ts` (#2491) carries the measured
146
- * account of both shapes and of why "a clamped, empty decode already yields
147
- * no match" is false; it is not restated here, because an argument kept in
148
- * two files is an argument that has to stay true in two files.
149
- *
150
- * The consequence specific to THIS site is that the wrong answer is acted
151
- * on. `retainSharedAtoms` un-skips every id `getPropertyIdsInSet` returns,
152
- * so a member list read out of the wrong record un-skips the wrong atoms;
153
- * and the source-iteration pass already refuses to emit a record whose ref
154
- * fails `isReadableSourceRef` (see the `continue` in `export`), so before
155
- * this gate these readers were making decisions on behalf of a container
156
- * that the same export had decided not to write. Gating them on the same
157
- * predicate is what makes the two passes agree.
158
- *
159
- * The degradation is the one the exporter already handles: a record with no
160
- * emittable bytes, generating nothing and named by nothing. It costs one
161
- * answer that used to be right by luck — an overrunning ref on the file's
162
- * LAST record clamps back to exactly that record's text — but that record
163
- * is one the emission pass drops anyway, so keeping the answer only kept
164
- * the disagreement.
165
- */
166
- export declare function entityLineText(ctx: PropertySetContext, entityId: number): string | null;
167
- /**
168
- * Get the name of a property set by parsing the entity
169
- */
170
- export declare function getPropertySetName(ctx: PropertySetContext, psetId: number): string | null;
171
- /**
172
- * Get IDs of properties in a property set
173
- */
174
- export declare function getPropertyIdsInSet(ctx: PropertySetContext, psetId: number): number[];
175
- /**
176
- * Collect what the overlay's property-set and quantity-set edits mean for this
177
- * export: which sets to regenerate (`pass.newPropertySets` /
178
- * `pass.newQuantitySets`), which source records to withhold
179
- * (`pass.skipPropertySetIds` / `pass.skipRelationshipIds`), and which type
180
- * objects need their `HasPropertySets` resolved later
181
- * (`pass.typeOwnedPsetNamesByEntity`, `…IdsByEntity`, `pass.rewrittenEntityIds`).
182
- *
183
- * The caller owns the `mutationView && options.applyMutations !== false` gate;
184
- * reaching here means overlay edits were both present and enabled.
185
- */
186
- export declare function collectPropertyAndQuantitySetMutations(pass: ExportPass, options: StepExportOptions, groups: PropertyMutationGroups, ctx: PropertySetContext): void;
1
+ import { type PropertySetContext } from './step-property-set-readers.js';
2
+ import type { ExportPass, StepExportOptions } from './step-exporter.js';
187
3
  /**
188
4
  * Write the generated property-set and quantity-set records into
189
5
  * `pass.entities`, and point every affected type object's `HasPropertySets` at
@@ -1 +1 @@
1
- {"version":3,"file":"step-property-sets.d.ts","sourceRoot":"","sources":["../src/step-property-sets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"step-property-sets.d.ts","sourceRoot":"","sources":["../src/step-property-sets.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,KAAK,kBAAkB,EAAsB,MAAM,gCAAgC,CAAC;AAE7F,OAAO,KAAK,EAAE,UAAU,EAAuB,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG7F;;;;;;;;;GASG;AACH,wBAAgB,sCAAsC,CACpD,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,iBAAiB,EAC1B,GAAG,EAAE,kBAAkB,GACtB,IAAI,CAgLN"}