@ifc-lite/export 3.0.0 → 3.1.0
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.
- package/README.md +34 -0
- package/dist/anonymize-export.d.ts +60 -0
- package/dist/anonymize-export.d.ts.map +1 -0
- package/dist/anonymize-export.js +117 -0
- package/dist/anonymize-export.js.map +1 -0
- package/dist/anonymize-placement.d.ts +71 -0
- package/dist/anonymize-placement.d.ts.map +1 -0
- package/dist/anonymize-placement.js +266 -0
- package/dist/anonymize-placement.js.map +1 -0
- package/dist/anonymize-scrub.d.ts +64 -0
- package/dist/anonymize-scrub.d.ts.map +1 -0
- package/dist/anonymize-scrub.js +312 -0
- package/dist/anonymize-scrub.js.map +1 -0
- package/dist/anonymize-types.d.ts +212 -0
- package/dist/anonymize-types.d.ts.map +1 -0
- package/dist/anonymize-types.js +5 -0
- package/dist/anonymize-types.js.map +1 -0
- package/dist/declared-property-type.d.ts +19 -8
- package/dist/declared-property-type.d.ts.map +1 -1
- package/dist/declared-property-type.js +31 -9
- package/dist/declared-property-type.js.map +1 -1
- package/dist/entity-type-sets.d.ts +47 -0
- package/dist/entity-type-sets.d.ts.map +1 -0
- package/dist/entity-type-sets.js +91 -0
- package/dist/entity-type-sets.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/merged-exporter.d.ts +19 -0
- package/dist/merged-exporter.d.ts.map +1 -1
- package/dist/merged-exporter.js +1 -1
- package/dist/merged-exporter.js.map +1 -1
- package/dist/parquet-exporter.d.ts +1 -3
- package/dist/parquet-exporter.d.ts.map +1 -1
- package/dist/parquet-exporter.js +11 -10
- package/dist/parquet-exporter.js.map +1 -1
- package/dist/property-value-serialization.d.ts +54 -0
- package/dist/property-value-serialization.d.ts.map +1 -0
- package/dist/property-value-serialization.js +134 -0
- package/dist/property-value-serialization.js.map +1 -0
- package/dist/reference-collector.d.ts +47 -0
- package/dist/reference-collector.d.ts.map +1 -1
- package/dist/reference-collector.js +88 -156
- package/dist/reference-collector.js.map +1 -1
- package/dist/related-entities.d.ts +10 -0
- package/dist/related-entities.d.ts.map +1 -0
- package/dist/related-entities.js +259 -0
- package/dist/related-entities.js.map +1 -0
- package/dist/schema-converter.d.ts.map +1 -1
- package/dist/schema-converter.js +46 -96
- package/dist/schema-converter.js.map +1 -1
- package/dist/source-ref-bounds.d.ts +3 -3
- package/dist/source-ref-bounds.js +3 -3
- package/dist/step-attribute-mutations.d.ts +0 -42
- package/dist/step-attribute-mutations.d.ts.map +1 -1
- package/dist/step-attribute-mutations.js +17 -147
- package/dist/step-attribute-mutations.js.map +1 -1
- package/dist/step-attribute-serializers.d.ts +41 -0
- package/dist/step-attribute-serializers.d.ts.map +1 -0
- package/dist/step-attribute-serializers.js +74 -0
- package/dist/step-attribute-serializers.js.map +1 -0
- package/dist/step-collection.d.ts +2 -2
- package/dist/step-collection.d.ts.map +1 -1
- package/dist/step-collection.js +38 -8
- package/dist/step-collection.js.map +1 -1
- package/dist/step-export-contexts.d.ts +5 -7
- package/dist/step-export-contexts.d.ts.map +1 -1
- package/dist/step-export-contexts.js +6 -7
- package/dist/step-export-contexts.js.map +1 -1
- package/dist/step-export-types.d.ts +27 -0
- package/dist/step-export-types.d.ts.map +1 -1
- package/dist/step-export-types.js.map +1 -1
- package/dist/step-exporter.d.ts +3 -3
- package/dist/step-exporter.js +4 -4
- package/dist/step-exporter.js.map +1 -1
- package/dist/step-geometry-types.d.ts +1 -17
- package/dist/step-geometry-types.d.ts.map +1 -1
- package/dist/step-geometry-types.js +51 -35
- package/dist/step-geometry-types.js.map +1 -1
- package/dist/step-georeferencing.d.ts +0 -10
- package/dist/step-georeferencing.d.ts.map +1 -1
- package/dist/step-georeferencing.js +46 -94
- package/dist/step-georeferencing.js.map +1 -1
- package/dist/step-header.d.ts +11 -1
- package/dist/step-header.d.ts.map +1 -1
- package/dist/step-header.js +2 -9
- package/dist/step-header.js.map +1 -1
- package/dist/step-map-unit.d.ts +90 -0
- package/dist/step-map-unit.d.ts.map +1 -0
- package/dist/step-map-unit.js +277 -0
- package/dist/step-map-unit.js.map +1 -0
- package/dist/step-overlay-attribute-overrides.d.ts +32 -0
- package/dist/step-overlay-attribute-overrides.d.ts.map +1 -0
- package/dist/step-overlay-attribute-overrides.js +83 -0
- package/dist/step-overlay-attribute-overrides.js.map +1 -0
- package/dist/step-overlay-entities.d.ts +3 -3
- package/dist/step-pass-builder.d.ts +4 -3
- package/dist/step-pass-builder.d.ts.map +1 -1
- package/dist/step-pass-builder.js.map +1 -1
- package/dist/step-property-set-collection.d.ts +24 -0
- package/dist/step-property-set-collection.d.ts.map +1 -0
- package/dist/step-property-set-collection.js +286 -0
- package/dist/step-property-set-collection.js.map +1 -0
- package/dist/step-property-set-generators.d.ts +20 -0
- package/dist/step-property-set-generators.d.ts.map +1 -0
- package/dist/step-property-set-generators.js +98 -0
- package/dist/step-property-set-generators.js.map +1 -0
- package/dist/step-property-set-index.d.ts +25 -0
- package/dist/step-property-set-index.d.ts.map +1 -0
- package/dist/step-property-set-index.js +69 -0
- package/dist/step-property-set-index.js.map +1 -0
- package/dist/step-property-set-readers.d.ts +158 -0
- package/dist/step-property-set-readers.d.ts.map +1 -0
- package/dist/step-property-set-readers.js +251 -0
- package/dist/step-property-set-readers.js.map +1 -0
- package/dist/step-property-sets.d.ts +2 -186
- package/dist/step-property-sets.d.ts.map +1 -1
- package/dist/step-property-sets.js +22 -674
- package/dist/step-property-sets.js.map +1 -1
- package/dist/step-serialization.d.ts +17 -51
- package/dist/step-serialization.d.ts.map +1 -1
- package/dist/step-serialization.js +19 -128
- package/dist/step-serialization.js.map +1 -1
- package/dist/step-source-iteration.d.ts +7 -5
- package/dist/step-source-iteration.d.ts.map +1 -1
- package/dist/step-source-iteration.js +3 -3
- package/dist/step-source-iteration.js.map +1 -1
- package/dist/subset-entity-reader.d.ts +103 -0
- package/dist/subset-entity-reader.d.ts.map +1 -0
- package/dist/subset-entity-reader.js +102 -0
- package/dist/subset-entity-reader.js.map +1 -0
- package/dist/subset-roots.d.ts +58 -0
- package/dist/subset-roots.d.ts.map +1 -0
- package/dist/subset-roots.js +103 -0
- package/dist/subset-roots.js.map +1 -0
- package/package.json +7 -7
|
@@ -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
|
-
|
|
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":"
|
|
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"}
|