@ifc-lite/export 3.1.0 → 4.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 (152) hide show
  1. package/README.md +3 -1
  2. package/dist/anonymize-export.d.ts.map +1 -1
  3. package/dist/anonymize-export.js +108 -8
  4. package/dist/anonymize-export.js.map +1 -1
  5. package/dist/anonymize-placement.js +4 -4
  6. package/dist/anonymize-scrub.d.ts +6 -0
  7. package/dist/anonymize-scrub.d.ts.map +1 -1
  8. package/dist/anonymize-scrub.js +8 -4
  9. package/dist/anonymize-scrub.js.map +1 -1
  10. package/dist/anonymize-types.d.ts +30 -7
  11. package/dist/anonymize-types.d.ts.map +1 -1
  12. package/dist/declared-property-type.d.ts +15 -0
  13. package/dist/declared-property-type.d.ts.map +1 -1
  14. package/dist/declared-property-type.js +43 -0
  15. package/dist/declared-property-type.js.map +1 -1
  16. package/dist/georef-closure.d.ts +55 -0
  17. package/dist/georef-closure.d.ts.map +1 -0
  18. package/dist/georef-closure.js +95 -0
  19. package/dist/georef-closure.js.map +1 -0
  20. package/dist/glb.d.ts.map +1 -1
  21. package/dist/glb.js +26 -2
  22. package/dist/glb.js.map +1 -1
  23. package/dist/ifc5-classification.d.ts +36 -0
  24. package/dist/ifc5-classification.d.ts.map +1 -0
  25. package/dist/ifc5-classification.js +46 -0
  26. package/dist/ifc5-classification.js.map +1 -0
  27. package/dist/ifc5-export-helpers.d.ts +32 -0
  28. package/dist/ifc5-export-helpers.d.ts.map +1 -0
  29. package/dist/ifc5-export-helpers.js +84 -0
  30. package/dist/ifc5-export-helpers.js.map +1 -0
  31. package/dist/ifc5-exporter.d.ts.map +1 -1
  32. package/dist/ifc5-exporter.js +14 -86
  33. package/dist/ifc5-exporter.js.map +1 -1
  34. package/dist/ifc5-material.d.ts +38 -0
  35. package/dist/ifc5-material.d.ts.map +1 -0
  36. package/dist/ifc5-material.js +32 -0
  37. package/dist/ifc5-material.js.map +1 -0
  38. package/dist/index.d.ts +2 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/lod0-generator.d.ts +16 -0
  43. package/dist/lod0-generator.d.ts.map +1 -1
  44. package/dist/lod0-generator.js +33 -1
  45. package/dist/lod0-generator.js.map +1 -1
  46. package/dist/merged-context.d.ts +100 -0
  47. package/dist/merged-context.d.ts.map +1 -0
  48. package/dist/merged-context.js +211 -0
  49. package/dist/merged-context.js.map +1 -0
  50. package/dist/merged-empty-containers.d.ts +63 -0
  51. package/dist/merged-empty-containers.d.ts.map +1 -0
  52. package/dist/merged-empty-containers.js +354 -0
  53. package/dist/merged-empty-containers.js.map +1 -0
  54. package/dist/merged-exporter.d.ts +28 -67
  55. package/dist/merged-exporter.d.ts.map +1 -1
  56. package/dist/merged-exporter.js +134 -258
  57. package/dist/merged-exporter.js.map +1 -1
  58. package/dist/merged-guid.d.ts +48 -0
  59. package/dist/merged-guid.d.ts.map +1 -0
  60. package/dist/merged-guid.js +128 -0
  61. package/dist/merged-guid.js.map +1 -0
  62. package/dist/merged-rel-aggregates.d.ts +63 -0
  63. package/dist/merged-rel-aggregates.d.ts.map +1 -0
  64. package/dist/merged-rel-aggregates.js +122 -0
  65. package/dist/merged-rel-aggregates.js.map +1 -0
  66. package/dist/merged-remap.d.ts +18 -0
  67. package/dist/merged-remap.d.ts.map +1 -0
  68. package/dist/merged-remap.js +66 -0
  69. package/dist/merged-remap.js.map +1 -0
  70. package/dist/merged-subcontext.d.ts +41 -0
  71. package/dist/merged-subcontext.d.ts.map +1 -0
  72. package/dist/merged-subcontext.js +99 -0
  73. package/dist/merged-subcontext.js.map +1 -0
  74. package/dist/parquet-exporter-ondemand.d.ts +34 -0
  75. package/dist/parquet-exporter-ondemand.d.ts.map +1 -0
  76. package/dist/parquet-exporter-ondemand.js +89 -0
  77. package/dist/parquet-exporter-ondemand.js.map +1 -0
  78. package/dist/parquet-exporter.d.ts +1 -29
  79. package/dist/parquet-exporter.d.ts.map +1 -1
  80. package/dist/parquet-exporter.js +59 -81
  81. package/dist/parquet-exporter.js.map +1 -1
  82. package/dist/parquet-type-strings.d.ts +11 -0
  83. package/dist/parquet-type-strings.d.ts.map +1 -0
  84. package/dist/parquet-type-strings.js +40 -0
  85. package/dist/parquet-type-strings.js.map +1 -0
  86. package/dist/parquet-uint32-columns.d.ts +30 -0
  87. package/dist/parquet-uint32-columns.d.ts.map +1 -0
  88. package/dist/parquet-uint32-columns.js +38 -0
  89. package/dist/parquet-uint32-columns.js.map +1 -0
  90. package/dist/reference-collector.d.ts +0 -31
  91. package/dist/reference-collector.d.ts.map +1 -1
  92. package/dist/reference-collector.js +27 -82
  93. package/dist/reference-collector.js.map +1 -1
  94. package/dist/related-entities.d.ts.map +1 -1
  95. package/dist/related-entities.js +19 -4
  96. package/dist/related-entities.js.map +1 -1
  97. package/dist/schema-converter-attr-remap.d.ts +53 -0
  98. package/dist/schema-converter-attr-remap.d.ts.map +1 -0
  99. package/dist/schema-converter-attr-remap.js +106 -0
  100. package/dist/schema-converter-attr-remap.js.map +1 -0
  101. package/dist/schema-converter.d.ts.map +1 -1
  102. package/dist/schema-converter.js +24 -1
  103. package/dist/schema-converter.js.map +1 -1
  104. package/dist/schema-untranslatable.d.ts +35 -0
  105. package/dist/schema-untranslatable.d.ts.map +1 -0
  106. package/dist/schema-untranslatable.js +60 -0
  107. package/dist/schema-untranslatable.js.map +1 -0
  108. package/dist/step-argument-parser.d.ts.map +1 -1
  109. package/dist/step-argument-parser.js +11 -1
  110. package/dist/step-argument-parser.js.map +1 -1
  111. package/dist/step-attribute-mutations.js +14 -11
  112. package/dist/step-attribute-mutations.js.map +1 -1
  113. package/dist/step-collection.d.ts.map +1 -1
  114. package/dist/step-collection.js +20 -6
  115. package/dist/step-collection.js.map +1 -1
  116. package/dist/step-export-types.d.ts +15 -0
  117. package/dist/step-export-types.d.ts.map +1 -1
  118. package/dist/step-export-types.js +10 -0
  119. package/dist/step-export-types.js.map +1 -1
  120. package/dist/step-exporter.d.ts.map +1 -1
  121. package/dist/step-exporter.js +9 -1
  122. package/dist/step-exporter.js.map +1 -1
  123. package/dist/step-map-unit.d.ts +2 -1
  124. package/dist/step-map-unit.d.ts.map +1 -1
  125. package/dist/step-map-unit.js.map +1 -1
  126. package/dist/step-serialization.d.ts +10 -30
  127. package/dist/step-serialization.d.ts.map +1 -1
  128. package/dist/step-serialization.js +10 -60
  129. package/dist/step-serialization.js.map +1 -1
  130. package/dist/step-source-iteration.d.ts.map +1 -1
  131. package/dist/step-source-iteration.js +21 -0
  132. package/dist/step-source-iteration.js.map +1 -1
  133. package/dist/style-closure.d.ts +91 -0
  134. package/dist/style-closure.d.ts.map +1 -0
  135. package/dist/style-closure.js +164 -0
  136. package/dist/style-closure.js.map +1 -0
  137. package/dist/subset-entity-reader.d.ts +10 -9
  138. package/dist/subset-entity-reader.d.ts.map +1 -1
  139. package/dist/subset-entity-reader.js +21 -11
  140. package/dist/subset-entity-reader.js.map +1 -1
  141. package/dist/subset-roots.d.ts +17 -1
  142. package/dist/subset-roots.d.ts.map +1 -1
  143. package/dist/subset-roots.js +67 -2
  144. package/dist/subset-roots.js.map +1 -1
  145. package/dist/type-owned-psets.d.ts +19 -0
  146. package/dist/type-owned-psets.d.ts.map +1 -1
  147. package/dist/type-owned-psets.js +31 -0
  148. package/dist/type-owned-psets.js.map +1 -1
  149. package/dist/unit-normalize.d.ts.map +1 -1
  150. package/dist/unit-normalize.js +10 -2
  151. package/dist/unit-normalize.js.map +1 -1
  152. package/package.json +8 -7
