@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,136 @@
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
+ * `FilterGroup` — one AND-combined (or OR-combined, per its own
6
+ * `combinator`) list of `FilterRule`s. A `SearchFilterStateValue` (and a
7
+ * saved filter, #4904) carries an ARRAY of these; groups themselves are
8
+ * always OR'd together, mirroring the selector grammar's `+` union
9
+ * (`IfcSlab, material=concrete + IfcDoor` → two groups).
10
+ *
11
+ * Kept in its own file (not folded into `filter-rules.ts`, already near the
12
+ * module-size cap) so both the evaluator and the serialization layer can
13
+ * import it without pulling in the whole rule taxonomy's JSON guards.
14
+ */
15
+ import { isFilterRule } from './filter-rules.js';
16
+ /** A fresh, empty AND group — what a brand-new filter (or a brand-new
17
+ * group added in the builder) starts from. */
18
+ export function emptyFilterGroup() {
19
+ return { rules: [], combinator: 'AND' };
20
+ }
21
+ export function isFilterGroup(value) {
22
+ if (typeof value !== 'object' || value === null)
23
+ return false;
24
+ const g = value;
25
+ return (Array.isArray(g.rules) &&
26
+ g.rules.every(isFilterRule) &&
27
+ (g.combinator === 'AND' || g.combinator === 'OR'));
28
+ }
29
+ /**
30
+ * Render `groups` back as selector-ish text — NOT a full inverse of
31
+ * `parseSelector`/`selectorToFilterRules` (most rule kinds have no single
32
+ * canonical spelling; a regex `Name` rule and a typed-in `contains` rule
33
+ * both become `Rule.name`, and only one of those round-trips through text).
34
+ * What this DOES round-trip exactly is the shape the selector's own union
35
+ * syntax cares about: class names (`IfcWall`, `IfcDoor`, …) and GlobalIds,
36
+ * `+`-joined across groups. Used by the Selector field to echo back what
37
+ * the CURRENT filter state reads as, so adding a second group in the
38
+ * builder is visible as a second `+`-joined clause without leaving the
39
+ * builder (#4904).
40
+ *
41
+ * All-or-nothing: an empty group, or a group whose rules do not reduce to
42
+ * exactly ONE renderable clause, makes the WHOLE echo `''` rather than a
43
+ * partial rendering — review (PR #4987) caught that joining per-group
44
+ * fragments with `+` regardless produced text that read as a DIFFERENT
45
+ * query than what evaluation actually runs: an empty group left a
46
+ * dangling ` + `, and several `ifcType`/`globalId` rules AND'd within one
47
+ * group rendered as comma-separated union terms (selector-text unions use
48
+ * `,` to ADD classes, not narrow them), so a two-rule AND group echoed as
49
+ * something the evaluator would read as broader than what it actually
50
+ * matches. Silently showing a narrower or broader query back to the user
51
+ * is the same "matched nothing, said nothing" defect class #4091 exists to
52
+ * avoid, so this omits the echo entirely rather than guess.
53
+ */
54
+ export function groupsToSelectorText(groups) {
55
+ const clauses = [];
56
+ for (const group of groups) {
57
+ if (group.rules.length === 0)
58
+ continue;
59
+ if (group.rules.length !== 1)
60
+ return '';
61
+ const clause = ruleToSelectorClause(group.rules[0]);
62
+ if (clause === null)
63
+ return '';
64
+ clauses.push(clause);
65
+ }
66
+ return clauses.join(' + ');
67
+ }
68
+ function ruleToSelectorClause(rule) {
69
+ switch (rule.kind) {
70
+ case 'ifcType':
71
+ if (!Array.isArray(rule.values) || rule.values.length === 0)
72
+ return null;
73
+ return rule.values.map((v) => (rule.op === 'notIn' ? `! ${v}` : v)).join(', ');
74
+ case 'globalId':
75
+ if (!Array.isArray(rule.values) || rule.values.length === 0)
76
+ return null;
77
+ return rule.values.map((v) => (rule.op === 'notIn' ? `! ${v}` : v)).join(', ');
78
+ default:
79
+ // Every other rule kind has no single canonical selector spelling —
80
+ // omitted rather than guessed. See the module doc above.
81
+ return null;
82
+ }
83
+ }
84
+ /** The rules of `groups[activeIndex]`, clamped into range — what the
85
+ * builder's active-group actions (`addFilterRule` etc., `searchSlice.
86
+ * filterGroups.ts`) operate on. Callers outside the store (HierarchyPanel's
87
+ * upsert-by-kind) read this instead of reimplementing the clamp. */
88
+ export function activeGroupRules(groups, activeIndex) {
89
+ if (groups.length === 0)
90
+ return [];
91
+ const i = Math.max(0, Math.min(activeIndex, groups.length - 1));
92
+ return groups[i]?.rules ?? [];
93
+ }
94
+ /** Total rule count across every group — what "empty filter" checks and rule
95
+ * count badges read, since a `groups.length === 1` filter with zero rules
96
+ * in that one group must still read as empty. */
97
+ export function totalRuleCount(groups) {
98
+ let n = 0;
99
+ for (const g of groups)
100
+ n += g.rules.length;
101
+ return n;
102
+ }
103
+ /**
104
+ * Parse a persisted `groups` array, refusing (returning `null`) the WHOLE
105
+ * query rather than silently dropping a group this build cannot read. A
106
+ * saved filter with an unreadable group is exactly the "matched nothing,
107
+ * said nothing" defect class #4091 exists to avoid — narrowing it to the
108
+ * readable groups would change which elements a `+` union matches without
109
+ * telling anyone.
110
+ */
111
+ export function parseFilterGroups(raw) {
112
+ if (!Array.isArray(raw) || raw.length === 0)
113
+ return null;
114
+ const groups = [];
115
+ for (const entry of raw) {
116
+ if (typeof entry !== 'object' || entry === null)
117
+ return null;
118
+ const g = entry;
119
+ if (!Array.isArray(g.rules))
120
+ return null;
121
+ // Every rule in a persisted group must parse — a group carrying even
122
+ // one rule this build cannot read is not "mostly readable", it is a
123
+ // different query, so the whole thing is refused (see module doc).
124
+ if (!g.rules.every(isFilterRule))
125
+ return null;
126
+ // A missing/corrupted combinator must refuse too, not default to AND —
127
+ // review (PR #4987): silently coercing a dropped/garbled `OR` into
128
+ // `AND` changes which elements the group matches without telling
129
+ // anyone, the exact defect class this whole function exists to avoid.
130
+ if (g.combinator !== 'AND' && g.combinator !== 'OR')
131
+ return null;
132
+ groups.push({ rules: g.rules, combinator: g.combinator });
133
+ }
134
+ return groups;
135
+ }
136
+ //# sourceMappingURL=filter-groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-groups.js","sourceRoot":"","sources":["../../src/filter/filter-groups.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Iteration-source selection (index prefilter) and cheap-first rule
3
+ * ordering for the path-B evaluator.
4
+ *
5
+ * Split out of `filter-evaluate.ts` (which keeps the orchestration /
6
+ * per-entity loop) to stay under the module size cap — both pieces here are
7
+ * pure given `store`/`rules`, which is what makes them unit-testable without
8
+ * an `EvalContext`.
9
+ */
10
+ import { type IfcDataStore } from '@ifc-lite/parser';
11
+ import { type MutablePropertyView } from '@ifc-lite/mutations';
12
+ import type { Combinator, FilterRule } from './filter-rules.js';
13
+ /** Return the raw expressId column as the iteration source. The per-entity
14
+ * loops already skip empty rows (`if (!expressId) continue`) so the
15
+ * typed-array shape is correctness-safe AND lets the federated entry report
16
+ * a `total` rather than streaming with `total = -1`. */
17
+ export declare function iterateAllExpressIds(store: IfcDataStore): ArrayLike<number>;
18
+ /**
19
+ * Decide which expressIds the evaluator walks. Public for testability —
20
+ * consumers should only depend on the results returned, not on the
21
+ * iteration count, but a benchmark / regression test may want to assert
22
+ * the prefilter actually narrows.
23
+ */
24
+ export declare function selectIterationSource(store: IfcDataStore, rules: readonly FilterRule[], combinator: Combinator, candidateExpressIds: Iterable<number> | undefined, modelId?: string, mutationView?: MutablePropertyView): ArrayLike<number> | Iterable<number>;
25
+ export declare function orderRulesByCost(rules: readonly FilterRule[]): FilterRule[];
26
+ //# sourceMappingURL=filter-iteration-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-iteration-source.d.ts","sourceRoot":"","sources":["../../src/filter/filter-iteration-source.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1F,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAKhE;;;yDAGyD;AACzD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC,MAAM,CAAC,CAE3E;AAgBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,UAAU,EAAE,UAAU,EACtB,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,EACjD,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,mBAAmB,GACjC,SAAS,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAuCtC;AAoED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,EAAE,CAO3E"}
@@ -0,0 +1,154 @@
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
+ * Iteration-source selection (index prefilter) and cheap-first rule
6
+ * ordering for the path-B evaluator.
7
+ *
8
+ * Split out of `filter-evaluate.ts` (which keeps the orchestration /
9
+ * per-entity loop) to stay under the module size cap — both pieces here are
10
+ * pure given `store`/`rules`, which is what makes them unit-testable without
11
+ * an `EvalContext`.
12
+ */
13
+ import { isQueryableObjectType } from '@ifc-lite/parser';
14
+ import { iterateEffectiveEntityIds } from '@ifc-lite/mutations';
15
+ import { unionByStorey } from './filter-match.js';
16
+ // ── Iteration source: index prefilter (AND + op:in) ──────────────────────────
17
+ /** Return the raw expressId column as the iteration source. The per-entity
18
+ * loops already skip empty rows (`if (!expressId) continue`) so the
19
+ * typed-array shape is correctness-safe AND lets the federated entry report
20
+ * a `total` rather than streaming with `total = -1`. */
21
+ export function iterateAllExpressIds(store) {
22
+ return store.entities.expressId;
23
+ }
24
+ function* effectiveFilterIds(store, view, types) {
25
+ const sourceIds = types ? undefined : store.entities.expressId;
26
+ for (const { expressId, type, overlayCreated } of iterateEffectiveEntityIds(store, view, types, sourceIds)) {
27
+ // An untyped search historically scanned EntityTable rows, not every STEP
28
+ // geometry primitive. A typed search already used the source type bucket.
29
+ if (!types && overlayCreated && !isQueryableObjectType(type))
30
+ continue;
31
+ yield expressId;
32
+ }
33
+ }
34
+ /**
35
+ * Decide which expressIds the evaluator walks. Public for testability —
36
+ * consumers should only depend on the results returned, not on the
37
+ * iteration count, but a benchmark / regression test may want to assert
38
+ * the prefilter actually narrows.
39
+ */
40
+ export function selectIterationSource(store, rules, combinator, candidateExpressIds, modelId, mutationView) {
41
+ // Caller-supplied narrowing wins (Tier-1 candidates).
42
+ if (candidateExpressIds !== undefined)
43
+ return candidateExpressIds;
44
+ if (mutationView?.hasPendingChanges()) {
45
+ // A source type bucket omits entities retyped into it and newly created
46
+ // entities. Keep the same AND narrowing when it is safe, but apply it to
47
+ // the effective ID set rather than the parser's immutable index.
48
+ if (combinator === 'AND') {
49
+ const typeRule = rules.find((rule) => rule.kind === 'ifcType' && rule.op === 'in' && rule.values.length > 0);
50
+ if (typeRule?.kind === 'ifcType') {
51
+ return effectiveFilterIds(store, mutationView, typeRule.values);
52
+ }
53
+ }
54
+ return effectiveFilterIds(store, mutationView);
55
+ }
56
+ // Prefilter only applies under AND. OR rules are unioned; you can't
57
+ // shrink the candidate set from a single OR clause without losing
58
+ // results from the other clauses.
59
+ if (combinator !== 'AND')
60
+ return iterateAllExpressIds(store);
61
+ // Try to find the smallest narrowing source. Multiple op:in rules in
62
+ // the same query can each suggest a candidate bucket; we pick the
63
+ // smallest one (the per-entity loop re-checks every rule, so any one
64
+ // valid bucket is correctness-safe — fewer rows = less work).
65
+ let best = null;
66
+ for (const rule of rules) {
67
+ if (rule.kind === 'ifcType' && rule.op === 'in' && rule.values.length > 0) {
68
+ const bucket = unionByType(store, rule.values);
69
+ if (bucket && (best === null || bucket.length < best.length))
70
+ best = bucket;
71
+ }
72
+ else if (rule.kind === 'storey' && rule.op === 'in' && rule.values.length > 0) {
73
+ const bucket = unionByStorey(store, rule, modelId);
74
+ if (bucket && (best === null || bucket.length < best.length))
75
+ best = bucket;
76
+ }
77
+ }
78
+ return best ?? iterateAllExpressIds(store);
79
+ }
80
+ function unionByType(store, names) {
81
+ // @raw-entity-enumeration-ok selectIterationSource reaches this source-bucket fast path only when no mutation view has pending changes
82
+ const byType = store.entityIndex.byType;
83
+ if (!byType || byType.size === 0)
84
+ return null;
85
+ // STEP type names are stored UPPERCASE; rule values arrive in canonical
86
+ // PascalCase ("IfcWall") so we uppercase here at the boundary.
87
+ const out = [];
88
+ for (const name of names) {
89
+ const bucket = byType.get(name.toUpperCase());
90
+ if (bucket)
91
+ for (const id of bucket)
92
+ out.push(id);
93
+ }
94
+ return out.length > 0 ? out : null;
95
+ }
96
+ // ── Cheap-first rule ordering ────────────────────────────────────────────────
97
+ /**
98
+ * AGENTS.md §2: never call `extractPropertiesOnDemand` in a large loop.
99
+ * We can't avoid it entirely for `property`/`quantity` rules, but we can
100
+ * make sure cheap rules check first so AND/OR short-circuit skips the
101
+ * expensive parse for entities that already fail/pass.
102
+ */
103
+ const RULE_COST = {
104
+ // Constant-time comparison against the entity's owning model.
105
+ model: 0,
106
+ // Constant-time set lookups against the owning model's tag set (#4215).
107
+ modelTag: 0,
108
+ // Column-only — single TypedArray read.
109
+ ifcType: 0,
110
+ // Pre-built reverse-map lookup.
111
+ storey: 1,
112
+ // Pre-built reverse-map lookup (elementToStorey → storeyElevations).
113
+ elevation: 1,
114
+ // String-table indirection.
115
+ name: 2,
116
+ // Column-only — same TypedArray read as ifcType, just a different column.
117
+ globalId: 0,
118
+ // Source-buffer parse (resolveEntityPredefinedType re-reads the entity's
119
+ // attributes) - same cost class as a pset parse, so order it after the
120
+ // cheap column checks that can short-circuit it. (#1462)
121
+ predefinedType: 10,
122
+ // Source-buffer parse (the AGENTS.md §2 hot path).
123
+ property: 10,
124
+ quantity: 10,
125
+ // Source-buffer parse (extractAllEntityAttributes) — same cost class.
126
+ attribute: 10,
127
+ // Relationship-graph walk + on-demand resolve — as costly as a pset parse.
128
+ material: 10,
129
+ classification: 10,
130
+ // One reverse-relationship lookup (IfcRelDefinesByType) + a columnar
131
+ // Name read on the resolved type id — no source-buffer parse, so it's
132
+ // cheap like `storey`/`elevation`, not `material`/`classification`.
133
+ type: 1,
134
+ // Relationship-graph walk (potentially multi-hop, containment +
135
+ // aggregation) plus a columnar Name read on each ancestor — no
136
+ // source-buffer parse, but costlier than the single-hop `type` lookup, so
137
+ // it sits with `material`/`classification`. No `op:'in'` prefilter bucket
138
+ // exists for it either (#4903's maintainer ruling: fall back to a full
139
+ // scan for `parent=`, the same trade-off `location=` already accepted,
140
+ // and narrow only if a real perf number asks for it).
141
+ parent: 10,
142
+ // One reverse-relationship lookup (IfcRelAssignsToGroup) + a columnar
143
+ // Name/class read per group, like `type`; no source-buffer parse.
144
+ group: 1,
145
+ };
146
+ export function orderRulesByCost(rules) {
147
+ // Stable sort — equal-cost rules retain their authored order so the
148
+ // user's intent is visible in debug logs / SQL preview.
149
+ return rules
150
+ .map((r, i) => ({ r, i, cost: RULE_COST[r.kind] }))
151
+ .sort((a, b) => a.cost - b.cost || a.i - b.i)
152
+ .map((x) => x.r);
153
+ }
154
+ //# sourceMappingURL=filter-iteration-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-iteration-source.js","sourceRoot":"","sources":["../../src/filter/filter-iteration-source.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;GAQG;AAEH,OAAO,EAAE,qBAAqB,EAAqB,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAA4B,MAAM,qBAAqB,CAAC;AAG1F,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,gFAAgF;AAEhF;;;yDAGyD;AACzD,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,OAAO,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;AAClC,CAAC;AAED,QAAQ,CAAC,CAAC,kBAAkB,CAC1B,KAAmB,EACnB,IAAyB,EACzB,KAAyB;IAEzB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC/D,KAAK,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,yBAAyB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3G,0EAA0E;QAC1E,0EAA0E;QAC1E,IAAI,CAAC,KAAK,IAAI,cAAc,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC;YAAE,SAAS;QACvE,MAAM,SAAS,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAmB,EACnB,KAA4B,EAC5B,UAAsB,EACtB,mBAAiD,EACjD,OAAgB,EAChB,YAAkC;IAElC,sDAAsD;IACtD,IAAI,mBAAmB,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IAElE,IAAI,YAAY,EAAE,iBAAiB,EAAE,EAAE,CAAC;QACtC,wEAAwE;QACxE,yEAAyE;QACzE,iEAAiE;QACjE,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC7G,IAAI,QAAQ,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;gBACjC,OAAO,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QACD,OAAO,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC;IAED,oEAAoE;IACpE,kEAAkE;IAClE,kCAAkC;IAClC,IAAI,UAAU,KAAK,KAAK;QAAE,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE7D,qEAAqE;IACrE,kEAAkE;IAClE,qEAAqE;IACrE,8DAA8D;IAC9D,IAAI,IAAI,GAAoB,IAAI,CAAC;IAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1E,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,MAAM,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBAAE,IAAI,GAAG,MAAM,CAAC;QAC9E,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChF,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YACnD,IAAI,MAAM,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBAAE,IAAI,GAAG,MAAM,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,OAAO,IAAI,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,WAAW,CAAC,KAAmB,EAAE,KAAwB;IAChE,uIAAuI;IACvI,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;IACxC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,wEAAwE;IACxE,+DAA+D;IAC/D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9C,IAAI,MAAM;YAAE,KAAK,MAAM,EAAE,IAAI,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,CAAC;AAED,gFAAgF;AAEhF;;;;;GAKG;AACH,MAAM,SAAS,GAAuC;IACpD,8DAA8D;IAC9D,KAAK,EAAW,CAAC;IACjB,wEAAwE;IACxE,QAAQ,EAAQ,CAAC;IACjB,wCAAwC;IACxC,OAAO,EAAS,CAAC;IACjB,gCAAgC;IAChC,MAAM,EAAU,CAAC;IACjB,qEAAqE;IACrE,SAAS,EAAO,CAAC;IACjB,4BAA4B;IAC5B,IAAI,EAAY,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,EAAQ,CAAC;IACjB,yEAAyE;IACzE,uEAAuE;IACvE,yDAAyD;IACzD,cAAc,EAAE,EAAE;IAClB,mDAAmD;IACnD,QAAQ,EAAQ,EAAE;IAClB,QAAQ,EAAQ,EAAE;IAClB,sEAAsE;IACtE,SAAS,EAAO,EAAE;IAClB,2EAA2E;IAC3E,QAAQ,EAAQ,EAAE;IAClB,cAAc,EAAE,EAAE;IAClB,qEAAqE;IACrE,sEAAsE;IACtE,oEAAoE;IACpE,IAAI,EAAY,CAAC;IACjB,gEAAgE;IAChE,+DAA+D;IAC/D,0EAA0E;IAC1E,0EAA0E;IAC1E,uEAAuE;IACvE,uEAAuE;IACvE,sDAAsD;IACtD,MAAM,EAAU,EAAE;IAClB,sEAAsE;IACtE,kEAAkE;IAClE,KAAK,EAAW,CAAC;CAClB,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,oEAAoE;IACpE,wDAAwD;IACxD,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAClD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAC5C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function isNumericArrayLike(value: unknown): value is ArrayLike<number>;
2
+ export declare function toNumericIterable(source: ArrayLike<number> | Iterable<number>): Iterable<number>;
3
+ /** Materialise known-size collections for indexed chunking and progress. */
4
+ export declare function materialiseNumericIterable(source: ArrayLike<number> | Iterable<number>): ArrayLike<number> | null;
5
+ //# sourceMappingURL=filter-iteration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-iteration.d.ts","sourceRoot":"","sources":["../../src/filter/filter-iteration.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,MAAM,CAAC,CAG7E;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAC3C,QAAQ,CAAC,MAAM,CAAC,CAMlB;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAC3C,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAI1B"}
@@ -0,0 +1,25 @@
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 function isNumericArrayLike(value) {
5
+ return typeof value === 'object' && value !== null
6
+ && typeof value.length === 'number';
7
+ }
8
+ export function toNumericIterable(source) {
9
+ if (Symbol.iterator in Object(source))
10
+ return source;
11
+ return (function* () {
12
+ const arr = source;
13
+ for (let i = 0; i < arr.length; i++)
14
+ yield arr[i];
15
+ })();
16
+ }
17
+ /** Materialise known-size collections for indexed chunking and progress. */
18
+ export function materialiseNumericIterable(source) {
19
+ if (Array.isArray(source) || isNumericArrayLike(source))
20
+ return source;
21
+ if (source instanceof Set)
22
+ return Array.from(source);
23
+ return null;
24
+ }
25
+ //# sourceMappingURL=filter-iteration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-iteration.js","sourceRoot":"","sources":["../../src/filter/filter-iteration.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;WAC7C,OAAQ,KAA8B,CAAC,MAAM,KAAK,QAAQ,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAA4C;IAE5C,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,MAA0B,CAAC;IACzE,OAAO,CAAC,QAAQ,CAAC;QACf,MAAM,GAAG,GAAG,MAA2B,CAAC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,EAAE,CAAC;AACP,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,0BAA0B,CACxC,MAA4C;IAE5C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACvE,IAAI,MAAM,YAAY,GAAG;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Per-rule value resolution + matching for the path-B evaluator.
3
+ *
4
+ * Split out of `filter-evaluate.ts` (which keeps the iteration /
5
+ * orchestration logic) to stay under the module size cap. These helpers
6
+ * are pure given their inputs, which is what makes them unit-testable in
7
+ * `filter-evaluate.test.ts` via the evaluator's `__internal` re-export.
8
+ */
9
+ import { extractPropertiesOnDemand, extractQuantitiesOnDemand, type IfcDataStore, type MaterialInfo, type ClassificationInfo } from '@ifc-lite/parser';
10
+ import { type PropertyRule, type QuantityRule, type ClassificationRule, type AttributeRule, type StoreyRule, type ParentRule, type TextKind } from './filter-rules.js';
11
+ /**
12
+ * Compare a rule's property-set / property name against a row's.
13
+ *
14
+ * `kind` is the rule saying what it holds (see {@link TextKind}). A `'regex'`
15
+ * name is a SOURCE and the whole string is the pattern, which is what makes
16
+ * the selector syntax's `/Pset_.*Common/.FireRating` reach `Pset_WallCommon`
17
+ * and `Pset_SlabCommon` in one rule; a `'literal'` name is compared as text
18
+ * even when it is spelled with slashes, which is what the grammar's quoting
19
+ * means and the only reason `"/Wall/".FireRating` can be asked for at all.
20
+ *
21
+ * A name with no declared kind was typed into a chip field, where the
22
+ * Lists-panel `/…/` convention (#1591) is the user's only way to say
23
+ * "pattern"; anything else there is the historical case-insensitive equality.
24
+ *
25
+ * `compileNameMatcher` throws when the regex body is syntactically valid but
26
+ * rejected by `@ifc-lite/regex-guard` as unsafe (catastrophic-backtracking
27
+ * shaped, or over the length cap) — see `filter-ops.ts`'s `regexOpMatches`
28
+ * docstring for the full contract. This function does not catch that; it
29
+ * propagates to `filter-evaluate.ts`'s caller, which must.
30
+ */
31
+ export declare function nameMatches(rulePattern: string, rowName: string, kind?: TextKind): boolean;
32
+ export interface PsetRow {
33
+ setName: string;
34
+ propertyName: string;
35
+ value: string;
36
+ }
37
+ export type PsetRows = ReadonlyArray<PsetRow>;
38
+ export interface QtyRow {
39
+ setName: string;
40
+ quantityName: string;
41
+ value: number;
42
+ }
43
+ export type QtyRows = ReadonlyArray<QtyRow>;
44
+ export declare function flattenPsets(psets: ReturnType<typeof extractPropertiesOnDemand>): PsetRows;
45
+ export declare function flattenQtys(qtos: ReturnType<typeof extractQuantitiesOnDemand>): QtyRows;
46
+ export declare function stringifyValue(value: unknown): string;
47
+ export declare function matchPropertyRule(rule: PropertyRule, rows: PsetRows): boolean;
48
+ /** One entity's generic named attributes, as `extractAllEntityAttributes`
49
+ * returns them — schema-driven, string/number/boolean values only. */
50
+ export type AttrRows = ReadonlyArray<{
51
+ name: string;
52
+ value: string | number | boolean;
53
+ }>;
54
+ /**
55
+ * Match an `attribute` rule (Description, ObjectType, Tag, LongName, any
56
+ * other schema-named attribute) against one entity's extracted attributes.
57
+ * Attribute NAME matching is case-insensitive, same as the IDS attribute
58
+ * facet this reuses the extraction from; the VALUE comparison reuses
59
+ * `valueOpMatches`, the same comparator `matchPropertyRule` uses, so a
60
+ * numeric attribute value compares the same way a numeric property does.
61
+ */
62
+ export declare function matchAttributeRule(rule: AttributeRule, attrs: AttrRows): boolean;
63
+ export declare function matchQuantityRule(rule: QuantityRule, rows: QtyRows): boolean;
64
+ export declare function defaultStoreyName(store: IfcDataStore, expressId: number): string;
65
+ /** Does `expressId` (in `modelId`) sit in a storey ref'd by `rule.refs`?
66
+ * `IfcBuildingStorey.Name` isn't unique, so once a `StoreyRule` carries
67
+ * an exact (modelId, expressId) ref (mirrored from a HierarchyPanel
68
+ * click), matching bypasses Name entirely. Reaches through a containing
69
+ * space via {@link storeyIdOf}, same as the Name-matched path. */
70
+ export declare function storeyMatchesRefs(store: IfcDataStore, expressId: number, modelId: string, rule: StoreyRule): boolean;
71
+ /** Index-prefilter twin of {@link storeyMatchesRefs}/Name matching: the
72
+ * bucket of elements a `storey op:'in'` rule narrows to for `modelId`.
73
+ * Includes each matched storey's directly-contained elements AND every
74
+ * element `bySpace` lists for a space belonging to that storey, so this
75
+ * prefilter can never exclude a candidate the (also widened) per-entity
76
+ * check in `filter-evaluate.ts` would go on to match. */
77
+ export declare function unionByStorey(store: IfcDataStore, rule: StoreyRule, modelId: string | undefined): number[] | null;
78
+ /** Collect the *individual* material names an element exposes - each layer /
79
+ * constituent / profile material, or the single plain material - for the
80
+ * multi-valued `material` rule matcher and the material dropdown. Shares the
81
+ * #1366 lens collector, so filtering by material groups by the real materials
82
+ * rather than the layer-set / Revit family+type name that masked them. (#1462) */
83
+ export declare function materialNamesOf(info: MaterialInfo | null): string[];
84
+ /** `materialNamesOf` plus every material Category (`material=` matches
85
+ * Name OR Category, #4094) — categories come from `flattenMaterials`
86
+ * (`@ifc-lite/ids`'s material-facet flattener), reused rather than a
87
+ * second layer/profile/constituent walk; only its `.category` field is
88
+ * used, since its `.name` field also surfaces a layer's own label, which
89
+ * `materialNamesOf` deliberately excludes (#1462). */
90
+ export declare function materialMatchCandidates(info: MaterialInfo | null): string[];
91
+ /**
92
+ * Match a classification rule against an element's classification refs.
93
+ * `system` (when set) scopes to one system; value ops match a ref's code
94
+ * (identification) OR name. Pushes both through even when `undefined`
95
+ * (absent) rather than filtering on truthy — `matchStringAnyNone` decides
96
+ * absent-vs-empty explicitly (#4930).
97
+ */
98
+ export declare function matchClassificationRule(rule: ClassificationRule, refs: readonly ClassificationInfo[]): boolean;
99
+ /** Element elevation in metres, derived from its building storey's
100
+ * elevation. Returns null when the element isn't placed in the spatial
101
+ * hierarchy (so an elevation rule simply doesn't match it). */
102
+ export declare function elevationOf(store: IfcDataStore, expressId: number): number | null;
103
+ /**
104
+ * `parent=Foo` (#4903) — does ANY ancestor of `expressId`, walking upward
105
+ * through spatial containment and aggregation to any depth via
106
+ * `collectSpatialAncestors` (`@ifc-lite/data`, the single shared resolver),
107
+ * have a Name the rule's op matches? Reuses `matchStringAnyNone`, the same
108
+ * multi-valued convention `material`/`classification` rules use: an element
109
+ * with NO ancestors — or none whose Name matches — satisfies neither a
110
+ * positive nor a negative op (#4659). `getNameOrUndefined`, not `getName`,
111
+ * so an ancestor with no Name at all is a real absent candidate (#4930).
112
+ */
113
+ export declare function matchParentRule(rule: ParentRule, store: IfcDataStore, expressId: number): boolean;
114
+ //# sourceMappingURL=filter-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-match.d.ts","sourceRoot":"","sources":["../../src/filter/filter-match.ts"],"names":[],"mappings":"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"}