@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,302 @@
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 { flattenMaterials } from '@ifc-lite/ids';
5
+ import { collectSpatialAncestors } from '@ifc-lite/data';
6
+ import { valueOpMatches, numericOpMatches, matchStringAnyNone } from './filter-ops.js';
7
+ import { lensMaterialNames } from './lens-material-names.js';
8
+ import { parsePropertyValue } from '@ifc-lite/encoding';
9
+ import { compileNameMatcher, isNamePattern } from '@ifc-lite/lists';
10
+ /**
11
+ * Compare a rule's property-set / property name against a row's.
12
+ *
13
+ * `kind` is the rule saying what it holds (see {@link TextKind}). A `'regex'`
14
+ * name is a SOURCE and the whole string is the pattern, which is what makes
15
+ * the selector syntax's `/Pset_.*Common/.FireRating` reach `Pset_WallCommon`
16
+ * and `Pset_SlabCommon` in one rule; a `'literal'` name is compared as text
17
+ * even when it is spelled with slashes, which is what the grammar's quoting
18
+ * means and the only reason `"/Wall/".FireRating` can be asked for at all.
19
+ *
20
+ * A name with no declared kind was typed into a chip field, where the
21
+ * Lists-panel `/…/` convention (#1591) is the user's only way to say
22
+ * "pattern"; anything else there is the historical case-insensitive equality.
23
+ *
24
+ * `compileNameMatcher` throws when the regex body is syntactically valid but
25
+ * rejected by `@ifc-lite/regex-guard` as unsafe (catastrophic-backtracking
26
+ * shaped, or over the length cap) — see `filter-ops.ts`'s `regexOpMatches`
27
+ * docstring for the full contract. This function does not catch that; it
28
+ * propagates to `filter-evaluate.ts`'s caller, which must.
29
+ */
30
+ export function nameMatches(rulePattern, rowName, kind) {
31
+ if (kind === 'regex')
32
+ return compileNameMatcher(`/${rulePattern}/`)(rowName);
33
+ if (kind === undefined && isNamePattern(rulePattern))
34
+ return compileNameMatcher(rulePattern)(rowName);
35
+ return rowName.toLowerCase() === rulePattern.toLowerCase();
36
+ }
37
+ export function flattenPsets(psets) {
38
+ const out = [];
39
+ for (const set of psets) {
40
+ for (const p of set.properties) {
41
+ out.push({
42
+ setName: set.name,
43
+ propertyName: p.name,
44
+ // Stringify everything — `valueOpMatches` re-parses numeric ops
45
+ // from this representation. Booleans render as "True"/"False" —
46
+ // the SAME capitalised display string the property table and the
47
+ // list engine (`@ifc-lite/encoding`'s `parsePropertyValue`) show —
48
+ // so a value picked from a chip/dropdown suggestion always matches
49
+ // what the user sees rendered elsewhere. `valueOpMatches`'s eq/ne
50
+ // are case-insensitive, so this doesn't change matching outcomes
51
+ // for either the search chips or free-typed "true"/"false".
52
+ value: stringifyValue(p.value),
53
+ });
54
+ }
55
+ }
56
+ return out;
57
+ }
58
+ export function flattenQtys(qtos) {
59
+ const out = [];
60
+ for (const set of qtos) {
61
+ for (const q of set.quantities) {
62
+ out.push({ setName: set.name, quantityName: q.name, value: q.value });
63
+ }
64
+ }
65
+ return out;
66
+ }
67
+ export function stringifyValue(value) {
68
+ if (value === null || value === undefined)
69
+ return '';
70
+ // Match `parsePropertyValue`'s boolean rendering ("True"/"False") — the
71
+ // SAME function the property table and the list engine's display use —
72
+ // so discovered dropdown suggestions equal what's actually shown/matched
73
+ // elsewhere (#IsExternal true/false mismatch report).
74
+ if (typeof value === 'boolean')
75
+ return parsePropertyValue(value).displayValue;
76
+ if (typeof value === 'number')
77
+ return String(value);
78
+ return String(value);
79
+ }
80
+ export function matchPropertyRule(rule, rows) {
81
+ // isSet / isNotSet are presence checks against (setName, propertyName).
82
+ if (rule.op === 'isSet' || rule.op === 'isNotSet') {
83
+ const present = rows.some((r) => nameMatches(rule.setName, r.setName, rule.setNameKind) &&
84
+ nameMatches(rule.propertyName, r.propertyName, rule.propertyNameKind));
85
+ return rule.op === 'isSet' ? present : !present;
86
+ }
87
+ return rows.some((r) => nameMatches(rule.setName, r.setName, rule.setNameKind) &&
88
+ nameMatches(rule.propertyName, r.propertyName, rule.propertyNameKind) &&
89
+ valueOpMatches(rule.op, r.value, rule.value, rule.valueKind));
90
+ }
91
+ /**
92
+ * Match an `attribute` rule (Description, ObjectType, Tag, LongName, any
93
+ * other schema-named attribute) against one entity's extracted attributes.
94
+ * Attribute NAME matching is case-insensitive, same as the IDS attribute
95
+ * facet this reuses the extraction from; the VALUE comparison reuses
96
+ * `valueOpMatches`, the same comparator `matchPropertyRule` uses, so a
97
+ * numeric attribute value compares the same way a numeric property does.
98
+ */
99
+ export function matchAttributeRule(rule, attrs) {
100
+ const wanted = rule.name.toLowerCase();
101
+ const found = attrs.find((a) => a.name.toLowerCase() === wanted);
102
+ const stringified = found === undefined ? undefined : stringifyValue(found.value);
103
+ if (rule.op === 'isSet' || rule.op === 'isNotSet') {
104
+ const present = (stringified ?? '').length > 0;
105
+ return rule.op === 'isSet' ? present : !present;
106
+ }
107
+ if (stringified === undefined)
108
+ return false;
109
+ return valueOpMatches(rule.op, stringified, rule.value, rule.valueKind);
110
+ }
111
+ export function matchQuantityRule(rule, rows) {
112
+ return rows.some((r) => nameMatches(rule.setName, r.setName, rule.setNameKind) &&
113
+ nameMatches(rule.quantityName, r.quantityName, rule.quantityNameKind) &&
114
+ numericOpMatches(rule.op, r.value, rule.value));
115
+ }
116
+ // ── Storey lookup fallback ────────────────────────────────────────────────────
117
+ /**
118
+ * Storey id an element belongs to: its own direct containment
119
+ * (`elementToStorey`, set for an element the storey directly contains plus
120
+ * its `IfcRelAggregates`-aggregated parts) OR — one hop through a
121
+ * containing `IfcSpace`/`IfcSpatialZone` — the storey THAT space belongs
122
+ * to. A space is itself mapped to its storey in `elementToStorey`
123
+ * (`SpatialHierarchyBuilder` walks a storey's own spatial children), so
124
+ * this is a composition of two maps that already exist plus
125
+ * `getContainingSpace` (`@ifc-lite/data`'s `spatialLookups`), not a new
126
+ * traversal. The single home for "which storey" so the per-entity
127
+ * evaluator (`defaultStoreyName`/`storeyMatchesRefs`) and the bulk index
128
+ * prefilter (`unionByStorey`) cannot answer it two different ways.
129
+ *
130
+ * Reaches exactly one level down from the storey: an element inside a
131
+ * space nested inside another space (rather than directly under the
132
+ * storey) is not resolved — see `docs/guide/selector-syntax.md`.
133
+ */
134
+ function storeyIdOf(hierarchy, expressId) {
135
+ const direct = hierarchy.elementToStorey.get(expressId);
136
+ if (direct !== undefined)
137
+ return direct;
138
+ // `getContainingSpace` is part of the `spatialLookups()` contract every
139
+ // real hierarchy carries, but several tests in this suite build a
140
+ // hand-rolled partial `spatialHierarchy` mock (`byStorey`/`elementToStorey`
141
+ // only) to exercise the ref/name-matching paths in isolation — guard
142
+ // rather than assume every field is present.
143
+ const spaceId = hierarchy.getContainingSpace?.(expressId);
144
+ if (spaceId == null)
145
+ return undefined;
146
+ return hierarchy.elementToStorey.get(spaceId);
147
+ }
148
+ /** Every element `bySpace` lists for a space belonging to `storeyId` — the
149
+ * bulk-prefilter twin of {@link storeyIdOf}'s one-hop space widening.
150
+ * `bySpace` is absent on the same partial test mocks {@link storeyIdOf}
151
+ * guards against. */
152
+ function spaceElementsOfStorey(hierarchy, storeyId) {
153
+ const out = [];
154
+ if (!hierarchy.bySpace)
155
+ return out;
156
+ for (const [spaceId, elements] of hierarchy.bySpace) {
157
+ if (hierarchy.elementToStorey.get(spaceId) !== storeyId)
158
+ continue;
159
+ for (const id of elements)
160
+ out.push(id);
161
+ }
162
+ return out;
163
+ }
164
+ export function defaultStoreyName(store, expressId) {
165
+ const hierarchy = store.spatialHierarchy;
166
+ if (!hierarchy)
167
+ return '';
168
+ const storeyId = storeyIdOf(hierarchy, expressId);
169
+ if (!storeyId)
170
+ return '';
171
+ return store.entities.getName(storeyId);
172
+ }
173
+ /** Does `expressId` (in `modelId`) sit in a storey ref'd by `rule.refs`?
174
+ * `IfcBuildingStorey.Name` isn't unique, so once a `StoreyRule` carries
175
+ * an exact (modelId, expressId) ref (mirrored from a HierarchyPanel
176
+ * click), matching bypasses Name entirely. Reaches through a containing
177
+ * space via {@link storeyIdOf}, same as the Name-matched path. */
178
+ export function storeyMatchesRefs(store, expressId, modelId, rule) {
179
+ const hierarchy = store.spatialHierarchy;
180
+ const storeyId = hierarchy ? storeyIdOf(hierarchy, expressId) : undefined;
181
+ return storeyId != null && !!rule.refs?.some((r) => r.modelId === modelId && r.expressId === storeyId);
182
+ }
183
+ /** Index-prefilter twin of {@link storeyMatchesRefs}/Name matching: the
184
+ * bucket of elements a `storey op:'in'` rule narrows to for `modelId`.
185
+ * Includes each matched storey's directly-contained elements AND every
186
+ * element `bySpace` lists for a space belonging to that storey, so this
187
+ * prefilter can never exclude a candidate the (also widened) per-entity
188
+ * check in `filter-evaluate.ts` would go on to match. */
189
+ export function unionByStorey(store, rule, modelId) {
190
+ const hierarchy = store.spatialHierarchy;
191
+ if (!hierarchy)
192
+ return null;
193
+ const out = [];
194
+ if (rule.refs) {
195
+ for (const ref of rule.refs) {
196
+ if (ref.modelId !== modelId)
197
+ continue;
198
+ const elements = hierarchy.byStorey.get(ref.expressId);
199
+ if (elements)
200
+ for (const id of elements)
201
+ out.push(id);
202
+ for (const id of spaceElementsOfStorey(hierarchy, ref.expressId))
203
+ out.push(id);
204
+ }
205
+ return out.length > 0 ? out : null;
206
+ }
207
+ const wanted = new Set(rule.values.map((n) => n.toLowerCase()));
208
+ for (const storeyId of hierarchy.byStorey.keys()) {
209
+ const name = store.entities.getName(storeyId);
210
+ if (!wanted.has(name.toLowerCase()))
211
+ continue;
212
+ const elements = hierarchy.byStorey.get(storeyId);
213
+ if (elements)
214
+ for (const id of elements)
215
+ out.push(id);
216
+ for (const id of spaceElementsOfStorey(hierarchy, storeyId))
217
+ out.push(id);
218
+ }
219
+ return out.length > 0 ? out : null;
220
+ }
221
+ // ── Material / classification / elevation resolution ─────────────────────────
222
+ /** Collect the *individual* material names an element exposes - each layer /
223
+ * constituent / profile material, or the single plain material - for the
224
+ * multi-valued `material` rule matcher and the material dropdown. Shares the
225
+ * #1366 lens collector, so filtering by material groups by the real materials
226
+ * rather than the layer-set / Revit family+type name that masked them. (#1462) */
227
+ export function materialNamesOf(info) {
228
+ return lensMaterialNames(info);
229
+ }
230
+ /** `materialNamesOf` plus every material Category (`material=` matches
231
+ * Name OR Category, #4094) — categories come from `flattenMaterials`
232
+ * (`@ifc-lite/ids`'s material-facet flattener), reused rather than a
233
+ * second layer/profile/constituent walk; only its `.category` field is
234
+ * used, since its `.name` field also surfaces a layer's own label, which
235
+ * `materialNamesOf` deliberately excludes (#1462). */
236
+ export function materialMatchCandidates(info) {
237
+ const categories = flattenMaterials(info)
238
+ .map((m) => m.category)
239
+ .filter((c) => !!c);
240
+ return [...new Set([...materialNamesOf(info), ...categories])];
241
+ }
242
+ /**
243
+ * Match a classification rule against an element's classification refs.
244
+ * `system` (when set) scopes to one system; value ops match a ref's code
245
+ * (identification) OR name. Pushes both through even when `undefined`
246
+ * (absent) rather than filtering on truthy — `matchStringAnyNone` decides
247
+ * absent-vs-empty explicitly (#4930).
248
+ */
249
+ export function matchClassificationRule(rule, refs) {
250
+ const sys = rule.system?.trim().toLowerCase();
251
+ const scoped = sys
252
+ ? refs.filter((r) => (r.system ?? '').toLowerCase() === sys)
253
+ : refs;
254
+ if (rule.op === 'isSet')
255
+ return scoped.length > 0;
256
+ if (rule.op === 'isNotSet')
257
+ return scoped.length === 0;
258
+ // Value ops — match against identification (code) and name of each ref.
259
+ // `unresolved` (server-parsed, no source bytes — #3948) means UNKNOWN, not
260
+ // ABSENT: excluded from candidates entirely, same as before #4930, so it
261
+ // can't flip a negative op false->true. Only a RESOLVED ref's genuinely
262
+ // `$` identification/name contributes a real `undefined` candidate.
263
+ const candidates = [];
264
+ for (const r of scoped) {
265
+ if (r.unresolved)
266
+ continue;
267
+ candidates.push(r.identification);
268
+ candidates.push(r.name);
269
+ }
270
+ // rule.op is now eq | ne | contains | notContains — a StringOp subset.
271
+ return matchStringAnyNone(rule.op, candidates, rule.value, rule.valueKind);
272
+ }
273
+ /** Element elevation in metres, derived from its building storey's
274
+ * elevation. Returns null when the element isn't placed in the spatial
275
+ * hierarchy (so an elevation rule simply doesn't match it). */
276
+ export function elevationOf(store, expressId) {
277
+ const hierarchy = store.spatialHierarchy;
278
+ if (!hierarchy)
279
+ return null;
280
+ const storeyId = hierarchy.elementToStorey.get(expressId);
281
+ if (!storeyId)
282
+ return null;
283
+ const elev = hierarchy.storeyElevations.get(storeyId);
284
+ return typeof elev === 'number' ? elev : null;
285
+ }
286
+ // ── Parent (spatial ancestor) resolution ──────────────────────────────────────
287
+ /**
288
+ * `parent=Foo` (#4903) — does ANY ancestor of `expressId`, walking upward
289
+ * through spatial containment and aggregation to any depth via
290
+ * `collectSpatialAncestors` (`@ifc-lite/data`, the single shared resolver),
291
+ * have a Name the rule's op matches? Reuses `matchStringAnyNone`, the same
292
+ * multi-valued convention `material`/`classification` rules use: an element
293
+ * with NO ancestors — or none whose Name matches — satisfies neither a
294
+ * positive nor a negative op (#4659). `getNameOrUndefined`, not `getName`,
295
+ * so an ancestor with no Name at all is a real absent candidate (#4930).
296
+ */
297
+ export function matchParentRule(rule, store, expressId) {
298
+ const ancestorNames = collectSpatialAncestors(store.relationships, expressId)
299
+ .map((id) => store.entities.getNameOrUndefined(id));
300
+ return matchStringAnyNone(rule.op, ancestorNames, rule.value, rule.valueKind);
301
+ }
302
+ //# sourceMappingURL=filter-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-match.js","sourceRoot":"","sources":["../../src/filter/filter-match.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { NumericOp, SetOp, StringOp, TextKind, ValueOp } from './filter-rules.js';
2
+ /**
3
+ * Trailing options accepted by every op-matching function below (#5138 PR 3).
4
+ * Omitted entirely = today's behaviour EXACTLY: search, saved filters and
5
+ * clash presets never pass this, so they fold case unconditionally (like
6
+ * `fold()` always did before this flag existed) and compare numbers with the
7
+ * historical absolute `1e-9` epsilon. Only the information-validation engine
8
+ * passes it, with `caseSensitive: rule.caseSensitive ?? true` (IDS parity,
9
+ * bSI #346) and `tolerance: rule.tolerance ?? 1e-6` (relative, bSI #418).
10
+ */
11
+ export interface OpMatchOptions {
12
+ caseSensitive?: boolean;
13
+ tolerance?: number;
14
+ }
15
+ /**
16
+ * Regex matching for the `matches` / `notMatches` ops, shared by every rule
17
+ * kind that owns them.
18
+ *
19
+ * `kind` is the producer speaking (see {@link TextKind}). A declared operand is
20
+ * a regular-expression SOURCE and the WHOLE string is the pattern, slashes
21
+ * included — that is what makes the selector `Name=/\/tmp\//` look for `/tmp/`
22
+ * rather than `tmp`. An operand with no declared kind is free text under the
23
+ * Lists-panel convention (#1591): a `/body/flags` literal is a pattern, flags
24
+ * and all, and anything else is a bare source. That is the spelling the chip
25
+ * editors ask for, and there the slashes ARE the user's only way to say
26
+ * "pattern", so reading them is the grammar rather than a guess.
27
+ *
28
+ * Case-SENSITIVE, unlike every other op here: the selector grammar's `/…/` is
29
+ * a Python regular expression, and those do not fold case. Write JavaScript's
30
+ * equivalent of `(?i)` by adding an `i` flag to a full literal (`/wand/i`).
31
+ *
32
+ * An empty operand never matches; a MALFORMED literal (bad regex syntax)
33
+ * never matches either (`compileNameMatcher` logs it and falls back to an
34
+ * exact compare against the literal text, which no property value equals).
35
+ *
36
+ * An UNSAFE pattern — syntactically valid but rejected by
37
+ * `@ifc-lite/regex-guard` as catastrophic-backtracking-shaped or over the
38
+ * length cap — is different: `compileNameMatcher` THROWS a plain `Error`
39
+ * naming the pattern and reason, once, on the first row that reaches it.
40
+ * This function does not catch that throw; it propagates out of
41
+ * `stringOpMatches` / `matchStringAnyNone` / `valueOpMatches` and out of
42
+ * `filter-evaluate.ts`'s per-entity loop. Every current caller of the
43
+ * evaluator (`SearchModal.filter.tsx`'s `runFilter`, `resolveClashSetFilter`,
44
+ * `query-adapter.ts`'s `entitiesMatchingActiveFilter`) already wraps its call
45
+ * in a try/catch that turns this into a visible, recoverable error rather
46
+ * than an unhandled exception — see those call sites for how each one
47
+ * surfaces it. A new caller of `evaluateFilterRules` /
48
+ * `evaluateFilterRulesFederated` MUST do the same.
49
+ */
50
+ export declare function regexOpMatches(candidate: string, value: string, kind: TextKind | undefined): boolean;
51
+ export declare function setOpMatches(op: SetOp, candidate: string, values: readonly string[], opts?: OpMatchOptions): boolean;
52
+ /**
53
+ * `globalId` rule matching — exact, case-SENSITIVE, unlike every other
54
+ * `SetOp` dimension in this module. A GlobalId is a 22-character base64
55
+ * string (IFC's compressed GUID encoding), where upper/lower case is part of
56
+ * the identity: folding case would let two DIFFERENT elements' GlobalIds
57
+ * collide on an `in` match.
58
+ */
59
+ export declare function globalIdOpMatches(op: SetOp, candidate: string, values: readonly string[]): boolean;
60
+ /**
61
+ * `candidate: string | undefined` — `undefined` means ABSENT (#4930), a real
62
+ * input, decided explicitly here: a positive op never matches it, its
63
+ * negation always does.
64
+ */
65
+ export declare function stringOpMatches(op: StringOp, candidate: string | undefined, value: string, valueKind?: TextKind, opts?: OpMatchOptions): boolean;
66
+ /**
67
+ * Match a StringOp against a *set* of candidates (materials, ancestors,
68
+ * classification refs). Positive ops match if ANY satisfies them; negative
69
+ * ops match only if NONE violates them; a `string | undefined` candidate is
70
+ * handled per-entry by `stringOpMatches` (#4930). An empty LIST stays
71
+ * distinct: "no candidates at all" never matches, negative ops included.
72
+ */
73
+ export declare function matchStringAnyNone(op: StringOp, candidates: readonly (string | undefined)[], value: string, valueKind?: TextKind, opts?: OpMatchOptions): boolean;
74
+ /**
75
+ * `opts.tolerance` (relative, bSI #418 — e.g. `1e-6`) replaces the absolute
76
+ * `1e-9` epsilon below for `eq`/`ne` and widens `gte`/`lte` to accept a value
77
+ * within tolerance of the boundary; `gt`/`lt` stay strict (a tolerant strict
78
+ * inequality would let a value equal to the boundary pass both `gt` and its
79
+ * own negation's neighbour). Omitted `opts`/`tolerance` = today's absolute-
80
+ * epsilon behaviour, unchanged.
81
+ */
82
+ export declare function numericOpMatches(op: NumericOp, candidate: number, value: number, opts?: OpMatchOptions): boolean;
83
+ /**
84
+ * Evaluate a Property ValueOp against the candidate's raw stringified
85
+ * value. `isSet`/`isNotSet` are presence checks and the property layer
86
+ * (filter-evaluate.ts) decides them before calling here — but we still
87
+ * accept them so the function is total.
88
+ */
89
+ export declare function valueOpMatches(op: ValueOp, psetVal: string, ruleVal: string, valueKind?: TextKind, opts?: OpMatchOptions): boolean;
90
+ //# sourceMappingURL=filter-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-ops.d.ts","sourceRoot":"","sources":["../../src/filter/filter-ops.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEvF;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAIpG;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,KAAK,EACT,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAIT;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAGlG;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,QAAQ,EACpB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAyBT;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,EACZ,UAAU,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,EAC3C,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,QAAQ,EACpB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAeT;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAyBhH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,OAAO,EACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,QAAQ,EACpB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAoBT"}
@@ -0,0 +1,202 @@
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
+ * Pure operator matching for the filter rule taxonomy (ported verbatim from
6
+ * the Tauri-side `filter.rs` engine).
7
+ *
8
+ * Split out of `filter-rules.ts` at the op seam so both stay under the module
9
+ * size cap: that file owns the rule SHAPES, their constructors and the JSON
10
+ * guards, this one owns what an operator MEANS. Nothing here reads a rule —
11
+ * each function takes an operator, a candidate and the rule's operand, which
12
+ * is what makes them testable without a model.
13
+ */
14
+ import { compileNameMatcher, isNamePattern } from '@ifc-lite/lists';
15
+ /**
16
+ * Fold a candidate that may be undefined at runtime — crash-safety only
17
+ * (#1195) — to lower-case UNLESS `caseSensitive` is `true`. Does NOT decide
18
+ * what "no value" means to an operator: `undefined` (absent) vs `''`
19
+ * (explicitly empty) is a real distinction a caller must resolve before
20
+ * calling this — see `stringOpMatches` (#4930). `caseSensitive` omitted (the
21
+ * only path before #5138 PR 3) always folds, so this is byte-for-byte the
22
+ * historical `lower()` for every existing caller.
23
+ */
24
+ function fold(s, caseSensitive) {
25
+ const v = s ?? '';
26
+ return caseSensitive ? v : v.toLowerCase();
27
+ }
28
+ /**
29
+ * Regex matching for the `matches` / `notMatches` ops, shared by every rule
30
+ * kind that owns them.
31
+ *
32
+ * `kind` is the producer speaking (see {@link TextKind}). A declared operand is
33
+ * a regular-expression SOURCE and the WHOLE string is the pattern, slashes
34
+ * included — that is what makes the selector `Name=/\/tmp\//` look for `/tmp/`
35
+ * rather than `tmp`. An operand with no declared kind is free text under the
36
+ * Lists-panel convention (#1591): a `/body/flags` literal is a pattern, flags
37
+ * and all, and anything else is a bare source. That is the spelling the chip
38
+ * editors ask for, and there the slashes ARE the user's only way to say
39
+ * "pattern", so reading them is the grammar rather than a guess.
40
+ *
41
+ * Case-SENSITIVE, unlike every other op here: the selector grammar's `/…/` is
42
+ * a Python regular expression, and those do not fold case. Write JavaScript's
43
+ * equivalent of `(?i)` by adding an `i` flag to a full literal (`/wand/i`).
44
+ *
45
+ * An empty operand never matches; a MALFORMED literal (bad regex syntax)
46
+ * never matches either (`compileNameMatcher` logs it and falls back to an
47
+ * exact compare against the literal text, which no property value equals).
48
+ *
49
+ * An UNSAFE pattern — syntactically valid but rejected by
50
+ * `@ifc-lite/regex-guard` as catastrophic-backtracking-shaped or over the
51
+ * length cap — is different: `compileNameMatcher` THROWS a plain `Error`
52
+ * naming the pattern and reason, once, on the first row that reaches it.
53
+ * This function does not catch that throw; it propagates out of
54
+ * `stringOpMatches` / `matchStringAnyNone` / `valueOpMatches` and out of
55
+ * `filter-evaluate.ts`'s per-entity loop. Every current caller of the
56
+ * evaluator (`SearchModal.filter.tsx`'s `runFilter`, `resolveClashSetFilter`,
57
+ * `query-adapter.ts`'s `entitiesMatchingActiveFilter`) already wraps its call
58
+ * in a try/catch that turns this into a visible, recoverable error rather
59
+ * than an unhandled exception — see those call sites for how each one
60
+ * surfaces it. A new caller of `evaluateFilterRules` /
61
+ * `evaluateFilterRulesFederated` MUST do the same.
62
+ */
63
+ export function regexOpMatches(candidate, value, kind) {
64
+ if (value.length === 0)
65
+ return false;
66
+ const literal = kind === undefined && isNamePattern(value) ? value : `/${value}/`;
67
+ return compileNameMatcher(literal)(candidate ?? '');
68
+ }
69
+ export function setOpMatches(op, candidate, values, opts) {
70
+ const c = fold(candidate, opts?.caseSensitive);
71
+ const hit = values.some((v) => fold(v, opts?.caseSensitive) === c);
72
+ return op === 'in' ? hit : !hit;
73
+ }
74
+ /**
75
+ * `globalId` rule matching — exact, case-SENSITIVE, unlike every other
76
+ * `SetOp` dimension in this module. A GlobalId is a 22-character base64
77
+ * string (IFC's compressed GUID encoding), where upper/lower case is part of
78
+ * the identity: folding case would let two DIFFERENT elements' GlobalIds
79
+ * collide on an `in` match.
80
+ */
81
+ export function globalIdOpMatches(op, candidate, values) {
82
+ const hit = values.includes(candidate);
83
+ return op === 'in' ? hit : !hit;
84
+ }
85
+ /**
86
+ * `candidate: string | undefined` — `undefined` means ABSENT (#4930), a real
87
+ * input, decided explicitly here: a positive op never matches it, its
88
+ * negation always does.
89
+ */
90
+ export function stringOpMatches(op, candidate, value, valueKind, opts) {
91
+ if (candidate === undefined) {
92
+ switch (op) {
93
+ case 'eq':
94
+ case 'contains':
95
+ case 'startsWith':
96
+ case 'matches':
97
+ return false;
98
+ case 'ne':
99
+ case 'notContains':
100
+ case 'notMatches':
101
+ return true;
102
+ }
103
+ }
104
+ const a = fold(candidate, opts?.caseSensitive);
105
+ const b = fold(value, opts?.caseSensitive);
106
+ switch (op) {
107
+ case 'eq': return a === b;
108
+ case 'ne': return a !== b;
109
+ case 'contains': return a.includes(b);
110
+ case 'notContains': return !a.includes(b);
111
+ case 'startsWith': return a.startsWith(b);
112
+ case 'matches': return regexOpMatches(candidate, value, valueKind);
113
+ case 'notMatches': return !regexOpMatches(candidate, value, valueKind);
114
+ }
115
+ }
116
+ /**
117
+ * Match a StringOp against a *set* of candidates (materials, ancestors,
118
+ * classification refs). Positive ops match if ANY satisfies them; negative
119
+ * ops match only if NONE violates them; a `string | undefined` candidate is
120
+ * handled per-entry by `stringOpMatches` (#4930). An empty LIST stays
121
+ * distinct: "no candidates at all" never matches, negative ops included.
122
+ */
123
+ export function matchStringAnyNone(op, candidates, value, valueKind, opts) {
124
+ if (candidates.length === 0)
125
+ return false;
126
+ switch (op) {
127
+ case 'eq':
128
+ case 'contains':
129
+ case 'startsWith':
130
+ case 'matches':
131
+ return candidates.some((c) => stringOpMatches(op, c, value, valueKind, opts));
132
+ case 'ne':
133
+ return candidates.every((c) => stringOpMatches('eq', c, value, undefined, opts) === false);
134
+ case 'notContains':
135
+ return candidates.every((c) => stringOpMatches('contains', c, value, undefined, opts) === false);
136
+ case 'notMatches':
137
+ return candidates.every((c) => stringOpMatches('matches', c, value, valueKind, opts) === false);
138
+ }
139
+ }
140
+ /**
141
+ * `opts.tolerance` (relative, bSI #418 — e.g. `1e-6`) replaces the absolute
142
+ * `1e-9` epsilon below for `eq`/`ne` and widens `gte`/`lte` to accept a value
143
+ * within tolerance of the boundary; `gt`/`lt` stay strict (a tolerant strict
144
+ * inequality would let a value equal to the boundary pass both `gt` and its
145
+ * own negation's neighbour). Omitted `opts`/`tolerance` = today's absolute-
146
+ * epsilon behaviour, unchanged.
147
+ */
148
+ export function numericOpMatches(op, candidate, value, opts) {
149
+ const tol = opts?.tolerance;
150
+ if (tol !== undefined) {
151
+ const eps = tol * Math.max(Math.abs(candidate), Math.abs(value), 1);
152
+ switch (op) {
153
+ case 'eq': return Math.abs(candidate - value) <= eps;
154
+ case 'ne': return Math.abs(candidate - value) > eps;
155
+ case 'gt': return candidate > value;
156
+ case 'gte': return candidate >= value - eps;
157
+ case 'lt': return candidate < value;
158
+ case 'lte': return candidate <= value + eps;
159
+ }
160
+ }
161
+ // The Rust side uses 1e-9 as the epsilon for eq/ne. Match it here for
162
+ // IDS-style parity — IFC quantities are stored as IFC4 IfcReal so the
163
+ // tolerance is large enough to absorb f32→f64 rounding from the parser.
164
+ const EPS = 1e-9;
165
+ switch (op) {
166
+ case 'eq': return Math.abs(candidate - value) < EPS;
167
+ case 'ne': return Math.abs(candidate - value) >= EPS;
168
+ case 'gt': return candidate > value;
169
+ case 'gte': return candidate >= value;
170
+ case 'lt': return candidate < value;
171
+ case 'lte': return candidate <= value;
172
+ }
173
+ }
174
+ /**
175
+ * Evaluate a Property ValueOp against the candidate's raw stringified
176
+ * value. `isSet`/`isNotSet` are presence checks and the property layer
177
+ * (filter-evaluate.ts) decides them before calling here — but we still
178
+ * accept them so the function is total.
179
+ */
180
+ export function valueOpMatches(op, psetVal, ruleVal, valueKind, opts) {
181
+ switch (op) {
182
+ case 'isSet': return (psetVal ?? '').length > 0;
183
+ case 'isNotSet': return (psetVal ?? '').length === 0;
184
+ case 'eq': return fold(psetVal, opts?.caseSensitive) === fold(ruleVal, opts?.caseSensitive);
185
+ case 'ne': return fold(psetVal, opts?.caseSensitive) !== fold(ruleVal, opts?.caseSensitive);
186
+ case 'contains': return fold(psetVal, opts?.caseSensitive).includes(fold(ruleVal, opts?.caseSensitive));
187
+ case 'notContains': return !fold(psetVal, opts?.caseSensitive).includes(fold(ruleVal, opts?.caseSensitive));
188
+ case 'matches': return regexOpMatches(psetVal, ruleVal, valueKind);
189
+ case 'notMatches': return !regexOpMatches(psetVal, ruleVal, valueKind);
190
+ case 'gt':
191
+ case 'gte':
192
+ case 'lt':
193
+ case 'lte': {
194
+ const cv = Number.parseFloat(psetVal);
195
+ const rv = Number.parseFloat(ruleVal);
196
+ if (!Number.isFinite(cv) || !Number.isFinite(rv))
197
+ return false;
198
+ return numericOpMatches(op, cv, rv, opts);
199
+ }
200
+ }
201
+ }
202
+ //# sourceMappingURL=filter-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-ops.js","sourceRoot":"","sources":["../../src/filter/filter-ops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FilterRule } from './filter-rules.js';
2
+ export declare function isFilterRule(value: unknown): value is FilterRule;
3
+ export declare function parseFilterRules(raw: unknown): FilterRule[];
4
+ //# sourceMappingURL=filter-rule-guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-rule-guards.d.ts","sourceRoot":"","sources":["../../src/filter/filter-rule-guards.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAoB,UAAU,EAAY,MAAM,mBAAmB,CAAC;AAUhF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CA6ChE;AASD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,EAAE,CAG3D"}