@@ -0,0 +1,66 @@
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
+ /**
5
+ * Express-id remapping for the merged exporter: rewrite every `#N` reference of
6
+ * one model's STEP line into the merged file's id space.
7
+ *
8
+ * Split out of `merged-exporter.ts` (it never used `this`) so the id-rewriting
9
+ * rule lives in one small module next to the other merge-time line surgery
10
+ * (`merged-empty-containers.ts`, `merged-subcontext.ts`).
11
+ */
12
+ /**
13
+ * Remap all #ID references in a STEP entity line.
14
+ * Applies offset to all IDs, then overrides with specific remappings.
15
+ *
16
+ * Only `#<digits>` tokens in code positions are rewritten; tokens inside
17
+ * single-quoted STEP strings (e.g. a 'Room #205' Name or a 'http://x#42'
18
+ * URL) are left untouched so string attribute values are not corrupted.
19
+ */
20
+ export function remapEntityText(entityText, offset, sharedRemap) {
21
+ const remapId = (originalId) => {
22
+ // Check if this ID has a specific remap (project, shared infrastructure)
23
+ const remapped = sharedRemap.get(originalId);
24
+ if (remapped !== undefined) {
25
+ return `#${remapped}`;
26
+ }
27
+ // Apply offset
28
+ return `#${originalId + offset}`;
29
+ };
30
+ let out = '';
31
+ let inString = false;
32
+ for (let i = 0; i < entityText.length; i++) {
33
+ const char = entityText[i];
34
+ if (inString) {
35
+ out += char;
36
+ if (char === "'") {
37
+ // STEP escapes a literal quote by doubling it ('').
38
+ if (entityText[i + 1] === "'") {
39
+ out += entityText[i + 1];
40
+ i++;
41
+ }
42
+ else {
43
+ inString = false;
44
+ }
45
+ }
46
+ continue;
47
+ }
48
+ if (char === "'") {
49
+ inString = true;
50
+ out += char;
51
+ continue;
52
+ }
53
+ if (char === '#' && entityText[i + 1] >= '0' && entityText[i + 1] <= '9') {
54
+ let j = i + 1;
55
+ while (j < entityText.length && entityText[j] >= '0' && entityText[j] <= '9')
56
+ j++;
57
+ const originalId = parseInt(entityText.slice(i + 1, j), 10);
58
+ out += remapId(originalId);
59
+ i = j - 1;
60
+ continue;
61
+ }
62
+ out += char;
63
+ }
64
+ return out;
65
+ }
66
+ //# sourceMappingURL=merged-remap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged-remap.js","sourceRoot":"","sources":["../src/merged-remap.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,MAAc,EACd,WAAwC;IAExC,MAAM,OAAO,GAAG,CAAC,UAAkB,EAAU,EAAE;QAC7C,yEAAyE;QACzE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,IAAI,QAAQ,EAAE,CAAC;QACxB,CAAC;QACD,eAAe;QACf,OAAO,IAAI,UAAU,GAAG,MAAM,EAAE,CAAC;IACnC,CAAC,CAAC;IAEF,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAE3B,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,IAAI,IAAI,CAAC;YACZ,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACjB,oDAAoD;gBACpD,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC9B,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBACzB,CAAC,EAAE,CAAC;gBACN,CAAC;qBAAM,CAAC;oBACN,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;YACH,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,IAAI,CAAC;YAChB,GAAG,IAAI,IAAI,CAAC;YACZ,SAAS;QACX,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;YACzE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG;gBAAE,CAAC,EAAE,CAAC;YAClF,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5D,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACV,SAAS;QACX,CAAC;QAED,GAAG,IAAI,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Kind-aware `IfcGeometricRepresentationSubContext` matching for
3
+ * {@link MergedExporter} (merge invariant lens pass 3, item 1).
4
+ *
5
+ * `MergedExporter` deduplicates a unit-compatible model's shared
6
+ * infrastructure — `IfcUnitAssignment`, `IfcGeometricRepresentationContext`
7
+ * and subcontexts — onto the primary model's instance. For subcontexts that
8
+ * used to mean taking the two models' subcontext id lists **positionally**:
9
+ * this model's first subcontext (in whatever order `entityIndex.byType`
10
+ * returns them) was unified onto the primary model's first subcontext, with
11
+ * no check that the two are the same kind.
12
+ *
13
+ * A model normally declares more than one subcontext ('Body' for solid
14
+ * geometry, 'Axis' for centreline geometry, sometimes 'FootPrint'/'Box'), and
15
+ * nothing in the IFC schema or common exporter behaviour guarantees they are
16
+ * always written in the same order across two different authoring tools. A
17
+ * positional match can therefore unify this model's 'Body' subcontext onto
18
+ * the primary model's 'Axis' subcontext (or vice versa): every
19
+ * `IfcShapeRepresentation.ContextOfItems` that pointed at the dropped 'Body'
20
+ * subcontext now resolves, after the remap, to a context tagged 'Axis' — the
21
+ * wrong kind, which many viewers filter out of the 3D view entirely (the
22
+ * geometry silently vanishes), with no dangling reference to reveal it.
23
+ *
24
+ * `planSubContextUnify` fixes this by matching on the subcontext's *kind*
25
+ * (its `ContextIdentifier` *and* `TargetView`, plus `UserDefinedTargetView`
26
+ * where applicable) rather than on raw array position.
27
+ */
28
+ import type { IfcDataStore } from '@ifc-lite/parser';
29
+ /** Group `ids` (a model's subcontext express ids) by {@link subContextKey}. */
30
+ export declare function groupSubContextsByKey(dataStore: IfcDataStore, ids: readonly number[]): Map<string, number[]>;
31
+ /**
32
+ * Plan this model's subcontext dedup against the primary model, key by key:
33
+ * a subcontext in `thisIds` is remapped+skipped only against an unclaimed
34
+ * primary-model subcontext with the SAME key (never a differently ordered one
35
+ * of a different kind or TargetView). A subcontext with no same-key match in
36
+ * the primary model is left un-remapped — kept as its own (offset-only)
37
+ * entity, exactly like today's "no shared infrastructure of this type"
38
+ * fallback. Mutates `sharedRemap`/`skipEntityIds`.
39
+ */
40
+ export declare function planSubContextUnify(dataStore: IfcDataStore, thisIds: readonly number[], firstModelSubContextsByKey: ReadonlyMap<string, number[]>, firstModelOffset: number, sharedRemap: Map<number, number>, skipEntityIds: Set<number>): void;
41
+ //# sourceMappingURL=merged-subcontext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged-subcontext.d.ts","sourceRoot":"","sources":["../src/merged-subcontext.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA8DrD,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQ5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,0BAA0B,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACzD,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,IAAI,CAYN"}
@@ -0,0 +1,99 @@
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 { asSourceBytes, STEP_TRIVIA } from '@ifc-lite/parser';
5
+ import { splitTopLevelStepArguments } from './step-argument-parser.js';
6
+ /**
7
+ * `#N=TYPE(...)` record, with STEP trivia (whitespace and/or a
8
+ * `/* ... *​/` comment, #3789) tolerated between the type name and `(` —
9
+ * same adjacency fix as `entity-extractor.ts`'s `extractEntity`. Without it
10
+ * a wrapped record's attribute is silently unreadable here (`getStepAttr`
11
+ * returns `null`), which reads identically to a genuinely-absent attribute.
12
+ */
13
+ const RECORD_RE = new RegExp(`^#\\d+\\s*=\\s*\\w+${STEP_TRIVIA}\\(([\\s\\S]*)\\)\\s*;?\\s*$`);
14
+ /** 0-based attribute index of `IfcGeometricRepresentationSubContext.ContextIdentifier`. */
15
+ const CONTEXT_IDENTIFIER_ATTR = 0;
16
+ /** 0-based attribute index of `IfcGeometricRepresentationSubContext.TargetView`. */
17
+ const TARGET_VIEW_ATTR = 8;
18
+ /** 0-based attribute index of `IfcGeometricRepresentationSubContext.UserDefinedTargetView`. */
19
+ const USER_DEFINED_TARGET_VIEW_ATTR = 9;
20
+ function decodeEntity(dataStore, expressId) {
21
+ const source = dataStore.source;
22
+ if (!source)
23
+ return null;
24
+ const ref = dataStore.entityIndex.byId.get(expressId);
25
+ if (!ref)
26
+ return null;
27
+ return asSourceBytes(source).decodeUtf8(ref.byteOffset, ref.byteOffset + ref.byteLength);
28
+ }
29
+ /** Extract one 0-based STEP attribute of `expressId`'s entity, or null if unreadable. */
30
+ function getStepAttr(dataStore, expressId, index) {
31
+ const text = decodeEntity(dataStore, expressId);
32
+ const match = text?.match(RECORD_RE);
33
+ if (!match)
34
+ return null;
35
+ const args = splitTopLevelStepArguments(match[1]);
36
+ const raw = args?.[index];
37
+ return raw === undefined ? null : raw.trim();
38
+ }
39
+ /** Normalize a STEP string/enum token to a comparison key; `''` for unset/unreadable. */
40
+ function normalizeLabel(raw) {
41
+ if (!raw || raw === '$' || raw === '*')
42
+ return '';
43
+ const quoted = raw.match(/^'([\s\S]*)'$/);
44
+ const inner = quoted ? quoted[1] : raw;
45
+ return inner.replace(/\./g, '').trim().toUpperCase();
46
+ }
47
+ /**
48
+ * The matching key of one `IfcGeometricRepresentationSubContext` combines its
49
+ * `ContextIdentifier` and `TargetView`: a Body representation for MODEL_VIEW
50
+ * is not interchangeable with one for PLAN_VIEW. `UserDefinedTargetView`
51
+ * distinguishes two USERDEFINED views. Empty pieces deliberately remain part
52
+ * of the key so identifier-less subcontexts still group by their view.
53
+ */
54
+ function subContextKey(dataStore, expressId) {
55
+ const identifier = normalizeLabel(getStepAttr(dataStore, expressId, CONTEXT_IDENTIFIER_ATTR));
56
+ const targetView = normalizeLabel(getStepAttr(dataStore, expressId, TARGET_VIEW_ATTR));
57
+ const userDefinedTargetView = targetView === 'USERDEFINED'
58
+ ? normalizeLabel(getStepAttr(dataStore, expressId, USER_DEFINED_TARGET_VIEW_ATTR))
59
+ : '';
60
+ return `${identifier}\u0000${targetView}\u0000${userDefinedTargetView}`;
61
+ }
62
+ /** Group `ids` (a model's subcontext express ids) by {@link subContextKey}. */
63
+ export function groupSubContextsByKey(dataStore, ids) {
64
+ const map = new Map();
65
+ for (const id of ids) {
66
+ const key = subContextKey(dataStore, id);
67
+ const bucket = map.get(key);
68
+ if (bucket)
69
+ bucket.push(id);
70
+ else
71
+ map.set(key, [id]);
72
+ }
73
+ return map;
74
+ }
75
+ /**
76
+ * Plan this model's subcontext dedup against the primary model, key by key:
77
+ * a subcontext in `thisIds` is remapped+skipped only against an unclaimed
78
+ * primary-model subcontext with the SAME key (never a differently ordered one
79
+ * of a different kind or TargetView). A subcontext with no same-key match in
80
+ * the primary model is left un-remapped — kept as its own (offset-only)
81
+ * entity, exactly like today's "no shared infrastructure of this type"
82
+ * fallback. Mutates `sharedRemap`/`skipEntityIds`.
83
+ */
84
+ export function planSubContextUnify(dataStore, thisIds, firstModelSubContextsByKey, firstModelOffset, sharedRemap, skipEntityIds) {
85
+ const nextIndex = new Map();
86
+ for (const id of thisIds) {
87
+ const key = subContextKey(dataStore, id);
88
+ const pool = firstModelSubContextsByKey.get(key);
89
+ if (!pool || pool.length === 0)
90
+ continue;
91
+ const idx = nextIndex.get(key) ?? 0;
92
+ if (idx >= pool.length)
93
+ continue; // every same-kind primary target already claimed
94
+ nextIndex.set(key, idx + 1);
95
+ sharedRemap.set(id, pool[idx] + firstModelOffset);
96
+ skipEntityIds.add(id);
97
+ }
98
+ }
99
+ //# sourceMappingURL=merged-subcontext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged-subcontext.js","sourceRoot":"","sources":["../src/merged-subcontext.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `Properties.parquet` / `Quantities.parquet` writers for the on-demand parse
3
+ * path, extracted from `ParquetExporter` (`parquet-exporter.ts`) to keep that
4
+ * file under its module-size budget.
5
+ *
6
+ * `IfcParser.parseColumnar` (`packages/parser`'s only parse path, used by
7
+ * every real caller) never calls `.add()` on the `PropertyTableBuilder`/
8
+ * `QuantityTableBuilder` it constructs — properties and quantities are served
9
+ * lazily through `onDemandPropertyMap`/`onDemandQuantityMap` +
10
+ * `store.getProperties()`/`store.getQuantities()` instead
11
+ * (`extractPropertiesOnDemand`/`extractQuantitiesOnDemand`, re-parsing the
12
+ * source buffer on access). `ParquetExporter`'s bulk-table writers read
13
+ * `store.properties`/`store.quantities` directly, which is only populated
14
+ * when a caller builds a store some other way (e.g. this package's own
15
+ * hand-built test fixtures) — every store from a real parse left those two
16
+ * tables at zero rows, silently, alongside a fully-populated
17
+ * `Entities.parquet`/`Relationships.parquet`. These two functions are the
18
+ * on-demand fallback `ParquetExporter` calls when the bulk table is empty:
19
+ * one flat row per property/quantity, read through the same
20
+ * `store.getProperties()`/`store.getQuantities()` accessor every other
21
+ * consumer (the query package, the viewer's property panel) uses — never a
22
+ * second copy of the pset/property walk.
23
+ */
24
+ import type { IfcDataStore } from '@ifc-lite/parser';
25
+ import type { EffectiveEntityIndex } from './effective-index.js';
26
+ export declare function writePropertiesOnDemand(store: IfcDataStore, effective: EffectiveEntityIndex | null): Promise<Uint8Array>;
27
+ /**
28
+ * `Formula` is always NULL here: the on-demand quantity reader
29
+ * (`readQuantitySet` in `@ifc-lite/parser`) reports only
30
+ * `{name, type, value}` per quantity, so there is no formula string to carry
31
+ * — `ParquetExporter`'s bulk-table path is the only one that can populate it.
32
+ */
33
+ export declare function writeQuantitiesOnDemand(store: IfcDataStore, effective: EffectiveEntityIndex | null): Promise<Uint8Array>;
34
+ //# sourceMappingURL=parquet-exporter-ondemand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parquet-exporter-ondemand.d.ts","sourceRoot":"","sources":["../src/parquet-exporter-ondemand.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAKjE,wBAAsB,uBAAuB,CACzC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,oBAAoB,GAAG,IAAI,GACvC,OAAO,CAAC,UAAU,CAAC,CA4CrB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CACzC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,oBAAoB,GAAG,IAAI,GACvC,OAAO,CAAC,UAAU,CAAC,CA+BrB"}
@@ -0,0 +1,89 @@
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 { PropertyValueType } from '@ifc-lite/data';
5
+ import { columnsToParquet } from './columns-to-parquet.js';
6
+ import { PARQUET_UINT32_COLUMNS } from './parquet-uint32-columns.js';
7
+ import { propertyValueTypeToString, quantityTypeToString } from './parquet-type-strings.js';
8
+ export async function writePropertiesOnDemand(store, effective) {
9
+ const entityIdCol = [];
10
+ const psetNameCol = [];
11
+ const psetGlobalIdCol = [];
12
+ const propNameCol = [];
13
+ const propTypeCol = [];
14
+ const valueStringCol = [];
15
+ // `IfcPropertyBoundedValue` is surfaced as a display string but retains
16
+ // the REAL type tag. Only a scalar numeric value belongs in ValueReal;
17
+ // writing 0 for the display-string form fabricates a measurement.
18
+ const valueRealCol = [];
19
+ const valueIntCol = [];
20
+ const valueBoolCol = [];
21
+ for (const id of store.onDemandPropertyMap.keys()) {
22
+ if (effective?.isDeleted(id))
23
+ continue;
24
+ const psets = store.getProperties(id);
25
+ for (const pset of psets) {
26
+ for (const prop of pset.properties) {
27
+ entityIdCol.push(id);
28
+ psetNameCol.push(pset.name);
29
+ psetGlobalIdCol.push(pset.globalId ?? '');
30
+ propNameCol.push(prop.name);
31
+ propTypeCol.push(prop.type);
32
+ const v = prop.value;
33
+ valueStringCol.push(typeof v === 'string' ? v : null);
34
+ valueRealCol.push(prop.type === PropertyValueType.Real && typeof v === 'number' ? v : null);
35
+ valueIntCol.push(prop.type === PropertyValueType.Integer && typeof v === 'number' ? v : 0);
36
+ valueBoolCol.push(typeof v === 'boolean' ? v : null);
37
+ }
38
+ }
39
+ }
40
+ return columnsToParquet({
41
+ EntityId: entityIdCol,
42
+ PsetName: psetNameCol,
43
+ PsetGlobalId: psetGlobalIdCol,
44
+ PropName: propNameCol,
45
+ PropType: propTypeCol.map(t => propertyValueTypeToString(t)),
46
+ ValueString: valueStringCol,
47
+ ValueReal: valueRealCol,
48
+ ValueInt: valueIntCol,
49
+ ValueBool: valueBoolCol,
50
+ }, new Set(['ValueReal']), PARQUET_UINT32_COLUMNS);
51
+ }
52
+ /**
53
+ * `Formula` is always NULL here: the on-demand quantity reader
54
+ * (`readQuantitySet` in `@ifc-lite/parser`) reports only
55
+ * `{name, type, value}` per quantity, so there is no formula string to carry
56
+ * — `ParquetExporter`'s bulk-table path is the only one that can populate it.
57
+ */
58
+ export async function writeQuantitiesOnDemand(store, effective) {
59
+ const entityIdCol = [];
60
+ const qsetNameCol = [];
61
+ const quantityNameCol = [];
62
+ const quantityTypeCol = [];
63
+ const valueCol = [];
64
+ const formulaCol = [];
65
+ for (const id of store.onDemandQuantityMap.keys()) {
66
+ if (effective?.isDeleted(id))
67
+ continue;
68
+ const qsets = store.getQuantities(id);
69
+ for (const qset of qsets) {
70
+ for (const q of qset.quantities) {
71
+ entityIdCol.push(id);
72
+ qsetNameCol.push(qset.name);
73
+ quantityNameCol.push(q.name);
74
+ quantityTypeCol.push(q.type);
75
+ valueCol.push(q.value);
76
+ formulaCol.push(q.formula ?? null);
77
+ }
78
+ }
79
+ }
80
+ return columnsToParquet({
81
+ EntityId: entityIdCol,
82
+ QsetName: qsetNameCol,
83
+ QuantityName: quantityNameCol,
84
+ QuantityType: quantityTypeCol.map(t => quantityTypeToString(t)),
85
+ Value: valueCol,
86
+ Formula: formulaCol,
87
+ }, new Set(['Value']), PARQUET_UINT32_COLUMNS);
88
+ }
89
+ //# sourceMappingURL=parquet-exporter-ondemand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parquet-exporter-ondemand.js","sourceRoot":"","sources":["../src/parquet-exporter-ondemand.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,6 @@
2
2
  import type { IfcDataStore } from '@ifc-lite/parser';
3
3
  import type { GeometryResult } from '@ifc-lite/geometry';
4
4
  import type { MutablePropertyView } from '@ifc-lite/mutations';
5
- import { QuantityType } from '@ifc-lite/data';
6
5
  export interface ParquetExportOptions {
7
6
  includeGeometry?: boolean;
8
7
  }
@@ -58,35 +57,8 @@ export declare class ParquetExporter {
58
57
  private writeMeshes;
59
58
  private writeSpatialHierarchy;
60
59
  private writeMetadata;
61
- /**
62
- * Column names whose domain is an unsigned 32-bit integer.
63
- *
64
- * Every one carries an IFC EXPRESS ID or an index into a geometry buffer,
65
- * and both are `u32` everywhere else in this codebase - `Uint32Array` in
66
- * the parser's entity index and its transports, `u32` in the Rust crates.
67
- * Arrow's content inference reaches for Int32 on any whole number, so an
68
- * express id at or above 2_147_483_648 came out NEGATIVE: an id-shaped
69
- * number that joins to nothing. STEP puts no upper bound on an entity id
70
- * below the `u32` the readers use, so that is reachable input rather than a
71
- * hypothetical.
72
- */
73
- private static readonly UINT32_COLUMNS;
74
- /**
75
- * NOT in the set above, deliberately: `BuildingId`, `SiteId` and `SpaceId`
76
- * in `SpatialHierarchy.parquet` carry **-1 as "none"** (see
77
- * `writeSpatialHierarchy` - a storey directly under the project has no
78
- * building). Declaring those unsigned turned that sentinel into
79
- * 4294967295: an id-shaped number where an obviously-absent marker used to
80
- * be, which is the exact failure this class of change exists to prevent.
81
- *
82
- * The residual gap is the narrower one: a building or site id at or above
83
- * 2^31 still wraps negative in those three columns. Fixing that properly
84
- * means writing NULL rather than -1 for "none", which changes what every
85
- * consumer reads for an absent parent and is a separate decision from the
86
- * id width.
87
- */
88
60
  private toParquet;
89
61
  private createZipArchive;
90
62
  }
91
- export declare function QuantityTypeToString(type: QuantityType): string;
63
+ export { quantityTypeToString as QuantityTypeToString } from './parquet-type-strings.js';
92
64
  //# sourceMappingURL=parquet-exporter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parquet-exporter.d.ts","sourceRoot":"","sources":["../src/parquet-exporter.ts"],"names":[],"mappings":"AAIA,uDAAuD;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAA+C,YAAY,EAAqD,MAAM,gBAAgB,CAAC;AAI9I,MAAM,WAAW,oBAAoB;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,YAAY,CAA6B;IAEjD;;;;;;;;;;;;;;;;;OAiBG;gBACS,KAAK,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,cAAc,EAAE,YAAY,CAAC,EAAE,mBAAmB;IAMpG;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACG,SAAS,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,UAAU,CAAC;IA4BxE;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAkB3C,aAAa;YAoDb,eAAe;YAsBf,eAAe;YAiBf,kBAAkB;YAkClB,YAAY;YAkBZ,iBAAiB;YA+DjB,gBAAgB;YA6BhB,WAAW;YAyCX,qBAAqB;IAuFnC,OAAO,CAAC,aAAa;IA6BrB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAKnC;IAEH;;;;;;;;;;;;;OAaG;YAEW,SAAS;YAIT,gBAAgB;CAUjC;AA6CD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAW/D"}
1
+ {"version":3,"file":"parquet-exporter.d.ts","sourceRoot":"","sources":["../src/parquet-exporter.ts"],"names":[],"mappings":"AAIA,uDAAuD;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAQ/D,MAAM,WAAW,oBAAoB;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,YAAY,CAA6B;IAEjD;;;;;;;;;;;;;;;;;OAiBG;gBACS,KAAK,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,cAAc,EAAE,YAAY,CAAC,EAAE,mBAAmB;IAMpG;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACG,SAAS,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,UAAU,CAAC;IA4BxE;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAkB3C,aAAa;YAoDb,eAAe;YAuCf,eAAe;YA0Bf,kBAAkB;YAwClB,YAAY;YAkBZ,iBAAiB;YA+DjB,gBAAgB;YA6BhB,WAAW;YAyCX,qBAAqB;IAuFnC,OAAO,CAAC,aAAa;YA6BP,SAAS;YAIT,gBAAgB;CAUjC;AAgCD,OAAO,EAAE,oBAAoB,IAAI,oBAAoB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1,9 +1,12 @@
1
1
  /* This Source Code Form is subject to the terms of the Mozilla Public
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
- import { IfcTypeEnum, EntityFlags, PropertyValueType, QuantityType, RelationshipType, IFC_ENTITY_NAMES, exactTypeName } from '@ifc-lite/data';
4
+ import { IfcTypeEnum, EntityFlags, RelationshipType, IFC_ENTITY_NAMES, exactTypeName, flattenRelationshipEdges } from '@ifc-lite/data';
5
5
  import { getEffectiveEntityIndex } from './effective-index.js';
6
6
  import { columnsToParquet } from './columns-to-parquet.js';
7
+ import { PARQUET_UINT32_COLUMNS } from './parquet-uint32-columns.js';
8
+ import { writePropertiesOnDemand, writeQuantitiesOnDemand } from './parquet-exporter-ondemand.js';
9
+ import { propertyValueTypeToString, quantityTypeToString } from './parquet-type-strings.js';
7
10
  /**
8
11
  * Export to ara3d BIM Open Schema compatible Parquet files.
9
12
  * Creates a .bos archive (ZIP of Parquet files).
@@ -148,6 +151,22 @@ export class ParquetExporter {
148
151
  async writeProperties() {
149
152
  const { properties, strings } = this.store;
150
153
  const effective = this.getEffective();
154
+ // `IfcParser.parseColumnar` (the sole parse path every real caller of
155
+ // this exporter goes through — `packages/parser`'s `parseLite`) never
156
+ // populates `store.properties`: it builds a `PropertyTableBuilder` but
157
+ // never calls `.add()` on it, relying instead on lazy per-entity
158
+ // extraction via `onDemandPropertyMap` + `store.getProperties()`
159
+ // (`extractPropertiesOnDemand`, re-parsing the source buffer on
160
+ // access). Reading `store.properties` directly here — the bulk table
161
+ // — silently wrote a zero-row `Properties.parquet` for every model
162
+ // parsed the normal way, geometry/relationships/entities tables full
163
+ // alongside it. `store.properties` only carries real rows when a
164
+ // caller builds a store some other way (e.g. hand-built fixtures in
165
+ // this file's own tests). Route through the on-demand path first;
166
+ // fall back to the bulk table when it was actually populated.
167
+ if (properties.entityId.length === 0 && this.store.onDemandPropertyMap && this.store.onDemandPropertyMap.size > 0) {
168
+ return writePropertiesOnDemand(this.store, effective);
169
+ }
151
170
  const entityId = Array.from(properties.entityId);
152
171
  // A property row belongs to the entity named in its own EntityId
153
172
  // column, not to its own row index — filter on that, not on ExpressId.
@@ -157,7 +176,7 @@ export class ParquetExporter {
157
176
  PsetName: mapTypedArray(properties.psetName, i => strings.get(i)),
158
177
  PsetGlobalId: mapTypedArray(properties.psetGlobalId, i => strings.get(i)),
159
178
  PropName: mapTypedArray(properties.propName, i => strings.get(i)),
160
- PropType: mapTypedArray(properties.propType, t => PropertyValueTypeToString(t)),
179
+ PropType: mapTypedArray(properties.propType, t => propertyValueTypeToString(t)),
161
180
  ValueString: mapTypedArray(properties.valueString, i => i >= 0 && i < strings.count ? strings.get(i) : null),
162
181
  ValueReal: Array.from(properties.valueReal),
163
182
  ValueInt: Array.from(properties.valueInt),
@@ -167,40 +186,54 @@ export class ParquetExporter {
167
186
  async writeQuantities() {
168
187
  const { quantities, strings } = this.store;
169
188
  const effective = this.getEffective();
189
+ // Same gap as `writeProperties`: `store.quantities` is only ever
190
+ // populated when a caller bulk-builds it directly (this file's own
191
+ // tests); the real `parseColumnar` path leaves it empty and serves
192
+ // quantities lazily through `onDemandQuantityMap` +
193
+ // `store.getQuantities()`.
194
+ if (quantities.entityId.length === 0 && this.store.onDemandQuantityMap && this.store.onDemandQuantityMap.size > 0) {
195
+ return writeQuantitiesOnDemand(this.store, effective);
196
+ }
170
197
  const entityId = Array.from(quantities.entityId);
171
198
  const keep = effective ? entityId.map((id) => !effective.isDeleted(id)) : null;
172
199
  return this.toParquet(filterColumns({
173
200
  EntityId: entityId,
174
201
  QsetName: mapTypedArray(quantities.qsetName, i => strings.get(i)),
175
202
  QuantityName: mapTypedArray(quantities.quantityName, i => strings.get(i)),
176
- QuantityType: mapTypedArray(quantities.quantityType, t => QuantityTypeToString(t)),
203
+ QuantityType: mapTypedArray(quantities.quantityType, t => quantityTypeToString(t)),
177
204
  Value: Array.from(quantities.value),
178
205
  Formula: mapTypedArray(quantities.formula, i => i > 0 ? strings.get(i) : null),
179
206
  }, keep), new Set(['Value']));
180
207
  }
181
208
  async writeRelationships() {
182
209
  const { relationships } = this.store;
183
- const edges = relationships.forward;
184
210
  const effective = this.getEffective();
185
- // Flatten CSR format to row-based
211
+ // One row per `IfcRel*` STEP record, including any collapsed into
212
+ // an edge's `shadowedRelationshipIds` (#3760/#3782) — not one row
213
+ // per deduped edge; see `flattenRelationshipEdges`'s doc comment.
186
214
  const sourceIds = [];
187
215
  const targetIds = [];
188
216
  const relTypes = [];
189
217
  const relIds = [];
190
- for (const [sourceId, offset] of edges.offsets) {
191
- const count = edges.counts.get(sourceId);
192
- for (let i = offset; i < offset + count; i++) {
193
- const targetId = edges.edgeTargets[i];
194
- // An edge naming a tombstoned entity on either end no longer
195
- // has a live entity to relate drop the row rather than
196
- // leave a dangling SourceId/TargetId in the export.
197
- if (effective && (effective.isDeleted(sourceId) || effective.isDeleted(targetId)))
198
- continue;
199
- sourceIds.push(sourceId);
200
- targetIds.push(targetId);
201
- relTypes.push(RelationshipTypeToString(edges.edgeTypes[i]));
202
- relIds.push(edges.edgeRelIds[i]);
203
- }
218
+ for (const row of flattenRelationshipEdges(relationships.forward)) {
219
+ // An edge naming a tombstoned entity on either end no longer
220
+ // has a live entity to relate drop the row rather than
221
+ // leave a dangling SourceId/TargetId in the export.
222
+ //
223
+ // `relationshipId` gets the same treatment: an `IfcRel*` record
224
+ // is itself a row in Entities.parquet (the columnar parser
225
+ // indexes it like any other line), so emitting a RelId for a
226
+ // deleted one leaves a dangling reference too. Because each
227
+ // shadowed id is its own row here, this drops exactly the
228
+ // deleted record and keeps a live sibling — the same rule
229
+ // `edgeSurvives` applies to the CLI/MCP `related()` path
230
+ // (#3782 review).
231
+ if (effective && (effective.isDeleted(row.sourceId) || effective.isDeleted(row.targetId) || effective.isDeleted(row.relationshipId)))
232
+ continue;
233
+ sourceIds.push(row.sourceId);
234
+ targetIds.push(row.targetId);
235
+ relTypes.push(RelationshipTypeToString(row.type));
236
+ relIds.push(row.relationshipId);
204
237
  }
205
238
  return this.toParquet({
206
239
  SourceId: sourceIds,
@@ -437,40 +470,8 @@ export class ParquetExporter {
437
470
  // ═══════════════════════════════════════════════════════════════
438
471
  // UTILITIES
439
472
  // ═══════════════════════════════════════════════════════════════
440
- /**
441
- * Column names whose domain is an unsigned 32-bit integer.
442
- *
443
- * Every one carries an IFC EXPRESS ID or an index into a geometry buffer,
444
- * and both are `u32` everywhere else in this codebase - `Uint32Array` in
445
- * the parser's entity index and its transports, `u32` in the Rust crates.
446
- * Arrow's content inference reaches for Int32 on any whole number, so an
447
- * express id at or above 2_147_483_648 came out NEGATIVE: an id-shaped
448
- * number that joins to nothing. STEP puts no upper bound on an entity id
449
- * below the `u32` the readers use, so that is reachable input rather than a
450
- * hypothetical.
451
- */
452
- static UINT32_COLUMNS = new Set([
453
- 'ExpressId', 'EntityId', 'SourceId', 'TargetId', 'RelId',
454
- 'ElementId', 'StoreyId',
455
- 'Index0', 'Index1', 'Index2',
456
- 'VertexStart', 'VertexCount', 'IndexStart', 'IndexCount',
457
- ]);
458
- /**
459
- * NOT in the set above, deliberately: `BuildingId`, `SiteId` and `SpaceId`
460
- * in `SpatialHierarchy.parquet` carry **-1 as "none"** (see
461
- * `writeSpatialHierarchy` - a storey directly under the project has no
462
- * building). Declaring those unsigned turned that sentinel into
463
- * 4294967295: an id-shaped number where an obviously-absent marker used to
464
- * be, which is the exact failure this class of change exists to prevent.
465
- *
466
- * The residual gap is the narrower one: a building or site id at or above
467
- * 2^31 still wraps negative in those three columns. Fixing that properly
468
- * means writing NULL rather than -1 for "none", which changes what every
469
- * consumer reads for an absent parent and is a separate decision from the
470
- * id width.
471
- */
472
473
  async toParquet(columns, floatColumns) {
473
- return columnsToParquet(columns, floatColumns, ParquetExporter.UINT32_COLUMNS);
474
+ return columnsToParquet(columns, floatColumns, PARQUET_UINT32_COLUMNS);
474
475
  }
475
476
  async createZipArchive(files) {
476
477
  const JSZip = (await import('jszip')).default;
@@ -503,35 +504,12 @@ function filterColumns(columns, keep) {
503
504
  }
504
505
  return out;
505
506
  }
506
- function PropertyValueTypeToString(type) {
507
- const names = {
508
- [PropertyValueType.String]: 'String',
509
- [PropertyValueType.Real]: 'Real',
510
- [PropertyValueType.Integer]: 'Integer',
511
- [PropertyValueType.Boolean]: 'Boolean',
512
- [PropertyValueType.Logical]: 'Logical',
513
- [PropertyValueType.Label]: 'Label',
514
- [PropertyValueType.Identifier]: 'Identifier',
515
- [PropertyValueType.Text]: 'Text',
516
- [PropertyValueType.Enum]: 'Enum',
517
- [PropertyValueType.Reference]: 'Reference',
518
- [PropertyValueType.List]: 'List',
519
- };
520
- return names[type] || 'Unknown';
521
- }
522
- // Quantity type conversion - exported for future use when quantities are implemented
523
- export function QuantityTypeToString(type) {
524
- const names = {
525
- [QuantityType.Length]: 'Length',
526
- [QuantityType.Area]: 'Area',
527
- [QuantityType.Volume]: 'Volume',
528
- [QuantityType.Count]: 'Count',
529
- [QuantityType.Weight]: 'Weight',
530
- [QuantityType.Time]: 'Time',
531
- [QuantityType.Number]: 'Number',
532
- };
533
- return names[type] || 'Unknown';
534
- }
507
+ // Kept exported under its original name here (moved to `parquet-type-strings.ts`
508
+ // so the on-demand writers can share it without importing this file, which
509
+ // imports them): nothing in-repo imports it from this path — index.ts never
510
+ // re-exported it — but it was public `export function` surface before the
511
+ // module split, so a re-export costs nothing to keep.
512
+ export { quantityTypeToString as QuantityTypeToString } from './parquet-type-strings.js';
535
513
  function RelationshipTypeToString(type) {
536
514
  const names = {
537
515
  [RelationshipType.ContainsElements]: 'IfcRelContainedInSpatialStructure',