@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.
Files changed (137) hide show
  1. package/README.md +34 -0
  2. package/dist/anonymize-export.d.ts +60 -0
  3. package/dist/anonymize-export.d.ts.map +1 -0
  4. package/dist/anonymize-export.js +117 -0
  5. package/dist/anonymize-export.js.map +1 -0
  6. package/dist/anonymize-placement.d.ts +71 -0
  7. package/dist/anonymize-placement.d.ts.map +1 -0
  8. package/dist/anonymize-placement.js +266 -0
  9. package/dist/anonymize-placement.js.map +1 -0
  10. package/dist/anonymize-scrub.d.ts +64 -0
  11. package/dist/anonymize-scrub.d.ts.map +1 -0
  12. package/dist/anonymize-scrub.js +312 -0
  13. package/dist/anonymize-scrub.js.map +1 -0
  14. package/dist/anonymize-types.d.ts +212 -0
  15. package/dist/anonymize-types.d.ts.map +1 -0
  16. package/dist/anonymize-types.js +5 -0
  17. package/dist/anonymize-types.js.map +1 -0
  18. package/dist/declared-property-type.d.ts +19 -8
  19. package/dist/declared-property-type.d.ts.map +1 -1
  20. package/dist/declared-property-type.js +31 -9
  21. package/dist/declared-property-type.js.map +1 -1
  22. package/dist/entity-type-sets.d.ts +47 -0
  23. package/dist/entity-type-sets.d.ts.map +1 -0
  24. package/dist/entity-type-sets.js +91 -0
  25. package/dist/entity-type-sets.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +5 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/merged-exporter.d.ts +19 -0
  31. package/dist/merged-exporter.d.ts.map +1 -1
  32. package/dist/merged-exporter.js +1 -1
  33. package/dist/merged-exporter.js.map +1 -1
  34. package/dist/parquet-exporter.d.ts +1 -3
  35. package/dist/parquet-exporter.d.ts.map +1 -1
  36. package/dist/parquet-exporter.js +11 -10
  37. package/dist/parquet-exporter.js.map +1 -1
  38. package/dist/property-value-serialization.d.ts +54 -0
  39. package/dist/property-value-serialization.d.ts.map +1 -0
  40. package/dist/property-value-serialization.js +134 -0
  41. package/dist/property-value-serialization.js.map +1 -0
  42. package/dist/reference-collector.d.ts +47 -0
  43. package/dist/reference-collector.d.ts.map +1 -1
  44. package/dist/reference-collector.js +88 -156
  45. package/dist/reference-collector.js.map +1 -1
  46. package/dist/related-entities.d.ts +10 -0
  47. package/dist/related-entities.d.ts.map +1 -0
  48. package/dist/related-entities.js +259 -0
  49. package/dist/related-entities.js.map +1 -0
  50. package/dist/schema-converter.d.ts.map +1 -1
  51. package/dist/schema-converter.js +46 -96
  52. package/dist/schema-converter.js.map +1 -1
  53. package/dist/source-ref-bounds.d.ts +3 -3
  54. package/dist/source-ref-bounds.js +3 -3
  55. package/dist/step-attribute-mutations.d.ts +0 -42
  56. package/dist/step-attribute-mutations.d.ts.map +1 -1
  57. package/dist/step-attribute-mutations.js +17 -147
  58. package/dist/step-attribute-mutations.js.map +1 -1
  59. package/dist/step-attribute-serializers.d.ts +41 -0
  60. package/dist/step-attribute-serializers.d.ts.map +1 -0
  61. package/dist/step-attribute-serializers.js +74 -0
  62. package/dist/step-attribute-serializers.js.map +1 -0
  63. package/dist/step-collection.d.ts +2 -2
  64. package/dist/step-collection.d.ts.map +1 -1
  65. package/dist/step-collection.js +38 -8
  66. package/dist/step-collection.js.map +1 -1
  67. package/dist/step-export-contexts.d.ts +5 -7
  68. package/dist/step-export-contexts.d.ts.map +1 -1
  69. package/dist/step-export-contexts.js +6 -7
  70. package/dist/step-export-contexts.js.map +1 -1
  71. package/dist/step-export-types.d.ts +27 -0
  72. package/dist/step-export-types.d.ts.map +1 -1
  73. package/dist/step-export-types.js.map +1 -1
  74. package/dist/step-exporter.d.ts +3 -3
  75. package/dist/step-exporter.js +4 -4
  76. package/dist/step-exporter.js.map +1 -1
  77. package/dist/step-geometry-types.d.ts +1 -17
  78. package/dist/step-geometry-types.d.ts.map +1 -1
  79. package/dist/step-geometry-types.js +51 -35
  80. package/dist/step-geometry-types.js.map +1 -1
  81. package/dist/step-georeferencing.d.ts +0 -10
  82. package/dist/step-georeferencing.d.ts.map +1 -1
  83. package/dist/step-georeferencing.js +46 -94
  84. package/dist/step-georeferencing.js.map +1 -1
  85. package/dist/step-header.d.ts +11 -1
  86. package/dist/step-header.d.ts.map +1 -1
  87. package/dist/step-header.js +2 -9
  88. package/dist/step-header.js.map +1 -1
  89. package/dist/step-map-unit.d.ts +90 -0
  90. package/dist/step-map-unit.d.ts.map +1 -0
  91. package/dist/step-map-unit.js +277 -0
  92. package/dist/step-map-unit.js.map +1 -0
  93. package/dist/step-overlay-attribute-overrides.d.ts +32 -0
  94. package/dist/step-overlay-attribute-overrides.d.ts.map +1 -0
  95. package/dist/step-overlay-attribute-overrides.js +83 -0
  96. package/dist/step-overlay-attribute-overrides.js.map +1 -0
  97. package/dist/step-overlay-entities.d.ts +3 -3
  98. package/dist/step-pass-builder.d.ts +4 -3
  99. package/dist/step-pass-builder.d.ts.map +1 -1
  100. package/dist/step-pass-builder.js.map +1 -1
  101. package/dist/step-property-set-collection.d.ts +24 -0
  102. package/dist/step-property-set-collection.d.ts.map +1 -0
  103. package/dist/step-property-set-collection.js +286 -0
  104. package/dist/step-property-set-collection.js.map +1 -0
  105. package/dist/step-property-set-generators.d.ts +20 -0
  106. package/dist/step-property-set-generators.d.ts.map +1 -0
  107. package/dist/step-property-set-generators.js +98 -0
  108. package/dist/step-property-set-generators.js.map +1 -0
  109. package/dist/step-property-set-index.d.ts +25 -0
  110. package/dist/step-property-set-index.d.ts.map +1 -0
  111. package/dist/step-property-set-index.js +69 -0
  112. package/dist/step-property-set-index.js.map +1 -0
  113. package/dist/step-property-set-readers.d.ts +158 -0
  114. package/dist/step-property-set-readers.d.ts.map +1 -0
  115. package/dist/step-property-set-readers.js +251 -0
  116. package/dist/step-property-set-readers.js.map +1 -0
  117. package/dist/step-property-sets.d.ts +2 -186
  118. package/dist/step-property-sets.d.ts.map +1 -1
  119. package/dist/step-property-sets.js +22 -674
  120. package/dist/step-property-sets.js.map +1 -1
  121. package/dist/step-serialization.d.ts +17 -51
  122. package/dist/step-serialization.d.ts.map +1 -1
  123. package/dist/step-serialization.js +19 -128
  124. package/dist/step-serialization.js.map +1 -1
  125. package/dist/step-source-iteration.d.ts +7 -5
  126. package/dist/step-source-iteration.d.ts.map +1 -1
  127. package/dist/step-source-iteration.js +3 -3
  128. package/dist/step-source-iteration.js.map +1 -1
  129. package/dist/subset-entity-reader.d.ts +103 -0
  130. package/dist/subset-entity-reader.d.ts.map +1 -0
  131. package/dist/subset-entity-reader.js +102 -0
  132. package/dist/subset-entity-reader.js.map +1 -0
  133. package/dist/subset-roots.d.ts +58 -0
  134. package/dist/subset-roots.d.ts.map +1 -0
  135. package/dist/subset-roots.js +103 -0
  136. package/dist/subset-roots.js.map +1 -0
  137. package/package.json +7 -7
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Scrub pass for the "anonymized isolated export" feature (#2934, plan A5):
3
+ * deterministic per-type pseudonyms for `Name`/`LongName`/`Description`/`Tag`
4
+ * on every included `IfcRoot`, `GlobalId` regeneration (the old->new map is
5
+ * handed back as `guidMap` rather than written into the file — see
6
+ * `AnonymizeResult.guidMap` on `anonymize-types.ts`), owner-history scrubbing
7
+ * (`IfcPerson`/`IfcOrganization`/`IfcPersonAndOrganization`), and clearing an
8
+ * included `IfcTypeObject`'s `HasPropertySets` slot.
9
+ *
10
+ * Expressed as mutations on a private `MutablePropertyView`, never as a text
11
+ * rewrite — see the plan's "Key mechanism choice": the effective-index
12
+ * closure walk (`effective-index.ts`) already reacts to a queued
13
+ * `setAttribute`/`setPositionalAttribute`, so these mutations are visible to
14
+ * `step-collection.ts`'s subset closure the same way any other overlay edit
15
+ * is, with no second code path that could disagree about what got scrubbed.
16
+ *
17
+ * Deliberately does NOT use `store.entities.getTypeName` for the pseudonym
18
+ * label despite the plan sketch spelling it that way: that table only
19
+ * indexes product-ish entities and answers `'Unknown'` for the rest (see its
20
+ * own doc) — including `IfcPropertySet` (an `IfcRoot` descendant this module
21
+ * must be able to pseudonymize under `keepPropertySets`) and every
22
+ * `IfcRel*` relationship (also `IfcRoot` descendants, per `IFC_ROOT_TYPES`).
23
+ * `'Unknown-1'`, `'Unknown-2'`, … colliding across every such class would be
24
+ * both a worse pseudonym AND a residual identifying signal (two entities
25
+ * sharing a label leaks that they were originally the SAME kind of thing).
26
+ * `normalizeIfcTypeName` off the effective index's own class answer has no
27
+ * such gap.
28
+ */
29
+ import type { IfcSourceBytes } from '@ifc-lite/parser';
30
+ import type { MutablePropertyView } from '@ifc-lite/mutations';
31
+ import type { EffectiveEntityIndex } from './effective-index.js';
32
+ import type { AnonymizeOptions } from './anonymize-types.js';
33
+ /** The subset of `AnonymizeOptions` this pass reads. */
34
+ export type ScrubOptions = Pick<AnonymizeOptions, 'pseudonymizeNames' | 'pseudonymizeAllNames' | 'regenerateGlobalIds' | 'scrubOwnerHistory' | 'neutralizeCurrency' | 'keepPropertySets' | 'guidRandom'>;
35
+ /** What one `applyScrub` call produced. */
36
+ export interface ScrubResult {
37
+ /** Original `GlobalId` -> regenerated `GlobalId`, one entry per
38
+ * `includedIds` `IfcRoot` entity whose GlobalId this pass actually
39
+ * regenerated. Empty when `regenerateGlobalIds` is `false`. */
40
+ guidMap: Map<string, string>;
41
+ /** How many entities this pass wrote at least one mutation for: every
42
+ * scrubbed `includedIds` root, plus every `IfcPerson`/`IfcOrganization`/
43
+ * `IfcPersonAndOrganization` found anywhere in `index` and scrubbed. */
44
+ scrubbedCount: number;
45
+ /** Non-fatal notices — an `includedIds` root this pass could not read (no
46
+ * source bytes, or a record that does not parse) or whose `GlobalId`
47
+ * could not be decoded, left untouched by the affected sub-step. */
48
+ warnings: string[];
49
+ }
50
+ /**
51
+ * Scrub `includedIds`' `IfcRoot` entities plus every owner-history entity in
52
+ * `index`, queuing every change on `view` (never writing into `store`).
53
+ *
54
+ * `index` must be the EFFECTIVE index (`getEffectiveEntityIndex`), not a
55
+ * bare source index — see that module's doc for why an overlay-created or
56
+ * retyped entity answers differently through it. `store` need only expose
57
+ * `source`: `readEntityArgs` reads SOURCE-backed records only, which is the
58
+ * entire domain of what this pass mutates (it creates no entities).
59
+ */
60
+ export declare function applyScrub(store: {
61
+ readonly source: IfcSourceBytes;
62
+ readonly schemaVersion?: string;
63
+ }, index: EffectiveEntityIndex, includedIds: ReadonlySet<number>, view: MutablePropertyView, opts?: ScrubOptions): ScrubResult;
64
+ //# sourceMappingURL=anonymize-scrub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymize-scrub.d.ts","sourceRoot":"","sources":["../src/anonymize-scrub.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAuC7D,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,IAAI,CAC7B,gBAAgB,EACd,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,GACpB,kBAAkB,GAClB,YAAY,CACf,CAAC;AAEF,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B;;oEAEgE;IAChE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;6EAEyE;IACzE,aAAa,EAAE,MAAM,CAAC;IACtB;;yEAEqE;IACrE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3E,KAAK,EAAE,oBAAoB,EAC3B,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,IAAI,EAAE,mBAAmB,EACzB,IAAI,GAAE,YAAiB,GACtB,WAAW,CAsIb"}
@@ -0,0 +1,312 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { getAttributeNamesAcrossSchemas, normalizeIfcTypeName } from '@ifc-lite/parser';
5
+ import { decodeStepStringLiteral, generateIfcGuid } from '@ifc-lite/encoding';
6
+ import { attrIndex, readEntityArgs, stepSourceSchema } from './subset-entity-reader.js';
7
+ import { IFC_ROOT_TYPES } from './subset-roots.js';
8
+ import { HAS_PROPERTY_SETS_SLOT, isTypeClass } from './type-owned-psets.js';
9
+ /** `Name`/`LongName`/`Description`/`Tag` — the `IfcRoot`-declared text
10
+ * fields a subset export pseudonymizes when the class declares them and
11
+ * the source value is actually set (not already `$`). Per the decision
12
+ * doc: "pseudonymize Name/LongName/Description/Tag on every exported
13
+ * IfcRoot as `<IfcType>-<n>`". */
14
+ const PSEUDONYM_ATTRIBUTES = ['Name', 'LongName', 'Description', 'Tag'];
15
+ /** Extra root-entity text fields covered by `pseudonymizeAllNames`:
16
+ * `IfcObject.ObjectType` (authoring tools put the family/type name there —
17
+ * "Basic Wall: <project> Exterior 300") and `IfcProject.Phase`. */
18
+ const ROOT_ALL_NAMES_ATTRIBUTES = ['ObjectType', 'Phase'];
19
+ /** Non-`IfcRoot` text fields covered by `pseudonymizeAllNames`. Only a
20
+ * quoted-string token is ever rewritten, so an enum-valued `Name`
21
+ * (`IfcSIUnit.Name = .METRE.`) or a `$` passes through untouched. */
22
+ const NON_ROOT_NAME_ATTRIBUTES = ['Name', 'LongName', 'Description', 'ProfileName', 'LayerSetName'];
23
+ /** Non-root classes whose `Name` is schema semantics rather than authored
24
+ * text, left alone by `pseudonymizeAllNames`: the authoring application
25
+ * (kept by decision — it is debugging signal), and property / quantity
26
+ * names (`FireRating`, `Width`), which are only exported at all under
27
+ * `keepPropertySets` and are what a property-debugging repro needs. */
28
+ function isNonRootNameExempt(typeUpper) {
29
+ return typeUpper === 'IFCAPPLICATION'
30
+ // Owner-history actors are `scrubOwnerHistory`'s job (their whole
31
+ // attribute list, not just Name), and must stay untouched when it is off.
32
+ || typeUpper === 'IFCPERSON'
33
+ || typeUpper === 'IFCORGANIZATION'
34
+ || typeUpper === 'IFCPERSONANDORGANIZATION'
35
+ || typeUpper.startsWith('IFCPROPERTY')
36
+ || typeUpper.startsWith('IFCPHYSICAL')
37
+ || typeUpper.startsWith('IFCQUANTITY');
38
+ }
39
+ /**
40
+ * Scrub `includedIds`' `IfcRoot` entities plus every owner-history entity in
41
+ * `index`, queuing every change on `view` (never writing into `store`).
42
+ *
43
+ * `index` must be the EFFECTIVE index (`getEffectiveEntityIndex`), not a
44
+ * bare source index — see that module's doc for why an overlay-created or
45
+ * retyped entity answers differently through it. `store` need only expose
46
+ * `source`: `readEntityArgs` reads SOURCE-backed records only, which is the
47
+ * entire domain of what this pass mutates (it creates no entities).
48
+ */
49
+ export function applyScrub(store, index, includedIds, view, opts = {}) {
50
+ // Model's OWN schema (#3309 review finding): the sweeps below walk
51
+ // arbitrary types, and the bundled schemas don't always agree on order.
52
+ const schema = stepSourceSchema(store.schemaVersion);
53
+ const pseudonymizeNames = opts.pseudonymizeNames ?? true;
54
+ const pseudonymizeAllNames = opts.pseudonymizeAllNames ?? true;
55
+ const regenerateGlobalIds = opts.regenerateGlobalIds ?? true;
56
+ const scrubOwnerHistory = opts.scrubOwnerHistory ?? true;
57
+ const neutralizeCurrency = opts.neutralizeCurrency ?? true;
58
+ const keepPropertySets = opts.keepPropertySets ?? false;
59
+ const guidMap = new Map();
60
+ const warnings = [];
61
+ let scrubbedCount = 0;
62
+ // Ascending expressId order, per plan A5 ("per-type counter in ascending
63
+ // expressId order") — determinism depends on this sort, not on Set
64
+ // insertion order, which `includedIds` makes no promise about.
65
+ const rootIds = [...includedIds]
66
+ .filter((id) => IFC_ROOT_TYPES.has(index.typeOf(id) ?? ''))
67
+ .sort((a, b) => a - b);
68
+ const pseudonymCounters = new Map();
69
+ for (const id of rootIds) {
70
+ const type = index.typeOf(id);
71
+ if (type === undefined)
72
+ continue; // gone from the effective model (tombstoned); nothing to scrub
73
+ const record = readEntityArgs(store, index, id);
74
+ if (!record) {
75
+ warnings.push(`Entity #${id} (${normalizeIfcTypeName(type)}): its source record could not be read, `
76
+ + 'so its name/GlobalId scrub was skipped and it is exported unmodified.');
77
+ continue;
78
+ }
79
+ let touched = false;
80
+ if (regenerateGlobalIds) {
81
+ const guidIdx = attrIndex(type, 'GlobalId', schema);
82
+ const oldGuid = guidIdx !== -1 ? decodeQuotedStepString(record.args[guidIdx]) : null;
83
+ if (oldGuid !== null) {
84
+ const newGuid = generateIfcGuid(opts.guidRandom);
85
+ // Positional: `setAttribute` re-resolves the slot by name at
86
+ // serialize time via `step-attribute-mutations.ts`'s own (still
87
+ // pinned-IFC4-first) lookup, undoing `guidIdx` above (#3309).
88
+ view.setPositionalAttribute(id, guidIdx, newGuid);
89
+ guidMap.set(oldGuid, newGuid);
90
+ touched = true;
91
+ }
92
+ else {
93
+ warnings.push(`Entity #${id} (${normalizeIfcTypeName(type)}): its GlobalId could not be read, `
94
+ + 'so it was not regenerated.');
95
+ }
96
+ }
97
+ if (pseudonymizeNames || pseudonymizeAllNames) {
98
+ const pseudonym = nextPseudonym(pseudonymCounters, type);
99
+ const attrs = [
100
+ ...(pseudonymizeNames ? PSEUDONYM_ATTRIBUTES : []),
101
+ ...(pseudonymizeAllNames ? ROOT_ALL_NAMES_ATTRIBUTES : []),
102
+ ];
103
+ for (const attr of attrs) {
104
+ const idx = attrIndex(type, attr, schema);
105
+ if (idx === -1 || idx >= record.args.length || !isQuotedStepString(record.args[idx]))
106
+ continue;
107
+ view.setPositionalAttribute(id, idx, pseudonym); // positional — see the GlobalId write above
108
+ touched = true;
109
+ }
110
+ }
111
+ if (!keepPropertySets && isTypeClass(type)) {
112
+ view.setPositionalAttribute(id, HAS_PROPERTY_SETS_SLOT, null);
113
+ touched = true;
114
+ }
115
+ if (touched)
116
+ scrubbedCount += 1;
117
+ }
118
+ if (pseudonymizeAllNames) {
119
+ scrubbedCount += pseudonymizeNonRootNames(store, index, view, pseudonymCounters);
120
+ }
121
+ if (scrubOwnerHistory || neutralizeCurrency) {
122
+ for (const [id, ref] of index) {
123
+ const type = index.typeOf(id) ?? ref.type.toUpperCase();
124
+ if (type === 'IFCMONETARYUNIT') {
125
+ if (neutralizeCurrency && neutralizeMonetaryUnit(store, index, view, id))
126
+ scrubbedCount += 1;
127
+ continue;
128
+ }
129
+ if (!scrubOwnerHistory)
130
+ continue;
131
+ // Below (and IFCMONETARYUNIT above): FIXED literal types, each
132
+ // slot checked directly against all three `ENTITIES_*` tables — no
133
+ // `schema` argument needed (unlike the arbitrary-type sweeps above).
134
+ switch (type) {
135
+ case 'IFCPERSON':
136
+ scrubPerson(view, id);
137
+ scrubbedCount += 1;
138
+ break;
139
+ case 'IFCORGANIZATION':
140
+ scrubOrganization(view, id);
141
+ scrubbedCount += 1;
142
+ break;
143
+ case 'IFCPERSONANDORGANIZATION': {
144
+ const rolesIdx = attrIndex('IFCPERSONANDORGANIZATION', 'Roles');
145
+ if (rolesIdx !== -1)
146
+ view.setPositionalAttribute(id, rolesIdx, null);
147
+ scrubbedCount += 1;
148
+ break;
149
+ }
150
+ case 'IFCOWNERHISTORY': {
151
+ // Dates pin the model to a project timeline: creation -> epoch 0
152
+ // (the attribute is mandatory, an IfcTimeStamp INTEGER), last
153
+ // modification -> $ (optional).
154
+ const createdIdx = attrIndex('IFCOWNERHISTORY', 'CreationDate');
155
+ const modifiedIdx = attrIndex('IFCOWNERHISTORY', 'LastModifiedDate');
156
+ if (createdIdx !== -1)
157
+ view.setPositionalAttribute(id, createdIdx, 0);
158
+ if (modifiedIdx !== -1)
159
+ view.setPositionalAttribute(id, modifiedIdx, null);
160
+ scrubbedCount += 1;
161
+ break;
162
+ }
163
+ case 'IFCAPPLICATION': {
164
+ // Keep ApplicationFullName/ApplicationIdentifier (the tool), drop
165
+ // Version (a vendor build string can carry the licence region).
166
+ const versionIdx = attrIndex('IFCAPPLICATION', 'Version');
167
+ if (versionIdx !== -1)
168
+ view.setPositionalAttribute(id, versionIdx, null);
169
+ scrubbedCount += 1;
170
+ break;
171
+ }
172
+ default:
173
+ break;
174
+ }
175
+ }
176
+ }
177
+ return { guidMap, scrubbedCount, warnings };
178
+ }
179
+ /**
180
+ * `IfcMonetaryUnit.Currency` -> US dollars, in the source file's own spelling:
181
+ * IFC2X3 declares it as the `IfcCurrencyEnum` (`.NOK.`), IFC4+ as an
182
+ * `IfcLabel` (`'NOK'`). Returns whether a mutation was queued.
183
+ */
184
+ function neutralizeMonetaryUnit(store, index, view, id) {
185
+ const idx = attrIndex('IFCMONETARYUNIT', 'Currency');
186
+ if (idx === -1)
187
+ return false;
188
+ const record = readEntityArgs(store, index, id);
189
+ const token = record?.args[idx];
190
+ if (token === undefined || token === '$')
191
+ return false;
192
+ const usd = token.startsWith('.') ? '.USD.' : 'USD';
193
+ if (token === '.USD.' || token === "'USD'")
194
+ return false;
195
+ view.setPositionalAttribute(id, idx, usd);
196
+ return true;
197
+ }
198
+ /** Next `<IfcType>-<n>` for `typeUpper`, advancing its per-type counter. */
199
+ function nextPseudonym(counters, typeUpper) {
200
+ const typeName = normalizeIfcTypeName(typeUpper);
201
+ const n = (counters.get(typeName) ?? 0) + 1;
202
+ counters.set(typeName, n);
203
+ return `${typeName}-${n}`;
204
+ }
205
+ function isQuotedStepString(token) {
206
+ return token !== undefined && token.length >= 2 && token[0] === "'" && token[token.length - 1] === "'";
207
+ }
208
+ /**
209
+ * `pseudonymizeAllNames` for the non-`IfcRoot` half: walk EVERY entity in
210
+ * `index` (not just `includedIds` — styles, materials, layers and profiles
211
+ * reach the export through the forward closure, which only exists after the
212
+ * exporter runs) and pseudonymize each quoted-string
213
+ * `NON_ROOT_NAME_ATTRIBUTES` slot. A mutation queued on an entity the
214
+ * closure never reaches is simply never serialized, so over-approximating
215
+ * here costs nothing but time, and the per-type slot lookup is cached so the
216
+ * walk touches source bytes only for classes that declare such a slot.
217
+ * Returns how many entities received at least one mutation.
218
+ */
219
+ function pseudonymizeNonRootNames(store, index, view, counters) {
220
+ // Same schema-aware resolution as `applyScrub`'s root pass: this walks
221
+ // every non-root type (materials, styles, layers, profiles, …), e.g.
222
+ // IFC2X3's `IfcApprovalRelationship.Name` sits at slot 3, IFC4's at 0.
223
+ const schema = stepSourceSchema(store.schemaVersion);
224
+ const slotsByType = new Map();
225
+ const slotsFor = (typeUpper) => {
226
+ let slots = slotsByType.get(typeUpper);
227
+ if (slots === undefined) {
228
+ slots = IFC_ROOT_TYPES.has(typeUpper) || isNonRootNameExempt(typeUpper)
229
+ ? []
230
+ : NON_ROOT_NAME_ATTRIBUTES
231
+ .map((name) => [name, attrIndex(typeUpper, name, schema)])
232
+ .filter(([, idx]) => idx !== -1);
233
+ slotsByType.set(typeUpper, slots);
234
+ }
235
+ return slots;
236
+ };
237
+ // Ascending expressId for the same determinism guarantee as the root pass.
238
+ const ids = [];
239
+ for (const [id] of index)
240
+ ids.push(id);
241
+ ids.sort((a, b) => a - b);
242
+ let scrubbed = 0;
243
+ for (const id of ids) {
244
+ const type = index.typeOf(id);
245
+ if (type === undefined)
246
+ continue;
247
+ const slots = slotsFor(type);
248
+ if (slots.length === 0)
249
+ continue;
250
+ const record = readEntityArgs(store, index, id);
251
+ if (!record)
252
+ continue; // overlay-created (e.g. the placement clones) or unreadable: nothing authored to scrub
253
+ let pseudonym = null;
254
+ for (const [, idx] of slots) {
255
+ if (idx >= record.args.length || !isQuotedStepString(record.args[idx]))
256
+ continue;
257
+ pseudonym ??= nextPseudonym(counters, type);
258
+ view.setPositionalAttribute(id, idx, pseudonym); // positional — same reasoning as `applyScrub`'s writes
259
+ }
260
+ if (pseudonym !== null)
261
+ scrubbed += 1;
262
+ }
263
+ return scrubbed;
264
+ }
265
+ /**
266
+ * Decode a raw STEP string-literal token (`'…'`, quotes still attached, as
267
+ * `SubsetEntityArgs.args` holds it — see `subset-entity-reader.ts`) to its
268
+ * Unicode value, or `null` when the token is not a quoted string: `$`, a
269
+ * numeric/enum/reference token, or simply absent because the record's
270
+ * trailing optional arguments were omitted short.
271
+ */
272
+ function decodeQuotedStepString(token) {
273
+ if (token === undefined || token.length < 2 || token[0] !== "'" || token[token.length - 1] !== "'") {
274
+ return null;
275
+ }
276
+ return decodeStepStringLiteral(token.slice(1, -1));
277
+ }
278
+ /** `IfcPerson.FamilyName` -> `'Anonymous'`; every other declared attribute
279
+ * (`Identification`, `GivenName`, `MiddleNames`, `PrefixTitles`,
280
+ * `SuffixTitles`, `Roles`, `Addresses`) -> `$`. Per the decision doc the
281
+ * ENTIRE attribute list is identifying fields/Roles/Addresses, but a total
282
+ * wipe emits `IFCPERSON($,$,$,$,$,$,$,$)`, which violates the schema's
283
+ * `IdentifiablePersonName` WHERE rule (`Identification`, `FamilyName`, or
284
+ * `GivenName` must be set) — a validator or strict viewer can then reject
285
+ * the very file this export exists to hand to a maintainer. Keeping one
286
+ * fixed, non-identifying `FamilyName` satisfies the rule without leaking
287
+ * anything. Schema-driven (`getAttributeNamesAcrossSchemas`) rather than a
288
+ * hardcoded slot count, the same way every other positional write in this
289
+ * package resolves slots. */
290
+ function scrubPerson(view, id) {
291
+ const names = getAttributeNamesAcrossSchemas('IFCPERSON');
292
+ const familyNameIdx = names.indexOf('FamilyName');
293
+ for (let i = 0; i < names.length; i++) {
294
+ if (i === familyNameIdx)
295
+ continue;
296
+ view.setPositionalAttribute(id, i, null);
297
+ }
298
+ view.setAttribute(id, 'FamilyName', 'Anonymous');
299
+ }
300
+ /** `IfcOrganization.Name` -> `'Anonymous'`; every other declared attribute
301
+ * (`Identification`, `Description`, `Roles`, `Addresses`) -> `$`. */
302
+ function scrubOrganization(view, id) {
303
+ const names = getAttributeNamesAcrossSchemas('IFCORGANIZATION');
304
+ const nameIdx = names.indexOf('Name');
305
+ for (let i = 0; i < names.length; i++) {
306
+ if (i === nameIdx)
307
+ continue;
308
+ view.setPositionalAttribute(id, i, null);
309
+ }
310
+ view.setAttribute(id, 'Name', 'Anonymous');
311
+ }
312
+ //# sourceMappingURL=anonymize-scrub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymize-scrub.js","sourceRoot":"","sources":["../src/anonymize-scrub.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAgC/D,OAAO,EAAE,8BAA8B,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAI9E,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE5E;;;;mCAImC;AACnC,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,CAAU,CAAC;AAEjF;;oEAEoE;AACpE,MAAM,yBAAyB,GAAG,CAAC,YAAY,EAAE,OAAO,CAAU,CAAC;AAEnE;;sEAEsE;AACtE,MAAM,wBAAwB,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,CAAU,CAAC;AAE7G;;;;wEAIwE;AACxE,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,OAAO,SAAS,KAAK,gBAAgB;QACnC,kEAAkE;QAClE,0EAA0E;WACvE,SAAS,KAAK,WAAW;WACzB,SAAS,KAAK,iBAAiB;WAC/B,SAAS,KAAK,0BAA0B;WACxC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC;WACnC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC;WACnC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAC3C,CAAC;AA8BD;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CACxB,KAA2E,EAC3E,KAA2B,EAC3B,WAAgC,EAChC,IAAyB,EACzB,OAAqB,EAAE;IAEvB,mEAAmE;IACnE,wEAAwE;IACxE,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACrD,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC;IACzD,MAAM,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC;IAC/D,MAAM,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC;IAC7D,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC;IACzD,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC;IAC3D,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,KAAK,CAAC;IAExD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,yEAAyE;IACzE,mEAAmE;IACnE,+DAA+D;IAC/D,MAAM,OAAO,GAAG,CAAC,GAAG,WAAW,CAAC;SAC7B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;SAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEpD,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS,CAAC,+DAA+D;QAEjG,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,QAAQ,CAAC,IAAI,CACX,WAAW,EAAE,KAAK,oBAAoB,CAAC,IAAI,CAAC,0CAA0C;kBAClF,uEAAuE,CAC5E,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,IAAI,mBAAmB,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACrF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBACjD,6DAA6D;gBAC7D,gEAAgE;gBAChE,8DAA8D;gBAC9D,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9B,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CACX,WAAW,EAAE,KAAK,oBAAoB,CAAC,IAAI,CAAC,qCAAqC;sBAC7E,4BAA4B,CACjC,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,iBAAiB,IAAI,oBAAoB,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAG,aAAa,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;YACzD,MAAM,KAAK,GAAsB;gBAC/B,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3D,CAAC;YACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC1C,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAAE,SAAS;gBAC/F,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,4CAA4C;gBAC7F,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,gBAAgB,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAC;YAC9D,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QAED,IAAI,OAAO;YAAE,aAAa,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,oBAAoB,EAAE,CAAC;QACzB,aAAa,IAAI,wBAAwB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,iBAAiB,IAAI,kBAAkB,EAAE,CAAC;QAC5C,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACxD,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC/B,IAAI,kBAAkB,IAAI,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;oBAAE,aAAa,IAAI,CAAC,CAAC;gBAC7F,SAAS;YACX,CAAC;YACD,IAAI,CAAC,iBAAiB;gBAAE,SAAS;YACjC,+DAA+D;YAC/D,mEAAmE;YACnE,qEAAqE;YACrE,QAAQ,IAAI,EAAE,CAAC;gBACb,KAAK,WAAW;oBACd,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBACtB,aAAa,IAAI,CAAC,CAAC;oBACnB,MAAM;gBACR,KAAK,iBAAiB;oBACpB,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBAC5B,aAAa,IAAI,CAAC,CAAC;oBACnB,MAAM;gBACR,KAAK,0BAA0B,CAAC,CAAC,CAAC;oBAChC,MAAM,QAAQ,GAAG,SAAS,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;oBAChE,IAAI,QAAQ,KAAK,CAAC,CAAC;wBAAE,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;oBACrE,aAAa,IAAI,CAAC,CAAC;oBACnB,MAAM;gBACR,CAAC;gBACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;oBACvB,iEAAiE;oBACjE,8DAA8D;oBAC9D,gCAAgC;oBAChC,MAAM,UAAU,GAAG,SAAS,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;oBAChE,MAAM,WAAW,GAAG,SAAS,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;oBACrE,IAAI,UAAU,KAAK,CAAC,CAAC;wBAAE,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;oBACtE,IAAI,WAAW,KAAK,CAAC,CAAC;wBAAE,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;oBAC3E,aAAa,IAAI,CAAC,CAAC;oBACnB,MAAM;gBACR,CAAC;gBACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;oBACtB,kEAAkE;oBAClE,gEAAgE;oBAChE,MAAM,UAAU,GAAG,SAAS,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;oBAC1D,IAAI,UAAU,KAAK,CAAC,CAAC;wBAAE,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;oBACzE,aAAa,IAAI,CAAC,CAAC;oBACnB,MAAM;gBACR,CAAC;gBACD;oBACE,MAAM;YACV,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,SAAS,sBAAsB,CAC7B,KAA0C,EAC1C,KAA2B,EAC3B,IAAyB,EACzB,EAAU;IAEV,MAAM,GAAG,GAAG,SAAS,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACvD,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IACpD,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAC5E,SAAS,aAAa,CAAC,QAA6B,EAAE,SAAiB;IACrE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC1B,OAAO,GAAG,QAAQ,IAAI,CAAC,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAyB;IACnD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;AACzG,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,wBAAwB,CAC/B,KAA2E,EAC3E,KAA2B,EAC3B,IAAyB,EACzB,QAA6B;IAE7B,uEAAuE;IACvE,qEAAqE;IACrE,uEAAuE;IACvE,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,IAAI,GAAG,EAA+D,CAAC;IAC3F,MAAM,QAAQ,GAAG,CAAC,SAAiB,EAAE,EAAE;QACrC,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,mBAAmB,CAAC,SAAS,CAAC;gBACrE,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,wBAAwB;qBACvB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAU,CAAC;qBAClE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACrC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,2EAA2E;IAC3E,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK;QAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAE1B,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM;YAAE,SAAS,CAAC,uFAAuF;QAC9G,IAAI,SAAS,GAAkB,IAAI,CAAC;QACpC,KAAK,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC;YAC5B,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAE,SAAS;YACjF,SAAS,KAAK,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5C,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,uDAAuD;QAC1G,CAAC;QACD,IAAI,SAAS,KAAK,IAAI;YAAE,QAAQ,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,sBAAsB,CAAC,KAAyB;IACvD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QACnG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,uBAAuB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;8BAW8B;AAC9B,SAAS,WAAW,CAAC,IAAyB,EAAE,EAAU;IACxD,MAAM,KAAK,GAAG,8BAA8B,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,aAAa;YAAE,SAAS;QAClC,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AACnD,CAAC;AAED;sEACsE;AACtE,SAAS,iBAAiB,CAAC,IAAyB,EAAE,EAAU;IAC9D,MAAM,KAAK,GAAG,8BAA8B,CAAC,iBAAiB,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,OAAO;YAAE,SAAS;QAC5B,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * The public vocabulary of the "anonymized isolated export" feature (#2934):
3
+ * what a caller passes to `collectRelatedEntities` / `exportAnonymizedSubset`
4
+ * and what it gets back. Declarations only, split out the same way
5
+ * `step-export-types.ts` is split from `step-exporter.ts` — so the two
6
+ * orchestrators (`related-entities.ts`, `anonymize-export.ts`) and every
7
+ * caller (viewer dialog, CLI) share one definition instead of drifting.
8
+ *
9
+ * Keyed by EXPRESS relationship name throughout (`IfcRelVoidsElement`, not
10
+ * `voids`), per this repo's IFC-schema-fidelity rule: these ARE the
11
+ * `IfcRel*` entity types the export walks, not a derived collection that
12
+ * would need its own name.
13
+ */
14
+ import type { RandomSource } from '@ifc-lite/encoding';
15
+ /**
16
+ * Which relationship kinds `collectRelatedEntities` expands from the seed
17
+ * selection, and how far. Every boolean defaults to what the doc comment
18
+ * says, not to `false` — an omitted `RelatedEntityOptions` (or an omitted
19
+ * field within one) must reproduce the bug the seed exhibits, which usually
20
+ * needs its host/opening/type/material context, not a bare, disconnected
21
+ * element.
22
+ */
23
+ export interface RelatedEntityOptions {
24
+ /** Openings of an included host element. Default `true`. */
25
+ IfcRelVoidsElement?: boolean;
26
+ /** The filler↔opening↔host chain (a window/door and the wall it sits in).
27
+ * Default `true`. */
28
+ IfcRelFillsElement?: boolean;
29
+ /**
30
+ * Aggregate parents/children (`IfcRelAggregates`): `'up'` walks to
31
+ * `RelatingObject` only, `'down'` to `RelatedObjects` only, `'both'` walks
32
+ * both directions, `'none'` skips it. Default `'both'`.
33
+ */
34
+ IfcRelAggregates?: 'up' | 'down' | 'both' | 'none';
35
+ /**
36
+ * Nesting parents/children (`IfcRelNests`) — a separate EXPRESS
37
+ * relationship from `IfcRelAggregates` even though the parser folds both
38
+ * into the same aggregation edge type on `RelationshipGraph`; this option
39
+ * is split out from the one above by REL ENTITY TYPE, not by graph edge,
40
+ * so a caller can toggle them independently. Default `'down'`.
41
+ */
42
+ IfcRelNests?: 'up' | 'down' | 'both' | 'none';
43
+ /** An included object's `IfcTypeObject` (`IfcRelDefinesByType`). Default `true`. */
44
+ IfcRelDefinesByType?: boolean;
45
+ /** An included object's material assignment (`IfcRelAssociatesMaterial`).
46
+ * Default `true`. */
47
+ IfcRelAssociatesMaterial?: boolean;
48
+ /**
49
+ * The spatial containment chain (`IfcRelContainedInSpatialStructure`) up to
50
+ * storey/building/site. Default `true`; regardless of this flag,
51
+ * `IfcProject` is ALWAYS included — a STEP file with no project is not a
52
+ * valid reproduction of anything.
53
+ */
54
+ IfcRelContainedInSpatialStructure?: boolean;
55
+ /**
56
+ * An included object's property sets (`IfcRelDefinesByProperties`).
57
+ * Default `false` — psets are the majority of what this feature exists to
58
+ * strip, so expanding them is opt-in, and turning this on still pulls in
59
+ * `ePSet_MapConversion`/`ePSet_ProjectedCRS` style georeferencing psets
60
+ * unless those are separately excluded.
61
+ */
62
+ IfcRelDefinesByProperties?: boolean;
63
+ /**
64
+ * BFS depth (both directions) to walk `IfcRelConnectsPathElements` from an
65
+ * included element — e.g. a wall's structurally-connected neighbour.
66
+ * Default `0` (no expansion).
67
+ */
68
+ IfcRelConnectsPathElementsDepth?: number;
69
+ }
70
+ /**
71
+ * One relationship kind/direction's contribution to a `collectRelatedEntities`
72
+ * result — e.g. "the openings reached via `IfcRelVoidsElement`, host role".
73
+ */
74
+ export interface RelatedEntityGroup {
75
+ /** EXPRESS relationship type name, e.g. `IfcRelVoidsElement`. */
76
+ relationship: string;
77
+ /** Which end of the relationship this group walked — e.g. `'opening'` /
78
+ * `'host'` for `IfcRelVoidsElement`, matching the doc at each walker's own
79
+ * call site rather than a fixed enum, since the meaningful roles differ
80
+ * per relationship kind. */
81
+ role: string;
82
+ /** ExpressIds of the related OBJECTS this group reached (never the
83
+ * `IfcRel*` relationship entity itself). */
84
+ expressIds: number[];
85
+ /** ExpressIds of the `IfcRel*` relationship entities that produced this
86
+ * group — these must ALSO be included in the export subset, or the
87
+ * association they represent has nothing naming it. */
88
+ relationshipIds: number[];
89
+ }
90
+ /** The full result of one `collectRelatedEntities` call. */
91
+ export interface RelatedEntities {
92
+ /** The caller's original seed selection, echoed back unfiltered. */
93
+ seeds: number[];
94
+ /** One entry per (relationship, role) that contributed anything, in walk
95
+ * order — the shape a UI groups a related-entity list by. */
96
+ groups: RelatedEntityGroup[];
97
+ /** The union of every id reached: seeds, every group's `expressIds`, and
98
+ * every group's `relationshipIds`. This — not any one group — is what a
99
+ * caller passes as `StepExportOptions.subsetEntityIds`. */
100
+ all: Set<number>;
101
+ /** `true` when the walk's work budget was exhausted before it reached a
102
+ * fixpoint (`Bounding walks over file-supplied references`, AGENTS.md) —
103
+ * the result is a valid, boundable subset of what a complete walk would
104
+ * have found, not the complete answer. */
105
+ truncated: boolean;
106
+ }
107
+ /**
108
+ * What to scrub, and how, when exporting the `subsetEntityIds` selection as
109
+ * an anonymized STEP file. Every field defaults to the value stated in its
110
+ * own doc comment (all `true`/enabled except where noted) — an anonymized
111
+ * export with no options at all is the maximally-scrubbed one, since the
112
+ * whole point of the feature is that a caller has to opt BACK IN to keep an
113
+ * identifying signal, never opt in to remove one.
114
+ */
115
+ export interface AnonymizeOptions {
116
+ /** Replace `Name`/`LongName`/`Description`/`Tag` on every exported
117
+ * `IfcRoot` with a `<IfcType>-<n>` pseudonym. Default `true`. */
118
+ pseudonymizeNames?: boolean;
119
+ /** Also pseudonymize names OUTSIDE the `IfcRoot` text fields: `ObjectType`
120
+ * and `IfcProject.Phase` on roots, and every quoted-string `Name` /
121
+ * `Description` / `LongName` / `ProfileName` / `LayerSetName` on
122
+ * non-`IfcRoot` entities (`IfcSurfaceStyle`, `IfcMaterial*`,
123
+ * `IfcPresentationLayerAssignment`, `IfcProfileDef`, `IfcColourRgb`, …).
124
+ * A surface style called after the building it belongs to identifies the
125
+ * project as surely as `IfcProject.Name` does. Enum-valued `Name`s
126
+ * (`IfcSIUnit.Name = .METRE.`) are never touched, nor are property /
127
+ * quantity names (schema semantics, only present under `keepPropertySets`)
128
+ * or `IfcApplication`. Default `true`; independent of `pseudonymizeNames`. */
129
+ pseudonymizeAllNames?: boolean;
130
+ /** Keep `IfcPropertySet`/`IfcElementQuantity` entities instead of dropping
131
+ * them. Default `false` (i.e. psets are dropped by default). */
132
+ keepPropertySets?: boolean;
133
+ /** Regenerate every exported `IfcRoot`'s `GlobalId` (the old→new mapping
134
+ * comes back as `AnonymizeResult.guidMap`, never written into the file
135
+ * itself). Default `true`. */
136
+ regenerateGlobalIds?: boolean;
137
+ /** Zero the translation of each root `IfcLocalPlacement`
138
+ * (`PlacementRelTo = $`), preserving `Axis`/`RefDirection` and every child
139
+ * placement untouched. Default `true`. */
140
+ zeroRootPlacement?: boolean;
141
+ /** Remove `IfcMapConversion(Scaled)`/`IfcProjectedCRS` and blank
142
+ * `IfcSite`'s georeferencing/address attributes and
143
+ * `IfcBuilding.BuildingAddress`. Default `true`. */
144
+ removeGeoreferencing?: boolean;
145
+ /** Scrub the owner-history chain: blank `IfcPerson`/`IfcOrganization`
146
+ * identifying fields, zero `IfcOwnerHistory.CreationDate` and blank
147
+ * `LastModifiedDate`, blank `IfcApplication.Version` (a vendor build
148
+ * string such as `26.0.0 NOR FULL` encodes the licence region), and blank
149
+ * the STEP header's `originating_system`. `IfcApplication`'s name and
150
+ * identifier are kept — the authoring tool is debugging signal, its
151
+ * regional build is not. Default `true`. */
152
+ scrubOwnerHistory?: boolean;
153
+ /** Rewrite every `IfcMonetaryUnit.Currency` to US dollars (`.USD.` under
154
+ * IFC2X3's enum, `'USD'` under IFC4+'s label), so a cost-loaded model does
155
+ * not reveal its country. Default `true`. */
156
+ neutralizeCurrency?: boolean;
157
+ /** Seeded randomness for regenerated `GlobalId`s (and any other id this
158
+ * export synthesizes) — omitted means the platform CSPRNG, so two runs of
159
+ * an otherwise-identical anonymization differ in exactly those bytes.
160
+ * Pass the same source across repeat runs for byte-reproducible output. */
161
+ guidRandom?: RandomSource;
162
+ /** Pin the STEP header `FILE_NAME` timestamp (STEP format, e.g.
163
+ * `20240101T000000`). Omitted = the wall clock. */
164
+ timeStamp?: string;
165
+ /** Name recorded in the exported STEP header's `FILE_NAME` field — NOT
166
+ * necessarily the name the caller writes the file to disk under. Default
167
+ * `'anonymized.ifc'`, chosen because it is neutral: a caller that passes
168
+ * the real output path or project name here (e.g. `Acme-Tower.ifc`)
169
+ * defeats the anonymization by writing the identifying name straight back
170
+ * into the header of the file this option exists to scrub it from. */
171
+ filename?: string;
172
+ }
173
+ /** The result of one `exportAnonymizedSubset` call. */
174
+ export interface AnonymizeResult {
175
+ /** The anonymized STEP file content. */
176
+ content: Uint8Array;
177
+ /** Old `GlobalId` → new `GlobalId`, for every regenerated `IfcRoot`. Kept
178
+ * out of the exported file itself — it is exactly the mapping back to the
179
+ * original, identifying model, so it is the caller's responsibility to
180
+ * keep it separate (and typically NOT share it alongside the file). */
181
+ guidMap: Map<string, string>;
182
+ stats: {
183
+ /** Total entities in the exported file. */
184
+ entityCount: number;
185
+ /** How many `IfcRoot` entities from `includedIds` actually made it into
186
+ * the export (after subset-root exclusion and relationship pruning). */
187
+ includedRootEntityCount: number;
188
+ /** ExpressIds of `IfcRel*` entities dropped because their mandatory,
189
+ * single-valued reference named an entity the exclusion left out —
190
+ * `relationshipRefsSurviveExclusion` (`reference-collector.ts`) already
191
+ * decides this for the closure walk; this is that same decision,
192
+ * reported so a caller can see WHICH associations the anonymization
193
+ * cost, not just that some file that changed. */
194
+ prunedRelationshipIds: number[];
195
+ /** Each root `IfcLocalPlacement` this export zeroed, and the translation
196
+ * it zeroed (the ORIGINAL, pre-zero coordinates) — reported because the
197
+ * translation was real coordinate data the caller may still want to log
198
+ * or compare against, even though it is deliberately not in the file. */
199
+ zeroedPlacements: {
200
+ expressId: number;
201
+ translation: number[];
202
+ }[];
203
+ /** Non-fatal notices: a placement chain this export could not safely
204
+ * zero (e.g. an `IfcGridPlacement`/`IfcLinearPlacement` root, left
205
+ * untouched), multiple root placements collapsing onto one origin, or
206
+ * anything else `AnonymizeOptions` asked for that this export could not
207
+ * fully deliver. Same `string[]` shape as
208
+ * `StepExportResult.stats.warnings`. */
209
+ warnings: string[];
210
+ };
211
+ }
212
+ //# sourceMappingURL=anonymize-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymize-types.d.ts","sourceRoot":"","sources":["../src/anonymize-types.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;0BACsB;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACnD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC9C,oFAAoF;IACpF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;0BACsB;IACtB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;;;OAKG;IACH,iCAAiC,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;;OAMG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB;;;iCAG6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb;iDAC6C;IAC7C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;4DAEwD;IACxD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;kEAC8D;IAC9D,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B;;gEAE4D;IAC5D,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjB;;;+CAG2C;IAC3C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B;sEACkE;IAClE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;mFAS+E;IAC/E,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;qEACiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;mCAE+B;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;+CAE2C;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;yDAEqD;IACrD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;iDAM6C;IAC7C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;kDAE8C;IAC9C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;gFAG4E;IAC5E,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B;wDACoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;2EAKuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,OAAO,EAAE,UAAU,CAAC;IACpB;;;4EAGwE;IACxE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,EAAE;QACL,2CAA2C;QAC3C,WAAW,EAAE,MAAM,CAAC;QACpB;iFACyE;QACzE,uBAAuB,EAAE,MAAM,CAAC;QAChC;;;;;0DAKkD;QAClD,qBAAqB,EAAE,MAAM,EAAE,CAAC;QAChC;;;kFAG0E;QAC1E,gBAAgB,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,EAAE,CAAA;SAAE,EAAE,CAAC;QACjE;;;;;iDAKyC;QACzC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH"}
@@ -0,0 +1,5 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ export {};
5
+ //# sourceMappingURL=anonymize-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymize-types.js","sourceRoot":"","sources":["../src/anonymize-types.ts"],"names":[],"mappings":"AAAA;;+DAE+D"}