@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,95 @@
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 isBetweenChip(row) {
5
+ return row.kind === 'between';
6
+ }
7
+ /** Same subject, ignoring op/value: what makes a `gte` and an `lte` rule
8
+ * "the same range" rather than two unrelated bounds. `null` for rule
9
+ * kinds that never fold (including every non-betweenable kind, which is
10
+ * every kind besides the three above). */
11
+ function subjectKey(rule) {
12
+ if (rule.kind === 'quantity')
13
+ return `quantity\u0000${rule.setName}\u0000${rule.quantityName}`;
14
+ if (rule.kind === 'property')
15
+ return `property\u0000${rule.setName}\u0000${rule.propertyName}`;
16
+ if (rule.kind === 'attribute')
17
+ return `attribute\u0000${rule.name}`;
18
+ return null;
19
+ }
20
+ function isBetweenable(rule) {
21
+ return rule.kind === 'property' || rule.kind === 'quantity' || rule.kind === 'attribute';
22
+ }
23
+ /**
24
+ * Merge `gte`/`lte` pairs on the identical subject into one `BetweenChip`,
25
+ * regardless of which one appears first. A subject with only a `gte`, only
26
+ * a `lte`, or more than one of either, is left as plain rules — folding
27
+ * only ever removes exactly two entries and adds exactly one, never
28
+ * guesses which of several candidates pairs with which.
29
+ *
30
+ * Pairing happens in a PASS OF ITS OWN, before anything is written to
31
+ * `out`: a single combined pass that pushed a `lte` immediately (because
32
+ * it isn't itself a `gte`) and only later discovered, at its `gte`
33
+ * partner's index, that the two pair up, would leave that `lte` in `out`
34
+ * TWICE — once as the plain rule already pushed, once folded inside the
35
+ * chip — so `[lte(300), gte(100)]` (partner before the `gte` that finds
36
+ * it) silently produced three rules on unfold instead of two. Deciding
37
+ * every pair up front means the emitting pass never has to un-push
38
+ * something it already committed.
39
+ */
40
+ export function foldBetweenPairs(rules) {
41
+ const consumed = new Set();
42
+ // gte index -> lte index, in both directions, so the emitting pass can
43
+ // look a consumed index up either way and always find its partner.
44
+ const partnerOf = new Map();
45
+ for (let i = 0; i < rules.length; i += 1) {
46
+ const rule = rules[i];
47
+ if (consumed.has(i) || !isBetweenable(rule) || rule.op !== 'gte')
48
+ continue;
49
+ const key = subjectKey(rule);
50
+ const partnerIndex = rules.findIndex((candidate, j) => j !== i &&
51
+ !consumed.has(j) &&
52
+ isBetweenable(candidate) &&
53
+ candidate.op === 'lte' &&
54
+ subjectKey(candidate) === key);
55
+ if (partnerIndex >= 0) {
56
+ consumed.add(i);
57
+ consumed.add(partnerIndex);
58
+ partnerOf.set(i, partnerIndex);
59
+ partnerOf.set(partnerIndex, i);
60
+ }
61
+ }
62
+ const out = [];
63
+ const emitted = new Set();
64
+ for (let i = 0; i < rules.length; i += 1) {
65
+ if (!consumed.has(i)) {
66
+ out.push(rules[i]);
67
+ continue;
68
+ }
69
+ if (emitted.has(i))
70
+ continue; // the chip already went out at its partner's index
71
+ const partnerIndex = partnerOf.get(i);
72
+ emitted.add(i);
73
+ emitted.add(partnerIndex);
74
+ const gteIndex = rules[i].op === 'gte' ? i : partnerIndex;
75
+ const lteIndex = gteIndex === i ? partnerIndex : i;
76
+ out.push({ kind: 'between', min: rules[gteIndex], max: rules[lteIndex] });
77
+ }
78
+ return out;
79
+ }
80
+ /** Reverses `foldBetweenPairs`: every `BetweenChip` becomes its `min`
81
+ * (`gte`) rule followed by its `max` (`lte`) rule — the exact shape
82
+ * `rule-set-io.ts` persists. */
83
+ export function unfoldBetweenChips(rows) {
84
+ const out = [];
85
+ for (const row of rows) {
86
+ if (isBetweenChip(row)) {
87
+ out.push(row.min, row.max);
88
+ }
89
+ else {
90
+ out.push(row);
91
+ }
92
+ }
93
+ return out;
94
+ }
95
+ //# sourceMappingURL=between-chip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"between-chip.js","sourceRoot":"","sources":["../../src/rule-set/between-chip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The subject invariants a `Requirement` must satisfy whichever authoring
3
+ * path produced it (#5182). The JSON parser (`rule-set-io-requirement.ts`)
4
+ * and the text parser (`requirement-text.ts`) both build requirements from
5
+ * user input and must refuse the same shapes before `rule-engine-sets.ts`
6
+ * sees them. Otherwise the text path accepts what the JSON path rejects, and
7
+ * the difference only shows up at evaluation time.
8
+ *
9
+ * Each check returns a plain message (no `where`/position prefix) rather than
10
+ * throwing, so each caller wraps it in its own error type: the JSON path's
11
+ * `fail()` wants a `${where}: ` prefix, the text path's `TextError` wants
12
+ * none.
13
+ */
14
+ import type { AggregateRequirement, Subject } from './rule-set.js';
15
+ /** An `aggregate` needs a subject unless `fn` is `count`, and a numeric `fn`
16
+ * needs a single-valued one: summing a list of material names has no
17
+ * numeric meaning. Returns the violation, or `undefined`. */
18
+ export declare function checkAggregateSubject(fn: AggregateRequirement['fn'], subject: Subject | undefined): string | undefined;
19
+ /** Both sides of a `compare` must be single-valued. Returns the first
20
+ * offending side and its message, or `undefined`. */
21
+ export declare function checkCompareSubjects(left: Subject, right: Subject): {
22
+ side: 'left' | 'right';
23
+ message: string;
24
+ } | undefined;
25
+ //# sourceMappingURL=requirement-invariants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-invariants.d.ts","sourceRoot":"","sources":["../../src/rule-set/requirement-invariants.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGnE;;8DAE8D;AAC9D,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,oBAAoB,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAStH;AAED;sDACsD;AACtD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,OAAO,GACb;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOzD"}
@@ -0,0 +1,29 @@
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 { isSingleValuedSubject } from './rule-set-io-subject.js';
5
+ /** An `aggregate` needs a subject unless `fn` is `count`, and a numeric `fn`
6
+ * needs a single-valued one: summing a list of material names has no
7
+ * numeric meaning. Returns the violation, or `undefined`. */
8
+ export function checkAggregateSubject(fn, subject) {
9
+ if (subject === undefined) {
10
+ if (fn !== 'count')
11
+ return `"subject" is required unless fn is "count"`;
12
+ return undefined;
13
+ }
14
+ if (fn !== 'count' && !isSingleValuedSubject(subject)) {
15
+ return `"${fn}" needs a single-valued subject, "${subject.kind}" is multi-valued`;
16
+ }
17
+ return undefined;
18
+ }
19
+ /** Both sides of a `compare` must be single-valued. Returns the first
20
+ * offending side and its message, or `undefined`. */
21
+ export function checkCompareSubjects(left, right) {
22
+ for (const [side, subject] of [['left', left], ['right', right]]) {
23
+ if (!isSingleValuedSubject(subject)) {
24
+ return { side, message: `"compare" needs a single-valued subject, "${subject.kind}" is multi-valued` };
25
+ }
26
+ }
27
+ return undefined;
28
+ }
29
+ //# sourceMappingURL=requirement-invariants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-invariants.js","sourceRoot":"","sources":["../../src/rule-set/requirement-invariants.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAgB/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;8DAE8D;AAC9D,MAAM,UAAU,qBAAqB,CAAC,EAA8B,EAAE,OAA4B;IAChG,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,EAAE,KAAK,OAAO;YAAE,OAAO,4CAA4C,CAAC;QACxE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,EAAE,KAAK,OAAO,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;QACtD,OAAO,IAAI,EAAE,qCAAqC,OAAO,CAAC,IAAI,mBAAmB,CAAC;IACpF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;sDACsD;AACtD,MAAM,UAAU,oBAAoB,CAClC,IAAa,EACb,KAAc;IAEd,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAU,EAAE,CAAC;QAC1E,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,6CAA6C,OAAO,CAAC,IAAI,mBAAmB,EAAE,CAAC;QACzG,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Text form for `unique`/`aggregate`/`compare` requirements (#5138 §6):
3
+ * `unique(Name)`, `count() >= 1 by parent`, `sum(Qto_X.NetFloorArea) > 300`,
4
+ * `Pset_X.End > Pset_X.Start date`. NOT IfcOpenShell selector syntax — a
5
+ * small recursive-descent grammar apart from `packages/query`/
6
+ * `selector-to-rules.ts`. `element` requirements are out of scope here.
7
+ */
8
+ import type { UniqueRequirement, AggregateRequirement, CompareRequirement } from './rule-set.js';
9
+ export type TextRequirement = UniqueRequirement | AggregateRequirement | CompareRequirement;
10
+ export type RequirementTextResult = {
11
+ ok: true;
12
+ requirement: TextRequirement;
13
+ } | {
14
+ ok: false;
15
+ error: string;
16
+ };
17
+ export declare function requirementToText(req: TextRequirement): string;
18
+ /** Parses one requirement-text line; a bad token comes back as `{ ok: false, error }`. */
19
+ export declare function parseRequirementText(text: string): RequirementTextResult;
20
+ //# sourceMappingURL=requirement-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-text.d.ts","sourceRoot":"","sources":["../../src/rule-set/requirement-text.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,OAAO,KAAK,EAAW,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAI1G,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;AAC5F,MAAM,MAAM,qBAAqB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,eAAe,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAoB9G,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAW9D;AA0GD,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAUxE"}
@@ -0,0 +1,179 @@
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 { checkAggregateSubject, checkCompareSubjects } from './requirement-invariants.js';
5
+ const BARE_KINDS = {
6
+ Name: 'name', type: 'type', parent: 'parent', storey: 'storey',
7
+ material: 'material', globalId: 'globalId', predefinedType: 'predefinedType', ifcType: 'ifcType',
8
+ };
9
+ const OP_TEXT = { eq: '=', ne: '!=', gt: '>', gte: '>=', lt: '<', lte: '<=' };
10
+ const TEXT_OP = { '=': 'eq', '!=': 'ne', '>=': 'gte', '<=': 'lte', '>': 'gt', '<': 'lt' };
11
+ const quoted = (name) => (/^[^\s."()>=<!]+$/.test(name) ? name : `"${name}"`);
12
+ function subjectToText(s) {
13
+ if (s.kind === 'attribute')
14
+ return quoted(s.name);
15
+ if (s.kind === 'property')
16
+ return `${quoted(s.setName)}.${quoted(s.propertyName)}`;
17
+ if (s.kind === 'quantity')
18
+ return `${quoted(s.setName)}.${quoted(s.quantityName)}`;
19
+ // System-scoped classification has no text spelling (kept out of this
20
+ // ~20-line grammar per plan §6); `system` is dropped, never guessed back.
21
+ if (s.kind === 'classification')
22
+ return 'classification';
23
+ // Likewise a class-scoped group (#5226): `group` is any group.
24
+ if (s.kind === 'group')
25
+ return 'group';
26
+ return Object.entries(BARE_KINDS).find(([, kind]) => kind === s.kind)?.[0] ?? s.kind;
27
+ }
28
+ export function requirementToText(req) {
29
+ if (req.kind === 'unique') {
30
+ return `unique(${subjectToText(req.subject)})${req.scope === 'perModel' ? ' perModel' : ''}`;
31
+ }
32
+ if (req.kind === 'aggregate') {
33
+ const subject = req.subject ? subjectToText(req.subject) : '';
34
+ const by = req.groupBy ? ` by ${subjectToText(req.groupBy.subject)}` : '';
35
+ return `${req.fn}(${subject}) ${OP_TEXT[req.op]} ${req.value}${by}`;
36
+ }
37
+ const date = req.valueType === 'date' ? ' date' : '';
38
+ return `${subjectToText(req.left)} ${OP_TEXT[req.op]} ${subjectToText(req.right)}${date}`;
39
+ }
40
+ // Parser below: index-based, no token array. Every failure throws `TextError`,
41
+ // caught once in `parseRequirementText`.
42
+ class TextError extends Error {
43
+ }
44
+ /** A quoted string, or a run of non-punctuation chars. */
45
+ function readSegment(s, i) {
46
+ if (s[i] === '"') {
47
+ const end = s.indexOf('"', i + 1);
48
+ if (end < 0)
49
+ throw new TextError(`unterminated quote at ${JSON.stringify(s.slice(i))}`);
50
+ return [s.slice(i + 1, end), end + 1];
51
+ }
52
+ const m = /^[^\s."()>=<!]+/.exec(s.slice(i));
53
+ if (!m)
54
+ throw new TextError(`expected a name at ${JSON.stringify(s.slice(i))}`);
55
+ return [m[0], i + m[0].length];
56
+ }
57
+ function skipWs(s, i) {
58
+ while (s[i] === ' ')
59
+ i += 1;
60
+ return i;
61
+ }
62
+ function parseSubject(s, i) {
63
+ i = skipWs(s, i);
64
+ const [seg, j] = readSegment(s, i);
65
+ if (s[j] === '.') {
66
+ const [seg2, k] = readSegment(s, j + 1);
67
+ // `Qto_…` names the QUANTITY table (selector adapter's `looksLikeQuantitySet`).
68
+ const subject = seg.startsWith('Qto_')
69
+ ? { kind: 'quantity', setName: seg, quantityName: seg2 }
70
+ : { kind: 'property', setName: seg, propertyName: seg2 };
71
+ return [subject, k];
72
+ }
73
+ if (seg in BARE_KINDS)
74
+ return [{ kind: BARE_KINDS[seg] }, j];
75
+ if (seg === 'classification')
76
+ return [{ kind: 'classification' }, j];
77
+ if (seg === 'group')
78
+ return [{ kind: 'group' }, j];
79
+ return [{ kind: 'attribute', name: seg }, j];
80
+ }
81
+ function parseOp(s, i) {
82
+ i = skipWs(s, i);
83
+ const two = s.slice(i, i + 2);
84
+ if (two === '>=' || two === '<=' || two === '!=')
85
+ return [TEXT_OP[two], i + 2];
86
+ const one = s[i];
87
+ if (one === '=' || one === '>' || one === '<')
88
+ return [TEXT_OP[one], i + 1];
89
+ throw new TextError(`expected an operator at ${JSON.stringify(s.slice(i))}`);
90
+ }
91
+ function parseNumber(s, i) {
92
+ i = skipWs(s, i);
93
+ const m = /^-?\d+(\.\d+)?/.exec(s.slice(i));
94
+ if (!m)
95
+ throw new TextError(`expected a number at ${JSON.stringify(s.slice(i))}`);
96
+ return [Number.parseFloat(m[0]), i + m[0].length];
97
+ }
98
+ function parenSpan(s, fn) {
99
+ const open = s.indexOf('(');
100
+ const close = s.indexOf(')', open);
101
+ if (open < 0 || close < 0)
102
+ throw new TextError(`expected "${fn}(...)" in ${JSON.stringify(s)}`);
103
+ return [open, close];
104
+ }
105
+ /** The whole `(...)` body must be one subject — `sum(Foo Bar)` used to parse
106
+ * `Foo` and silently drop `Bar` (review on #5144). */
107
+ function parseParenSubject(s, open, close) {
108
+ const [subject, end] = parseSubject(s, open + 1);
109
+ const leftover = s.slice(end, close).trim();
110
+ if (leftover !== '')
111
+ throw new TextError(`unexpected ${JSON.stringify(leftover)} inside the parentheses`);
112
+ return subject;
113
+ }
114
+ function parseUnique(s) {
115
+ const [open, close] = parenSpan(s, 'unique');
116
+ const subject = parseParenSubject(s, open, close);
117
+ const rest = s.slice(close + 1).trim();
118
+ if (rest === '')
119
+ return { kind: 'unique', subject };
120
+ if (rest === 'perModel')
121
+ return { kind: 'unique', subject, scope: 'perModel' };
122
+ throw new TextError(`unexpected trailing text ${JSON.stringify(rest)}`);
123
+ }
124
+ function parseAggregate(s, fn) {
125
+ const [open, close] = parenSpan(s, fn);
126
+ const inner = s.slice(open + 1, close).trim();
127
+ const subject = inner === '' ? undefined : parseParenSubject(s, open, close);
128
+ // Shared with the JSON parser (#5182) — same two invariants, same
129
+ // wording, checked before the requirement is returned so the text and
130
+ // JSON authoring paths can't drift on what they accept.
131
+ const subjectError = checkAggregateSubject(fn, subject);
132
+ if (subjectError)
133
+ throw new TextError(subjectError);
134
+ const [op, afterOp] = parseOp(s, close + 1);
135
+ const [value, afterValue] = parseNumber(s, afterOp);
136
+ let i = afterValue;
137
+ let groupBy;
138
+ const byMatch = /^\s*by\s+/.exec(s.slice(i));
139
+ if (byMatch) {
140
+ i += byMatch[0].length;
141
+ const [gs, j] = parseSubject(s, i);
142
+ groupBy = { subject: gs };
143
+ i = j;
144
+ }
145
+ const trailing = s.slice(i).trim();
146
+ if (trailing !== '')
147
+ throw new TextError(`unexpected trailing text ${JSON.stringify(trailing)}`);
148
+ return { kind: 'aggregate', fn, ...(subject ? { subject } : {}), ...(groupBy ? { groupBy } : {}), op, value };
149
+ }
150
+ function parseCompare(s) {
151
+ const [left, afterLeft] = parseSubject(s, 0);
152
+ const [op, afterOp] = parseOp(s, afterLeft);
153
+ const [right, afterRight] = parseSubject(s, afterOp);
154
+ const sideError = checkCompareSubjects(left, right);
155
+ if (sideError)
156
+ throw new TextError(`${sideError.side}: ${sideError.message}`);
157
+ const trailing = s.slice(afterRight).trim();
158
+ if (trailing === '')
159
+ return { kind: 'compare', left, right, op };
160
+ if (trailing === 'date')
161
+ return { kind: 'compare', left, right, op, valueType: 'date' };
162
+ throw new TextError(`unexpected trailing text ${JSON.stringify(trailing)}`);
163
+ }
164
+ /** Parses one requirement-text line; a bad token comes back as `{ ok: false, error }`. */
165
+ export function parseRequirementText(text) {
166
+ const s = text.trim();
167
+ try {
168
+ if (s.startsWith('unique('))
169
+ return { ok: true, requirement: parseUnique(s) };
170
+ const fn = /^(count|sum|min|max|avg)\s*\(/.exec(s);
171
+ if (fn)
172
+ return { ok: true, requirement: parseAggregate(s, fn[1]) };
173
+ return { ok: true, requirement: parseCompare(s) };
174
+ }
175
+ catch (err) {
176
+ return { ok: false, error: err instanceof TextError ? err.message : `Failed to parse: ${err.message}` };
177
+ }
178
+ }
179
+ //# sourceMappingURL=requirement-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-text.js","sourceRoot":"","sources":["../../src/rule-set/requirement-text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `RuleBlock`/`Requirement` parsing for `.rules.json` files (#5138). Split
3
+ * out of `rule-set-io.ts` to stay under the module-size cap.
4
+ */
5
+ import type { RuleBlock, Requirement } from './rule-set.js';
6
+ import type { FilterRule } from '../filter/filter-rules.js';
7
+ /** Kinds a `requirement: { kind: 'element' }` block may filter on — the
8
+ * issue's nine operators, per-element only. `model`/`modelTag`/`storey`/
9
+ * `globalId`/`elevation` scope WHICH elements to check, they are not
10
+ * themselves checkable facts, so they belong in `applicability` only. */
11
+ /** Exported so the rule editor (#5138 PR 5, `validation/RuleBlockEditor.tsx`)
12
+ * can restrict its "Add rule" menu to exactly what this parser accepts —
13
+ * one list, not a second copy that could drift. */
14
+ export declare const ELEMENT_REQUIREMENT_KINDS: ReadonlySet<FilterRule['kind']>;
15
+ /** Parses a `RuleBlock`, restricting which `FilterRule` kinds may appear.
16
+ * Pass `null` for applicability/`groupBy.universe` (same vocabulary a saved
17
+ * search filter allows, minus `elevation`/ref-carrying `storey`, checked
18
+ * unconditionally below); pass `ELEMENT_REQUIREMENT_KINDS` for an
19
+ * `element` requirement's block. */
20
+ export declare function parseRuleBlock(raw: unknown, where: string, allowedKinds: ReadonlySet<FilterRule['kind']> | null): RuleBlock;
21
+ export declare function parseRequirement(raw: unknown, where: string): Requirement;
22
+ //# sourceMappingURL=rule-set-io-requirement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-set-io-requirement.d.ts","sourceRoot":"","sources":["../../src/rule-set/rule-set-io-requirement.ts"],"names":[],"mappings":"AAIA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAyF,MAAM,eAAe,CAAC;AAEnJ,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,2BAA2B,CAAC;AAUvE;;;0EAG0E;AAC1E;;oDAEoD;AACpD,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAGpE,CAAC;AAEH;;;;qCAIqC;AACrC,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAW3H;AAcD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAczE"}
@@ -0,0 +1,140 @@
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 { parseFilterGroups } from '../filter/filter-groups.js';
5
+ import { fail, isPlainObject, unknownKeysOf, warnUnknownFields } from './rule-set-io-shared.js';
6
+ import { parseSubject } from './rule-set-io-subject.js';
7
+ import { checkAggregateSubject, checkCompareSubjects } from './requirement-invariants.js';
8
+ const NUMERIC_OPS = new Set(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']);
9
+ const AGGREGATE_FNS = new Set(['count', 'sum', 'min', 'max', 'avg']);
10
+ const AUTHORED_AS = new Set(['chips', 'selector']);
11
+ const UNIQUE_SCOPES = new Set(['perModel', 'federation']);
12
+ /** Kinds a `requirement: { kind: 'element' }` block may filter on — the
13
+ * issue's nine operators, per-element only. `model`/`modelTag`/`storey`/
14
+ * `globalId`/`elevation` scope WHICH elements to check, they are not
15
+ * themselves checkable facts, so they belong in `applicability` only. */
16
+ /** Exported so the rule editor (#5138 PR 5, `validation/RuleBlockEditor.tsx`)
17
+ * can restrict its "Add rule" menu to exactly what this parser accepts —
18
+ * one list, not a second copy that could drift. */
19
+ export const ELEMENT_REQUIREMENT_KINDS = new Set([
20
+ 'property', 'quantity', 'attribute', 'name', 'material', 'classification',
21
+ 'type', 'parent', 'predefinedType', 'ifcType', 'group',
22
+ ]);
23
+ /** Parses a `RuleBlock`, restricting which `FilterRule` kinds may appear.
24
+ * Pass `null` for applicability/`groupBy.universe` (same vocabulary a saved
25
+ * search filter allows, minus `elevation`/ref-carrying `storey`, checked
26
+ * unconditionally below); pass `ELEMENT_REQUIREMENT_KINDS` for an
27
+ * `element` requirement's block. */
28
+ export function parseRuleBlock(raw, where, allowedKinds) {
29
+ if (!isPlainObject(raw))
30
+ fail(`${where}: must be an object`);
31
+ const b = raw;
32
+ const groups = parseFilterGroups(b.groups);
33
+ if (!groups)
34
+ fail(`${where}: "groups" is missing or unreadable`);
35
+ if (b.authoredAs !== undefined && !AUTHORED_AS.has(b.authoredAs))
36
+ fail(`${where}: bad "authoredAs"`);
37
+ for (const group of groups)
38
+ validateBlockRules(group, where, allowedKinds);
39
+ warnUnknownFields(where, unknownKeysOf(b, ['groups', 'authoredAs']));
40
+ return { groups, authoredAs: b.authoredAs ?? 'chips' };
41
+ }
42
+ function validateBlockRules(group, where, allowedKinds) {
43
+ for (const rule of group.rules) {
44
+ if (rule.kind === 'elevation')
45
+ fail(`${where}: "elevation" rules are not allowed (no aggregate meaning, plan §3)`);
46
+ if (rule.kind === 'storey' && 'refs' in rule && rule.refs) {
47
+ fail(`${where}: a storey rule with "refs" (local runtime ids) cannot be saved to a rule-set file`);
48
+ }
49
+ if (allowedKinds && !allowedKinds.has(rule.kind)) {
50
+ fail(`${where}: requirement rules may not use kind "${rule.kind}" (allowed: ${[...allowedKinds].join(', ')})`);
51
+ }
52
+ }
53
+ }
54
+ export function parseRequirement(raw, where) {
55
+ if (!isPlainObject(raw))
56
+ fail(`${where}: "requirement" must be an object`);
57
+ const r = raw;
58
+ const kind = r.kind;
59
+ if (kind === 'element') {
60
+ const block = parseRuleBlock(r.block, `${where}.block`, ELEMENT_REQUIREMENT_KINDS);
61
+ return { kind: 'element', block };
62
+ }
63
+ if (kind === 'unique')
64
+ return parseUniqueRequirement(r, where);
65
+ if (kind === 'aggregate')
66
+ return parseAggregateRequirement(r, where);
67
+ if (kind === 'compare')
68
+ return parseCompareRequirement(r, where);
69
+ if (kind === 'unit')
70
+ return parseUnitRequirement(r, where);
71
+ fail(`${where}: unrecognised requirement kind ${JSON.stringify(kind)}`);
72
+ }
73
+ function parseUniqueRequirement(r, where) {
74
+ const subject = parseSubject(r.subject, `${where}.subject`);
75
+ if (r.scope !== undefined && !UNIQUE_SCOPES.has(r.scope))
76
+ fail(`${where}: bad "scope"`);
77
+ return { kind: 'unique', subject, ...(r.scope !== undefined ? { scope: r.scope } : {}) };
78
+ }
79
+ function parseAggregateRequirement(r, where) {
80
+ if (typeof r.fn !== 'string' || !AGGREGATE_FNS.has(r.fn))
81
+ fail(`${where}: bad "fn"`);
82
+ const fn = r.fn;
83
+ let subject;
84
+ if (r.subject !== undefined) {
85
+ subject = parseSubject(r.subject, `${where}.subject`);
86
+ }
87
+ // Shared with the text parser (#5182) — a subject is required unless `fn`
88
+ // is `count`, and a present subject must be single-valued unless `fn` is
89
+ // `count` (multi-valued subjects like material/classification/parent only
90
+ // make sense as a bucketing key, not as something to sum/average).
91
+ const subjectError = checkAggregateSubject(fn, subject);
92
+ if (subjectError)
93
+ fail(`${where}: ${subjectError}`);
94
+ let groupBy;
95
+ if (r.groupBy !== undefined) {
96
+ if (!isPlainObject(r.groupBy))
97
+ fail(`${where}.groupBy: must be an object`);
98
+ const g = r.groupBy;
99
+ const groupSubject = parseSubject(g.subject, `${where}.groupBy.subject`);
100
+ const universe = g.universe !== undefined ? parseRuleBlock(g.universe, `${where}.groupBy.universe`, null) : undefined;
101
+ groupBy = { subject: groupSubject, ...(universe ? { universe } : {}) };
102
+ }
103
+ if (!NUMERIC_OPS.has(r.op))
104
+ fail(`${where}: bad "op"`);
105
+ if (typeof r.value !== 'number' || !Number.isFinite(r.value))
106
+ fail(`${where}: "value" must be a finite number`);
107
+ return {
108
+ kind: 'aggregate',
109
+ fn,
110
+ ...(subject ? { subject } : {}),
111
+ ...(groupBy ? { groupBy } : {}),
112
+ op: r.op,
113
+ value: r.value,
114
+ };
115
+ }
116
+ function parseCompareRequirement(r, where) {
117
+ const left = parseSubject(r.left, `${where}.left`);
118
+ const right = parseSubject(r.right, `${where}.right`);
119
+ const sideError = checkCompareSubjects(left, right);
120
+ if (sideError)
121
+ fail(`${where}.${sideError.side}: ${sideError.message}`);
122
+ if (!NUMERIC_OPS.has(r.op))
123
+ fail(`${where}: bad "op"`);
124
+ if (r.valueType !== undefined && r.valueType !== 'number' && r.valueType !== 'date')
125
+ fail(`${where}: bad "valueType"`);
126
+ return {
127
+ kind: 'compare', left, right, op: r.op,
128
+ ...(r.valueType !== undefined ? { valueType: r.valueType } : {}),
129
+ };
130
+ }
131
+ function parseUnitRequirement(r, where) {
132
+ const subject = parseSubject(r.subject, `${where}.subject`);
133
+ if (subject.kind !== 'property' && subject.kind !== 'quantity') {
134
+ fail(`${where}.subject: "unit" needs a property or quantity subject, not "${subject.kind}"`);
135
+ }
136
+ if (typeof r.unit !== 'string' || r.unit.trim().length === 0)
137
+ fail(`${where}: "unit" must be a non-empty string`);
138
+ return { kind: 'unit', subject, unit: r.unit.trim() };
139
+ }
140
+ //# sourceMappingURL=rule-set-io-requirement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-set-io-requirement.js","sourceRoot":"","sources":["../../src/rule-set/rule-set-io-requirement.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAQ/D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,4BAA4B,CAAC;AAEjF,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAE1F,MAAM,WAAW,GAAwB,IAAI,GAAG,CAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AACpG,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AACrE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AACnD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;AAE1D;;;0EAG0E;AAC1E;;oDAEoD;AACpD,MAAM,CAAC,MAAM,yBAAyB,GAAoC,IAAI,GAAG,CAAC;IAChF,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB;IACzE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,OAAO;CACvD,CAAC,CAAC;AAEH;;;;qCAIqC;AACrC,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,KAAa,EAAE,YAAoD;IAC9G,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,qBAAqB,CAAC,CAAC;IAC7D,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,IAAI,CAAC,GAAG,KAAK,qCAAqC,CAAC,CAAC;IACjE,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,UAAoB,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAE/G,KAAK,MAAM,KAAK,IAAI,MAAM;QAAE,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAE3E,iBAAiB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAG,CAAC,CAAC,UAAsC,IAAI,OAAO,EAAE,CAAC;AACtF,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAkB,EAAE,KAAa,EAAE,YAAoD;IACjH,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;YAAE,IAAI,CAAC,GAAG,KAAK,qEAAqE,CAAC,CAAC;QACnH,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1D,IAAI,CAAC,GAAG,KAAK,oFAAoF,CAAC,CAAC;QACrG,CAAC;QACD,IAAI,YAAY,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,KAAK,yCAAyC,IAAI,CAAC,IAAI,eAAe,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjH,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAY,EAAE,KAAa;IAC1D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,mCAAmC,CAAC,CAAC;IAC3E,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IAEpB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,QAAQ,EAAE,yBAAyB,CAAC,CAAC;QACnF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;IACD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,sBAAsB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/D,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,yBAAyB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACrE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,uBAAuB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACjE,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,oBAAoB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,CAAC,GAAG,KAAK,mCAAmC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,sBAAsB,CAAC,CAA0B,EAAE,KAAa;IACvE,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;IAC5D,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,KAAe,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,eAAe,CAAC,CAAC;IAClG,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAmC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACzH,CAAC;AAED,SAAS,yBAAyB,CAAC,CAA0B,EAAE,KAAa;IAC1E,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC;IACrF,MAAM,EAAE,GAAG,CAAC,CAAC,EAAgC,CAAC;IAE9C,IAAI,OAA4B,CAAC;IACjC,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;IACxD,CAAC;IACD,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,mEAAmE;IACnE,MAAM,YAAY,GAAG,qBAAqB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,YAAY;QAAE,IAAI,CAAC,GAAG,KAAK,KAAK,YAAY,EAAE,CAAC,CAAC;IAEpD,IAAI,OAAwC,CAAC;IAC7C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;YAAE,IAAI,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,CAAC,CAAC,OAAkC,CAAC;QAC/C,MAAM,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,kBAAkB,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,KAAK,mBAAmB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtH,OAAO,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACzE,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAY,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC;IACjE,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,mCAAmC,CAAC,CAAC;IAEhH,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,EAAE;QACF,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,EAAE,EAAE,CAAC,CAAC,EAAe;QACrB,KAAK,EAAE,CAAC,CAAC,KAAK;KACf,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,CAA0B,EAAE,KAAa;IACxE,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACpD,IAAI,SAAS;QAAE,IAAI,CAAC,GAAG,KAAK,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAY,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC;IACjE,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,KAAK,MAAM;QAAE,IAAI,CAAC,GAAG,KAAK,mBAAmB,CAAC,CAAC;IACvH,OAAO;QACL,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAe;QACnD,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAA4C,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpG,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,CAA0B,EAAE,KAAa;IACrE,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/D,IAAI,CAAC,GAAG,KAAK,+DAA+D,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,IAAI,CAAC,GAAG,KAAK,qCAAqC,CAAC,CAAC;IAClH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared plumbing for the `rule-set-io-*.ts` split (#5138): the exception
3
+ * type `parseRuleSetFile` catches to turn any failure into `{ ok: false,
4
+ * error }`, small JSON-shape guards, and the one-warning-per-parse budget
5
+ * for unknown optional fields (plan §3 forward-compat posture). Split out
6
+ * of `rule-set-io.ts` purely to stay under the ~400-line module cap — the
7
+ * parse logic is one recursive-descent pass over one file format, not
8
+ * several independent modules.
9
+ */
10
+ /** Thrown by every `parseX` helper on a validation failure; caught once at
11
+ * the top of `parseRuleSetFile` and turned into `{ ok: false, error }`.
12
+ * Never escapes this package. */
13
+ export declare class RuleSetError extends Error {
14
+ }
15
+ export declare function fail(message: string): never;
16
+ export declare function isPlainObject(v: unknown): v is Record<string, unknown>;
17
+ export declare function isStringArray(v: unknown): v is string[];
18
+ export declare function unknownKeysOf(obj: Record<string, unknown>, known: readonly string[]): string[];
19
+ export declare function resetWarnBudget(): void;
20
+ /** Warn once, for the whole parse, that unknown OPTIONAL fields were
21
+ * dropped — never for an unknown top-level key, which is a hard reject
22
+ * (`fail`), not a warning. */
23
+ export declare function warnUnknownFields(context: string, unknown: string[]): void;
24
+ //# sourceMappingURL=rule-set-io-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-set-io-shared.d.ts","sourceRoot":"","sources":["../../src/rule-set/rule-set-io-shared.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH;;kCAEkC;AAClC,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAE3C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtE;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,EAAE,CAEvD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAE9F;AASD,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;+BAE+B;AAC/B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAI1E"}