@ifc-lite/rules 0.2.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 (179) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +45 -0
  3. package/dist/engine/rule-engine-chunk.d.ts +25 -0
  4. package/dist/engine/rule-engine-chunk.d.ts.map +1 -0
  5. package/dist/engine/rule-engine-chunk.js +43 -0
  6. package/dist/engine/rule-engine-chunk.js.map +1 -0
  7. package/dist/engine/rule-engine-compare.d.ts +17 -0
  8. package/dist/engine/rule-engine-compare.d.ts.map +1 -0
  9. package/dist/engine/rule-engine-compare.js +69 -0
  10. package/dist/engine/rule-engine-compare.js.map +1 -0
  11. package/dist/engine/rule-engine-requirements.d.ts +37 -0
  12. package/dist/engine/rule-engine-requirements.d.ts.map +1 -0
  13. package/dist/engine/rule-engine-requirements.js +293 -0
  14. package/dist/engine/rule-engine-requirements.js.map +1 -0
  15. package/dist/engine/rule-engine-sets.d.ts +46 -0
  16. package/dist/engine/rule-engine-sets.d.ts.map +1 -0
  17. package/dist/engine/rule-engine-sets.js +298 -0
  18. package/dist/engine/rule-engine-sets.js.map +1 -0
  19. package/dist/engine/rule-engine-unit.d.ts +23 -0
  20. package/dist/engine/rule-engine-unit.d.ts.map +1 -0
  21. package/dist/engine/rule-engine-unit.js +64 -0
  22. package/dist/engine/rule-engine-unit.js.map +1 -0
  23. package/dist/engine/rule-engine.d.ts +29 -0
  24. package/dist/engine/rule-engine.d.ts.map +1 -0
  25. package/dist/engine/rule-engine.js +244 -0
  26. package/dist/engine/rule-engine.js.map +1 -0
  27. package/dist/filter/effective-filter-fields.d.ts +13 -0
  28. package/dist/filter/effective-filter-fields.d.ts.map +1 -0
  29. package/dist/filter/effective-filter-fields.js +44 -0
  30. package/dist/filter/effective-filter-fields.js.map +1 -0
  31. package/dist/filter/entity-predefined-type.d.ts +24 -0
  32. package/dist/filter/entity-predefined-type.d.ts.map +1 -0
  33. package/dist/filter/entity-predefined-type.js +34 -0
  34. package/dist/filter/entity-predefined-type.js.map +1 -0
  35. package/dist/filter/filter-evaluate-groups.d.ts +36 -0
  36. package/dist/filter/filter-evaluate-groups.d.ts.map +1 -0
  37. package/dist/filter/filter-evaluate-groups.js +111 -0
  38. package/dist/filter/filter-evaluate-groups.js.map +1 -0
  39. package/dist/filter/filter-evaluate-model-tag.d.ts +22 -0
  40. package/dist/filter/filter-evaluate-model-tag.d.ts.map +1 -0
  41. package/dist/filter/filter-evaluate-model-tag.js +35 -0
  42. package/dist/filter/filter-evaluate-model-tag.js.map +1 -0
  43. package/dist/filter/filter-evaluate-mutations.d.ts +51 -0
  44. package/dist/filter/filter-evaluate-mutations.d.ts.map +1 -0
  45. package/dist/filter/filter-evaluate-mutations.js +111 -0
  46. package/dist/filter/filter-evaluate-mutations.js.map +1 -0
  47. package/dist/filter/filter-evaluate-yield.d.ts +9 -0
  48. package/dist/filter/filter-evaluate-yield.d.ts.map +1 -0
  49. package/dist/filter/filter-evaluate-yield.js +32 -0
  50. package/dist/filter/filter-evaluate-yield.js.map +1 -0
  51. package/dist/filter/filter-evaluate.d.ts +120 -0
  52. package/dist/filter/filter-evaluate.d.ts.map +1 -0
  53. package/dist/filter/filter-evaluate.js +408 -0
  54. package/dist/filter/filter-evaluate.js.map +1 -0
  55. package/dist/filter/filter-group-rule.d.ts +23 -0
  56. package/dist/filter/filter-group-rule.d.ts.map +1 -0
  57. package/dist/filter/filter-group-rule.js +69 -0
  58. package/dist/filter/filter-group-rule.js.map +1 -0
  59. package/dist/filter/filter-groups.d.ts +65 -0
  60. package/dist/filter/filter-groups.d.ts.map +1 -0
  61. package/dist/filter/filter-groups.js +136 -0
  62. package/dist/filter/filter-groups.js.map +1 -0
  63. package/dist/filter/filter-iteration-source.d.ts +26 -0
  64. package/dist/filter/filter-iteration-source.d.ts.map +1 -0
  65. package/dist/filter/filter-iteration-source.js +154 -0
  66. package/dist/filter/filter-iteration-source.js.map +1 -0
  67. package/dist/filter/filter-iteration.d.ts +5 -0
  68. package/dist/filter/filter-iteration.d.ts.map +1 -0
  69. package/dist/filter/filter-iteration.js +25 -0
  70. package/dist/filter/filter-iteration.js.map +1 -0
  71. package/dist/filter/filter-match.d.ts +114 -0
  72. package/dist/filter/filter-match.d.ts.map +1 -0
  73. package/dist/filter/filter-match.js +302 -0
  74. package/dist/filter/filter-match.js.map +1 -0
  75. package/dist/filter/filter-ops.d.ts +90 -0
  76. package/dist/filter/filter-ops.d.ts.map +1 -0
  77. package/dist/filter/filter-ops.js +202 -0
  78. package/dist/filter/filter-ops.js.map +1 -0
  79. package/dist/filter/filter-rule-guards.d.ts +4 -0
  80. package/dist/filter/filter-rule-guards.d.ts.map +1 -0
  81. package/dist/filter/filter-rule-guards.js +74 -0
  82. package/dist/filter/filter-rule-guards.js.map +1 -0
  83. package/dist/filter/filter-rules.d.ts +275 -0
  84. package/dist/filter/filter-rules.d.ts.map +1 -0
  85. package/dist/filter/filter-rules.js +68 -0
  86. package/dist/filter/filter-rules.js.map +1 -0
  87. package/dist/filter/lens-material-names.d.ts +13 -0
  88. package/dist/filter/lens-material-names.d.ts.map +1 -0
  89. package/dist/filter/lens-material-names.js +36 -0
  90. package/dist/filter/lens-material-names.js.map +1 -0
  91. package/dist/filter/measure-units.d.ts +29 -0
  92. package/dist/filter/measure-units.d.ts.map +1 -0
  93. package/dist/filter/measure-units.js +72 -0
  94. package/dist/filter/measure-units.js.map +1 -0
  95. package/dist/filter/model-tag.d.ts +65 -0
  96. package/dist/filter/model-tag.d.ts.map +1 -0
  97. package/dist/filter/model-tag.js +57 -0
  98. package/dist/filter/model-tag.js.map +1 -0
  99. package/dist/filter/read-measure-subject.d.ts +28 -0
  100. package/dist/filter/read-measure-subject.d.ts.map +1 -0
  101. package/dist/filter/read-measure-subject.js +116 -0
  102. package/dist/filter/read-measure-subject.js.map +1 -0
  103. package/dist/filter/read-subject.d.ts +72 -0
  104. package/dist/filter/read-subject.d.ts.map +1 -0
  105. package/dist/filter/read-subject.js +128 -0
  106. package/dist/filter/read-subject.js.map +1 -0
  107. package/dist/filter/subject-match.d.ts +27 -0
  108. package/dist/filter/subject-match.d.ts.map +1 -0
  109. package/dist/filter/subject-match.js +52 -0
  110. package/dist/filter/subject-match.js.map +1 -0
  111. package/dist/filter/subject-read-options.d.ts +33 -0
  112. package/dist/filter/subject-read-options.d.ts.map +1 -0
  113. package/dist/filter/subject-read-options.js +8 -0
  114. package/dist/filter/subject-read-options.js.map +1 -0
  115. package/dist/ids/ids-facet-to-rules.d.ts +20 -0
  116. package/dist/ids/ids-facet-to-rules.d.ts.map +1 -0
  117. package/dist/ids/ids-facet-to-rules.js +230 -0
  118. package/dist/ids/ids-facet-to-rules.js.map +1 -0
  119. package/dist/ids/ids-regex.d.ts +44 -0
  120. package/dist/ids/ids-regex.d.ts.map +1 -0
  121. package/dist/ids/ids-regex.js +334 -0
  122. package/dist/ids/ids-regex.js.map +1 -0
  123. package/dist/ids/ids-to-rule-set.d.ts +39 -0
  124. package/dist/ids/ids-to-rule-set.d.ts.map +1 -0
  125. package/dist/ids/ids-to-rule-set.js +103 -0
  126. package/dist/ids/ids-to-rule-set.js.map +1 -0
  127. package/dist/ids/ids-xml-writer.d.ts +11 -0
  128. package/dist/ids/ids-xml-writer.d.ts.map +1 -0
  129. package/dist/ids/ids-xml-writer.js +172 -0
  130. package/dist/ids/ids-xml-writer.js.map +1 -0
  131. package/dist/ids/rule-set-to-ids.d.ts +45 -0
  132. package/dist/ids/rule-set-to-ids.d.ts.map +1 -0
  133. package/dist/ids/rule-set-to-ids.js +140 -0
  134. package/dist/ids/rule-set-to-ids.js.map +1 -0
  135. package/dist/ids/rule-to-ids-facets.d.ts +52 -0
  136. package/dist/ids/rule-to-ids-facets.d.ts.map +1 -0
  137. package/dist/ids/rule-to-ids-facets.js +291 -0
  138. package/dist/ids/rule-to-ids-facets.js.map +1 -0
  139. package/dist/ids/stored-unit-scale.d.ts +15 -0
  140. package/dist/ids/stored-unit-scale.d.ts.map +1 -0
  141. package/dist/ids/stored-unit-scale.js +64 -0
  142. package/dist/ids/stored-unit-scale.js.map +1 -0
  143. package/dist/index.d.ts +39 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +46 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/rule-set/between-chip.d.ts +54 -0
  148. package/dist/rule-set/between-chip.d.ts.map +1 -0
  149. package/dist/rule-set/between-chip.js +95 -0
  150. package/dist/rule-set/between-chip.js.map +1 -0
  151. package/dist/rule-set/requirement-invariants.d.ts +25 -0
  152. package/dist/rule-set/requirement-invariants.d.ts.map +1 -0
  153. package/dist/rule-set/requirement-invariants.js +29 -0
  154. package/dist/rule-set/requirement-invariants.js.map +1 -0
  155. package/dist/rule-set/requirement-text.d.ts +20 -0
  156. package/dist/rule-set/requirement-text.d.ts.map +1 -0
  157. package/dist/rule-set/requirement-text.js +179 -0
  158. package/dist/rule-set/requirement-text.js.map +1 -0
  159. package/dist/rule-set/rule-set-io-requirement.d.ts +22 -0
  160. package/dist/rule-set/rule-set-io-requirement.d.ts.map +1 -0
  161. package/dist/rule-set/rule-set-io-requirement.js +140 -0
  162. package/dist/rule-set/rule-set-io-requirement.js.map +1 -0
  163. package/dist/rule-set/rule-set-io-shared.d.ts +24 -0
  164. package/dist/rule-set/rule-set-io-shared.d.ts.map +1 -0
  165. package/dist/rule-set/rule-set-io-shared.js +48 -0
  166. package/dist/rule-set/rule-set-io-shared.js.map +1 -0
  167. package/dist/rule-set/rule-set-io-subject.d.ts +10 -0
  168. package/dist/rule-set/rule-set-io-subject.d.ts.map +1 -0
  169. package/dist/rule-set/rule-set-io-subject.js +67 -0
  170. package/dist/rule-set/rule-set-io-subject.js.map +1 -0
  171. package/dist/rule-set/rule-set-io.d.ts +37 -0
  172. package/dist/rule-set/rule-set-io.d.ts.map +1 -0
  173. package/dist/rule-set/rule-set-io.js +142 -0
  174. package/dist/rule-set/rule-set-io.js.map +1 -0
  175. package/dist/rule-set/rule-set.d.ts +109 -0
  176. package/dist/rule-set/rule-set.d.ts.map +1 -0
  177. package/dist/rule-set/rule-set.js +5 -0
  178. package/dist/rule-set/rule-set.js.map +1 -0
  179. package/package.json +59 -0
@@ -0,0 +1,408 @@
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
+ * Path-B runtime evaluator.
6
+ *
7
+ * Applies a list of `FilterRule`s to one or more `IfcDataStore`s without
8
+ * touching DuckDB. Three optimisations make this safe on huge (4M-entity)
9
+ * models without a Worker:
10
+ *
11
+ * 1. **Index prefilter (AND + op:in only).** When the rule list contains
12
+ * any `ifcType` or `storey` `op:'in'` rule under an AND combinator,
13
+ * the iteration source is derived from `entityIndex.byType` /
14
+ * `spatialHierarchy.byStorey` — typically 100× narrowing. Per-entity
15
+ * rule evaluation still re-checks every rule for correctness, so
16
+ * picking one prefilter (the smallest bucket) is enough; we don't
17
+ * need to intersect. `notIn` and `OR` skip the prefilter and fall
18
+ * back to the full column scan.
19
+ *
20
+ * 2. **Cheap-first per-entity ordering.** Rules are sorted by cost at
21
+ * evaluation time so column-only checks (`ifcType`, `name`, `storey`,
22
+ * `predefinedType`) run before `property` / `quantity` rules that
23
+ * trigger on-demand source-buffer parses. Combined with AND/OR
24
+ * short-circuit, this avoids the AGENTS.md §2 "never call
25
+ * extractPropertiesOnDemand in a large loop" trap — a single
26
+ * ifcType rule excluding 99% of entities skips 99% of the parses.
27
+ *
28
+ * 3. **Async chunked yielding (federated entry).** The federated entry
29
+ * is async and yields to the event loop every `chunkSize` rows
30
+ * (default 20_000, same as `buildTier1Index`). `AbortSignal` is
31
+ * honoured at chunk boundaries; an `onProgress(scanned, total)`
32
+ * callback fires once per chunk. The synchronous single-model
33
+ * entry remains for tests and small candidate sets.
34
+ */
35
+ import { extractTypePropertiesOnDemand, extractAllMaterialsOnDemand, extractClassificationsOnDemand, mergeInheritedPropertySets, } from '@ifc-lite/parser';
36
+ import { ownPropertySetsFor, typePropertySetsFor, quantitySetsFor, attributesFor, mutatedAttributeValue } from './filter-evaluate-mutations.js';
37
+ import { RelationshipType } from '@ifc-lite/data';
38
+ import { combineRuleResults, } from './filter-rules.js';
39
+ import { setOpMatches, globalIdOpMatches, stringOpMatches, matchStringAnyNone, numericOpMatches, } from './filter-ops.js';
40
+ import { isNumericArrayLike, materialiseNumericIterable, toNumericIterable, } from './filter-iteration.js';
41
+ import { selectIterationSource, orderRulesByCost } from './filter-iteration-source.js';
42
+ import { buildFilterResult, effectiveFilterFields } from './effective-filter-fields.js';
43
+ import { throwAbort, yieldToEventLoop } from './filter-evaluate-yield.js';
44
+ import { modelPrunedUnderAnd, modelScopedRuleMatches, isModelScopedRule, NO_MODEL_TAGS, } from './filter-evaluate-model-tag.js';
45
+ import { flattenPsets, flattenQtys, stringifyValue, matchPropertyRule, matchQuantityRule, matchAttributeRule, defaultStoreyName, storeyMatchesRefs, materialNamesOf, materialMatchCandidates, matchClassificationRule, matchParentRule, elevationOf, } from './filter-match.js';
46
+ import { resolveEntityPredefinedType } from './entity-predefined-type.js';
47
+ import { matchGroupRule } from './filter-group-rule.js';
48
+ import { readsThroughSubject } from './subject-read-options.js';
49
+ import { matchRuleThroughSubject } from './subject-match.js';
50
+ const DEFAULT_LIMIT = 5_000;
51
+ const DEFAULT_CHUNK_SIZE = 20_000;
52
+ // ── Sync entry (small candidate sets, tests) ─────────────────────────────────
53
+ /**
54
+ * Evaluate `rules` against one model synchronously. Suitable for tests
55
+ * and small candidate sets where the chunked async path's overhead
56
+ * isn't justified. For real UI flows (huge models, cancellable runs),
57
+ * use `evaluateFilterRulesFederated` (async).
58
+ */
59
+ export function evaluateFilterRules(modelId, store, rules, combinator, options = {}) {
60
+ if (rules.length === 0)
61
+ return [];
62
+ const limit = options.limit ?? DEFAULT_LIMIT;
63
+ const orderedRules = orderRulesByCost(rules);
64
+ const iterIds = toNumericIterable(selectIterationSource(store, rules, combinator, options.candidateExpressIds, modelId));
65
+ const out = [];
66
+ const ctx = {
67
+ store,
68
+ modelId,
69
+ scope: {
70
+ filterIdentity: options.modelFilterIdentity ?? modelId,
71
+ tagIds: options.modelTagIds,
72
+ definedModelTagIds: options.definedModelTagIds ?? NO_MODEL_TAGS,
73
+ },
74
+ table: store.entities,
75
+ options,
76
+ hasPropertyRule: orderedRules.some((r) => r.kind === 'property'),
77
+ hasQuantityRule: orderedRules.some((r) => r.kind === 'quantity'),
78
+ hasMaterialRule: orderedRules.some((r) => r.kind === 'material'),
79
+ hasClassificationRule: orderedRules.some((r) => r.kind === 'classification'),
80
+ hasAttributeRule: orderedRules.some((r) => r.kind === 'attribute'),
81
+ typePsetCache: new Map(),
82
+ };
83
+ for (const expressId of iterIds) {
84
+ if (out.length >= limit)
85
+ break;
86
+ // Skip empty rows from the raw expressId column. ArrayLike sources
87
+ // (the full-table fast-path) include zero-padded slots; bucket
88
+ // sources (byType / byStorey) never do, so this is a no-op there.
89
+ if (!expressId)
90
+ continue;
91
+ const fields = evaluateOneEntity(ctx, expressId, orderedRules, combinator);
92
+ if (!fields)
93
+ continue;
94
+ out.push(buildFilterResult(modelId, expressId, fields));
95
+ }
96
+ return out;
97
+ }
98
+ export async function evaluateFilterRulesFederated(models, rules, combinator, options = {}) {
99
+ if (rules.length === 0)
100
+ return [];
101
+ const limit = options.limit ?? DEFAULT_LIMIT;
102
+ const chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;
103
+ const signal = options.signal;
104
+ const orderedRules = orderRulesByCost(rules);
105
+ const out = [];
106
+ // Pre-compute per-model iteration plans + a global total so the
107
+ // progress callback can render a single bar across the federation.
108
+ const definedModelTagIds = options.definedModelTagIds ?? NO_MODEL_TAGS;
109
+ const plans = [];
110
+ let grandTotal = 0;
111
+ let totalKnown = true;
112
+ for (const m of models) {
113
+ if (!m.store)
114
+ continue;
115
+ const scope = { filterIdentity: m.filterIdentity ?? m.id, tagIds: m.tagIds, definedModelTagIds };
116
+ // Whole-model prune, AND only: under OR a model failing its model-scoped
117
+ // rules still contributes entities the other rules admit (#4215).
118
+ if (combinator === 'AND' && modelPrunedUnderAnd(orderedRules, scope))
119
+ continue;
120
+ const candidates = options.candidateExpressIdsByModel?.get(m.id);
121
+ const source = candidates ?? selectIterationSource(m.store, rules, combinator, undefined, m.id, m.mutationView);
122
+ const arr = materialiseNumericIterable(source);
123
+ if (arr === null) {
124
+ totalKnown = false;
125
+ }
126
+ else {
127
+ grandTotal += arr.length;
128
+ }
129
+ plans.push({
130
+ modelId: m.id,
131
+ scope,
132
+ store: m.store, mutationView: m.mutationView,
133
+ iter: arr ?? source,
134
+ total: arr ? arr.length : -1,
135
+ });
136
+ }
137
+ let scanned = 0;
138
+ options.onProgress?.(0, totalKnown ? grandTotal : -1);
139
+ for (const plan of plans) {
140
+ if (out.length >= limit)
141
+ break;
142
+ if (signal?.aborted)
143
+ throwAbort(signal);
144
+ const ctx = {
145
+ store: plan.store,
146
+ modelId: plan.modelId,
147
+ scope: plan.scope, mutationView: plan.mutationView,
148
+ table: plan.store.entities,
149
+ options,
150
+ hasPropertyRule: orderedRules.some((r) => r.kind === 'property'),
151
+ hasQuantityRule: orderedRules.some((r) => r.kind === 'quantity'),
152
+ hasMaterialRule: orderedRules.some((r) => r.kind === 'material'),
153
+ hasClassificationRule: orderedRules.some((r) => r.kind === 'classification'),
154
+ hasAttributeRule: orderedRules.some((r) => r.kind === 'attribute'),
155
+ typePsetCache: new Map(),
156
+ };
157
+ // Walk the per-model iter in chunkSize-sized strides, yielding the
158
+ // event loop between chunks. ArrayLike fast-path uses index access;
159
+ // the fallback path drains an iterator into chunks.
160
+ if (Array.isArray(plan.iter) || isNumericArrayLike(plan.iter)) {
161
+ const arr = plan.iter;
162
+ for (let i = 0; i < arr.length && out.length < limit; i += chunkSize) {
163
+ if (signal?.aborted)
164
+ throwAbort(signal);
165
+ const end = Math.min(i + chunkSize, arr.length);
166
+ for (let j = i; j < end; j++) {
167
+ const expressId = arr[j];
168
+ if (!expressId)
169
+ continue;
170
+ const fields = evaluateOneEntity(ctx, expressId, orderedRules, combinator);
171
+ if (!fields)
172
+ continue;
173
+ out.push(buildFilterResult(plan.modelId, expressId, fields));
174
+ if (out.length >= limit)
175
+ break;
176
+ }
177
+ scanned += end - i;
178
+ options.onProgress?.(scanned, totalKnown ? grandTotal : -1);
179
+ if (end < arr.length && out.length < limit)
180
+ await yieldToEventLoop();
181
+ }
182
+ }
183
+ else {
184
+ let buffered = 0;
185
+ for (const expressId of plan.iter) {
186
+ if (out.length >= limit)
187
+ break;
188
+ if (!expressId)
189
+ continue;
190
+ const fields = evaluateOneEntity(ctx, expressId, orderedRules, combinator);
191
+ if (fields) {
192
+ out.push(buildFilterResult(plan.modelId, expressId, fields));
193
+ }
194
+ buffered++;
195
+ scanned++;
196
+ if (buffered >= chunkSize) {
197
+ buffered = 0;
198
+ if (signal?.aborted)
199
+ throwAbort(signal);
200
+ options.onProgress?.(scanned, totalKnown ? grandTotal : -1);
201
+ await yieldToEventLoop();
202
+ }
203
+ }
204
+ // Final progress tick for the residual.
205
+ options.onProgress?.(scanned, totalKnown ? grandTotal : -1);
206
+ }
207
+ }
208
+ return out;
209
+ }
210
+ function evaluateOneEntity(ctx, expressId, orderedRules, combinator) {
211
+ if (ctx.mutationView?.isDeleted(expressId))
212
+ return null;
213
+ const fields = effectiveFilterFields(ctx.store, ctx.mutationView, expressId);
214
+ // Lazy pset/qto reads — only invoked when an ordered rule for that
215
+ // family actually needs the data. Cheap-first ordering means cheap
216
+ // rules check first; AND short-circuit on a cheap miss skips the
217
+ // parse entirely.
218
+ let psetCache = null;
219
+ let qtyCache = null;
220
+ let matCache = null;
221
+ let classCache = null;
222
+ let attrCache = null;
223
+ const psetsFor = () => {
224
+ if (!psetCache) {
225
+ const ownSets = ownPropertySetsFor(ctx.store, expressId, ctx.mutationView); // #4946, mutation-aware
226
+ const typeSets = getInheritedTypePsets(ctx, expressId);
227
+ // IFC inheritance is per-PROPERTY, not per-set, and the occurrence's
228
+ // own value wins on a name collision — same rule the IDS bridge
229
+ // already applies (`mergeInheritedPropertySets`, packages/parser).
230
+ // A type-only pset (nothing on the instance) is appended as-is, so
231
+ // `isSet`/`isNotSet` now also see properties that exist ONLY on the
232
+ // type — a deliberate presence-semantics change (was instance-only).
233
+ psetCache = flattenPsets(mergeInheritedPropertySets(ownSets, typeSets));
234
+ }
235
+ return psetCache;
236
+ };
237
+ const qtysFor = () => {
238
+ if (!qtyCache)
239
+ qtyCache = flattenQtys(quantitySetsFor(ctx.store, expressId, ctx.mutationView));
240
+ return qtyCache;
241
+ };
242
+ const matNamesFor = () => {
243
+ if (!matCache) {
244
+ // Union across ALL associations so a material rule matches an element
245
+ // whose second IfcRelAssociatesMaterial carries the queried name.
246
+ const seen = new Set();
247
+ for (const info of extractAllMaterialsOnDemand(ctx.store, expressId)) {
248
+ for (const n of materialMatchCandidates(info))
249
+ seen.add(n);
250
+ }
251
+ matCache = [...seen];
252
+ }
253
+ return matCache;
254
+ };
255
+ const classFor = () => {
256
+ if (!classCache)
257
+ classCache = extractClassificationsOnDemand(ctx.store, expressId);
258
+ return classCache;
259
+ };
260
+ const attrsFor = () => {
261
+ if (!attrCache)
262
+ attrCache = attributesFor(ctx.store, expressId, ctx.mutationView);
263
+ return attrCache;
264
+ };
265
+ const ruleResults = [];
266
+ for (const rule of orderedRules) {
267
+ const result = evaluateRule(rule, ctx, expressId, ctx.hasPropertyRule ? psetsFor : null, ctx.hasQuantityRule ? qtysFor : null, ctx.hasMaterialRule ? matNamesFor : null, ctx.hasClassificationRule ? classFor : null, ctx.hasAttributeRule ? attrsFor : null, fields);
268
+ ruleResults.push(result);
269
+ if (combinator === 'AND' && !result)
270
+ return null;
271
+ if (combinator === 'OR' && result)
272
+ return fields;
273
+ }
274
+ return combineRuleResults(combinator, ruleResults) ? fields : null;
275
+ }
276
+ /**
277
+ * Resolve `expressId`'s TYPE-level property sets (e.g. `IfcWallType`'s
278
+ * `Pset_WallCommon`) via `IfcRelDefinesByType`, caching the parse per
279
+ * TYPE (`ctx.typePsetCache`) rather than per instance. Many occurrences
280
+ * share one type, so this is O(distinct types) source-buffer parses per
281
+ * filter run, not O(entities) — see the AGENTS.md §2 warning this module
282
+ * already guards for instance psets.
283
+ *
284
+ * Mirrors `packages/ids/src/bridge/properties.ts`'s `inheritedPropertySets`
285
+ * / `typePropertySetsFromTable` split: `extractTypePropertiesOnDemand` only
286
+ * resolves from the source buffer, so on a server-parsed store (no
287
+ * `source`, e.g. after collab/session load) it always returns null — the
288
+ * type's psets there are keyed under the type's own expressId in the
289
+ * pre-built `PropertyTable` (`store.properties.getForEntity(typeId)`,
290
+ * issue #1787's server-path fallback).
291
+ */
292
+ function getInheritedTypePsets(ctx, expressId) {
293
+ if (!ctx.store.relationships)
294
+ return [];
295
+ const typeIds = ctx.store.relationships.getRelated(expressId, RelationshipType.DefinesByType, 'inverse');
296
+ if (typeIds.length === 0)
297
+ return [];
298
+ const typeId = typeIds[0];
299
+ // Only the BASE read is cached per type (review finding: `mutationView` is
300
+ // live/mutable, edited possibly mid-run between chunk yields — caching the
301
+ // mutation-APPLIED result by typeId would keep answering pre-edit psets).
302
+ // The overlay is cheap and applied fresh on every call.
303
+ let base = ctx.typePsetCache.get(typeId);
304
+ if (base === undefined) {
305
+ base = ctx.store.source && ctx.store.source.length > 0
306
+ ? extractTypePropertiesOnDemand(ctx.store, expressId)?.properties ?? []
307
+ : (ctx.store.properties?.getForEntity?.(typeId) ?? []);
308
+ ctx.typePsetCache.set(typeId, base);
309
+ }
310
+ return typePropertySetsFor(base, typeId, ctx.mutationView); // #4946, mutation-aware
311
+ }
312
+ function evaluateRule(rule, ctx, expressId, psetsFor, qtysFor, matNamesFor, classFor, attrsFor, fields) {
313
+ if (isModelScopedRule(rule))
314
+ return modelScopedRuleMatches(rule, ctx.scope);
315
+ switch (rule.kind) {
316
+ case 'storey': {
317
+ // Exact-identity mode (refs present): Name isn't unique.
318
+ if (rule.refs) {
319
+ const isMatch = storeyMatchesRefs(ctx.store, expressId, ctx.modelId, rule);
320
+ return rule.op === 'in' ? isMatch : !isMatch;
321
+ }
322
+ const storeyName = ctx.options.storeyNameOf?.(expressId)
323
+ ?? defaultStoreyName(ctx.store, expressId);
324
+ return setOpMatches(rule.op, storeyName, rule.values);
325
+ }
326
+ case 'ifcType':
327
+ return setOpMatches(rule.op, fields.ifcType, rule.values);
328
+ case 'predefinedType': {
329
+ // No columnar accessor - resolve from the source buffer, per-store (#1462). A live edit (#4946) wins.
330
+ const pt = mutatedAttributeValue(ctx.mutationView, expressId, 'PredefinedType')
331
+ ?? ctx.options.predefinedTypeOf?.(expressId)
332
+ ?? resolveEntityPredefinedType(ctx.store, expressId)
333
+ ?? '';
334
+ return setOpMatches(rule.op, pt, rule.values);
335
+ }
336
+ case 'name': {
337
+ // getNameOrUndefined: absent must reach as undefined, not '' (#4930). A live edit (#4946) wins.
338
+ return stringOpMatches(rule.op, fields.name, rule.value, rule.valueKind);
339
+ }
340
+ case 'globalId':
341
+ return globalIdOpMatches(rule.op, fields.globalId, rule.values);
342
+ case 'attribute': {
343
+ if (!attrsFor)
344
+ return false;
345
+ return matchAttributeRule(rule, attrsFor());
346
+ }
347
+ case 'property':
348
+ if (readsThroughSubject(rule))
349
+ return matchRuleThroughSubject(rule, ctx.store, expressId);
350
+ return psetsFor ? matchPropertyRule(rule, psetsFor()) : false;
351
+ case 'quantity':
352
+ if (readsThroughSubject(rule))
353
+ return matchRuleThroughSubject(rule, ctx.store, expressId);
354
+ return qtysFor ? matchQuantityRule(rule, qtysFor()) : false;
355
+ case 'material': {
356
+ if (!matNamesFor)
357
+ return false;
358
+ return matchStringAnyNone(rule.op, matNamesFor(), rule.value, rule.valueKind);
359
+ }
360
+ case 'classification': {
361
+ if (!classFor)
362
+ return false;
363
+ return matchClassificationRule(rule, classFor());
364
+ }
365
+ case 'elevation': {
366
+ const elev = elevationOf(ctx.store, expressId);
367
+ if (elev === null)
368
+ return false;
369
+ return numericOpMatches(rule.op, elev, rule.value);
370
+ }
371
+ case 'type': {
372
+ const typeName = relatingTypeNameOf(ctx, expressId);
373
+ if (typeName === null)
374
+ return false;
375
+ return stringOpMatches(rule.op, typeName, rule.value, rule.valueKind);
376
+ }
377
+ case 'parent': return matchParentRule(rule, ctx.store, expressId);
378
+ case 'group': return matchGroupRule(rule, ctx.store, expressId);
379
+ }
380
+ }
381
+ /**
382
+ * The Name of `expressId`'s RELATING TYPE via `IfcRelDefinesByType` (#4094).
383
+ * `null` = no such relation (must never match). `undefined` = the related
384
+ * `IfcTypeObject` has no Name (#4930) — distinct from an empty one.
385
+ */
386
+ function relatingTypeNameOf(ctx, expressId) {
387
+ if (!ctx.store.relationships)
388
+ return null;
389
+ const typeIds = ctx.store.relationships.getRelated(expressId, RelationshipType.DefinesByType, 'inverse');
390
+ if (typeIds.length === 0)
391
+ return null;
392
+ return ctx.table.getNameOrUndefined(typeIds[0]);
393
+ }
394
+ // ── Exposed for tests ────────────────────────────────────────────────────────
395
+ export const __internal = {
396
+ flattenPsets,
397
+ flattenQtys,
398
+ stringifyValue,
399
+ matchPropertyRule,
400
+ matchQuantityRule,
401
+ matchAttributeRule,
402
+ materialNamesOf,
403
+ matchClassificationRule,
404
+ elevationOf,
405
+ orderRulesByCost,
406
+ selectIterationSource,
407
+ };
408
+ //# sourceMappingURL=filter-evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-evaluate.js","sourceRoot":"","sources":["../../src/filter/filter-evaluate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type IfcDataStore } from '@ifc-lite/parser';
2
+ import type { ClassificationOp, TextKind } from './filter-rules.js';
3
+ export interface GroupRule {
4
+ kind: 'group';
5
+ /** Only groups of this IFC class or a subclass, e.g. `IfcSystem`. Absent = any `IfcGroup`. */
6
+ groupClass?: string;
7
+ op: ClassificationOp;
8
+ /** Matched against each group's Name. Ignored for `isSet` / `isNotSet`. */
9
+ value: string;
10
+ /** How `value` reads. Only consulted by the `matches` / `notMatches` ops. */
11
+ valueKind?: TextKind;
12
+ }
13
+ export declare function groupRule(op: ClassificationOp, value: string, groupClass?: string, valueKind?: TextKind): GroupRule;
14
+ /**
15
+ * The groups `expressId` is assigned to (inverse `IfcRelAssignsToGroup`),
16
+ * narrowed to `groupClass` when given. The exact declared class is read,
17
+ * not the coalesced one, so a subclass check is a real subclass check.
18
+ */
19
+ export declare function assignedGroupIds(store: IfcDataStore, expressId: number, groupClass?: string): number[];
20
+ /** The Names of those groups; `undefined` for a group with no Name. */
21
+ export declare function assignedGroupNames(store: IfcDataStore, expressId: number, groupClass?: string): (string | undefined)[];
22
+ export declare function matchGroupRule(rule: GroupRule, store: IfcDataStore, expressId: number): boolean;
23
+ //# sourceMappingURL=filter-group-rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-group-rule.d.ts","sourceRoot":"","sources":["../../src/filter/filter-group-rule.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGpE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,gBAAgB,CAAC;IACrB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,SAAS,CAAC,EAAE,QAAQ,CAAC;CACtB;AAED,wBAAgB,SAAS,CACvB,EAAE,EAAE,gBAAgB,EACpB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,QAAQ,GACnB,SAAS,CAQX;AAeD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAOtG;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAEtH;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAK/F"}
@@ -0,0 +1,69 @@
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
+ * `group` — membership in an `IfcGroup` through `IfcRelAssignsToGroup`
6
+ * (#5226): "every AHU is assigned to a system". The one vocabulary entry
7
+ * both search/applicability and validation requirements read, like every
8
+ * other `FilterRule` kind. Own module because `filter-rules.ts` sits at the
9
+ * module-size cap.
10
+ *
11
+ * An element's candidates are the Names of every group it is assigned to,
12
+ * optionally only groups of one IFC class (`groupClass`, subclasses
13
+ * included: `IfcSystem` also takes `IfcDistributionSystem` and
14
+ * `IfcBuildingSystem`). `isSet` / `isNotSet` ask whether there is any such
15
+ * group at all, named or not. Value ops match ANY group Name for a positive
16
+ * op and NONE for a negative one, the convention `material` and `parent`
17
+ * already use.
18
+ */
19
+ import { exactTypeName, RelationshipType } from '@ifc-lite/data';
20
+ import { expandTypes } from '@ifc-lite/parser';
21
+ import { matchStringAnyNone } from './filter-ops.js';
22
+ export function groupRule(op, value, groupClass, valueKind) {
23
+ return {
24
+ kind: 'group',
25
+ ...(groupClass ? { groupClass } : {}),
26
+ op,
27
+ value,
28
+ ...(valueKind ? { valueKind } : {}),
29
+ };
30
+ }
31
+ const classSetCache = new Map();
32
+ /** `groupClass` plus every subclass, upper-cased, for the store's schema. */
33
+ function classSet(groupClass, schemaVersion) {
34
+ const key = `${schemaVersion ?? ''}|${groupClass.toUpperCase()}`;
35
+ let set = classSetCache.get(key);
36
+ if (!set) {
37
+ set = new Set(expandTypes([groupClass], schemaVersion).map((t) => t.toUpperCase()));
38
+ classSetCache.set(key, set);
39
+ }
40
+ return set;
41
+ }
42
+ /**
43
+ * The groups `expressId` is assigned to (inverse `IfcRelAssignsToGroup`),
44
+ * narrowed to `groupClass` when given. The exact declared class is read,
45
+ * not the coalesced one, so a subclass check is a real subclass check.
46
+ */
47
+ export function assignedGroupIds(store, expressId, groupClass) {
48
+ if (!store.relationships)
49
+ return [];
50
+ const ids = store.relationships.getRelated(expressId, RelationshipType.AssignsToGroup, 'inverse');
51
+ const wanted = groupClass?.trim();
52
+ if (!wanted)
53
+ return ids;
54
+ const classes = classSet(wanted, store.schemaVersion);
55
+ return ids.filter((id) => classes.has(exactTypeName(store.entities, id).toUpperCase()));
56
+ }
57
+ /** The Names of those groups; `undefined` for a group with no Name. */
58
+ export function assignedGroupNames(store, expressId, groupClass) {
59
+ return assignedGroupIds(store, expressId, groupClass).map((id) => store.entities.getNameOrUndefined(id));
60
+ }
61
+ export function matchGroupRule(rule, store, expressId) {
62
+ const names = assignedGroupNames(store, expressId, rule.groupClass);
63
+ if (rule.op === 'isSet')
64
+ return names.length > 0;
65
+ if (rule.op === 'isNotSet')
66
+ return names.length === 0;
67
+ return matchStringAnyNone(rule.op, names, rule.value, rule.valueKind);
68
+ }
69
+ //# sourceMappingURL=filter-group-rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-group-rule.js","sourceRoot":"","sources":["../../src/filter/filter-group-rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `FilterGroup` — one AND-combined (or OR-combined, per its own
3
+ * `combinator`) list of `FilterRule`s. A `SearchFilterStateValue` (and a
4
+ * saved filter, #4904) carries an ARRAY of these; groups themselves are
5
+ * always OR'd together, mirroring the selector grammar's `+` union
6
+ * (`IfcSlab, material=concrete + IfcDoor` → two groups).
7
+ *
8
+ * Kept in its own file (not folded into `filter-rules.ts`, already near the
9
+ * module-size cap) so both the evaluator and the serialization layer can
10
+ * import it without pulling in the whole rule taxonomy's JSON guards.
11
+ */
12
+ import { type Combinator, type FilterRule } from './filter-rules.js';
13
+ export interface FilterGroup {
14
+ rules: FilterRule[];
15
+ combinator: Combinator;
16
+ }
17
+ /** A fresh, empty AND group — what a brand-new filter (or a brand-new
18
+ * group added in the builder) starts from. */
19
+ export declare function emptyFilterGroup(): FilterGroup;
20
+ export declare function isFilterGroup(value: unknown): value is FilterGroup;
21
+ /**
22
+ * Render `groups` back as selector-ish text — NOT a full inverse of
23
+ * `parseSelector`/`selectorToFilterRules` (most rule kinds have no single
24
+ * canonical spelling; a regex `Name` rule and a typed-in `contains` rule
25
+ * both become `Rule.name`, and only one of those round-trips through text).
26
+ * What this DOES round-trip exactly is the shape the selector's own union
27
+ * syntax cares about: class names (`IfcWall`, `IfcDoor`, …) and GlobalIds,
28
+ * `+`-joined across groups. Used by the Selector field to echo back what
29
+ * the CURRENT filter state reads as, so adding a second group in the
30
+ * builder is visible as a second `+`-joined clause without leaving the
31
+ * builder (#4904).
32
+ *
33
+ * All-or-nothing: an empty group, or a group whose rules do not reduce to
34
+ * exactly ONE renderable clause, makes the WHOLE echo `''` rather than a
35
+ * partial rendering — review (PR #4987) caught that joining per-group
36
+ * fragments with `+` regardless produced text that read as a DIFFERENT
37
+ * query than what evaluation actually runs: an empty group left a
38
+ * dangling ` + `, and several `ifcType`/`globalId` rules AND'd within one
39
+ * group rendered as comma-separated union terms (selector-text unions use
40
+ * `,` to ADD classes, not narrow them), so a two-rule AND group echoed as
41
+ * something the evaluator would read as broader than what it actually
42
+ * matches. Silently showing a narrower or broader query back to the user
43
+ * is the same "matched nothing, said nothing" defect class #4091 exists to
44
+ * avoid, so this omits the echo entirely rather than guess.
45
+ */
46
+ export declare function groupsToSelectorText(groups: readonly FilterGroup[]): string;
47
+ /** The rules of `groups[activeIndex]`, clamped into range — what the
48
+ * builder's active-group actions (`addFilterRule` etc., `searchSlice.
49
+ * filterGroups.ts`) operate on. Callers outside the store (HierarchyPanel's
50
+ * upsert-by-kind) read this instead of reimplementing the clamp. */
51
+ export declare function activeGroupRules(groups: readonly FilterGroup[], activeIndex: number): FilterRule[];
52
+ /** Total rule count across every group — what "empty filter" checks and rule
53
+ * count badges read, since a `groups.length === 1` filter with zero rules
54
+ * in that one group must still read as empty. */
55
+ export declare function totalRuleCount(groups: readonly FilterGroup[]): number;
56
+ /**
57
+ * Parse a persisted `groups` array, refusing (returning `null`) the WHOLE
58
+ * query rather than silently dropping a group this build cannot read. A
59
+ * saved filter with an unreadable group is exactly the "matched nothing,
60
+ * said nothing" defect class #4091 exists to avoid — narrowing it to the
61
+ * readable groups would change which elements a `+` union matches without
62
+ * telling anyone.
63
+ */
64
+ export declare function parseFilterGroups(raw: unknown): FilterGroup[] | null;
65
+ //# sourceMappingURL=filter-groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-groups.d.ts","sourceRoot":"","sources":["../../src/filter/filter-groups.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAgB,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEnF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;+CAC+C;AAC/C,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAQlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAU3E;AAiBD;;;qEAGqE;AACrE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,EAAE,CAIlG;AAED;;kDAEkD;AAClD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAIrE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,EAAE,GAAG,IAAI,CAmBpE"}