@orygn/opa-mcp 0.4.0 → 0.6.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 (236) hide show
  1. package/CHANGELOG.md +583 -4
  2. package/README.md +144 -98
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +9 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +14 -4
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +91 -19
  9. package/dist/config.js.map +1 -1
  10. package/dist/constants.d.ts +1 -6
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/constants.js +17 -3
  13. package/dist/constants.js.map +1 -1
  14. package/dist/lib/bundle-signatures.d.ts +23 -0
  15. package/dist/lib/bundle-signatures.d.ts.map +1 -0
  16. package/dist/lib/bundle-signatures.js +152 -0
  17. package/dist/lib/bundle-signatures.js.map +1 -0
  18. package/dist/lib/child-env.d.ts +9 -2
  19. package/dist/lib/child-env.d.ts.map +1 -1
  20. package/dist/lib/child-env.js +38 -6
  21. package/dist/lib/child-env.js.map +1 -1
  22. package/dist/lib/conftest-cli.d.ts +25 -10
  23. package/dist/lib/conftest-cli.d.ts.map +1 -1
  24. package/dist/lib/conftest-cli.js +233 -50
  25. package/dist/lib/conftest-cli.js.map +1 -1
  26. package/dist/lib/errors.d.ts +4 -5
  27. package/dist/lib/errors.d.ts.map +1 -1
  28. package/dist/lib/errors.js +15 -3
  29. package/dist/lib/errors.js.map +1 -1
  30. package/dist/lib/inline-paths.d.ts +37 -0
  31. package/dist/lib/inline-paths.d.ts.map +1 -0
  32. package/dist/lib/inline-paths.js +162 -0
  33. package/dist/lib/inline-paths.js.map +1 -0
  34. package/dist/lib/install-id.d.ts.map +1 -1
  35. package/dist/lib/install-id.js +18 -3
  36. package/dist/lib/install-id.js.map +1 -1
  37. package/dist/lib/json-stream.d.ts +26 -0
  38. package/dist/lib/json-stream.d.ts.map +1 -0
  39. package/dist/lib/json-stream.js +69 -0
  40. package/dist/lib/json-stream.js.map +1 -0
  41. package/dist/lib/opa-cli.d.ts +74 -16
  42. package/dist/lib/opa-cli.d.ts.map +1 -1
  43. package/dist/lib/opa-cli.js +107 -30
  44. package/dist/lib/opa-cli.js.map +1 -1
  45. package/dist/lib/opa-client.d.ts +27 -1
  46. package/dist/lib/opa-client.d.ts.map +1 -1
  47. package/dist/lib/opa-client.js +98 -14
  48. package/dist/lib/opa-client.js.map +1 -1
  49. package/dist/lib/opa-paths.d.ts +34 -0
  50. package/dist/lib/opa-paths.d.ts.map +1 -0
  51. package/dist/lib/opa-paths.js +101 -0
  52. package/dist/lib/opa-paths.js.map +1 -0
  53. package/dist/lib/output.d.ts.map +1 -1
  54. package/dist/lib/output.js +83 -10
  55. package/dist/lib/output.js.map +1 -1
  56. package/dist/lib/regal-cli.d.ts +16 -1
  57. package/dist/lib/regal-cli.d.ts.map +1 -1
  58. package/dist/lib/regal-cli.js +40 -6
  59. package/dist/lib/regal-cli.js.map +1 -1
  60. package/dist/lib/rego-ast-walker.d.ts +0 -6
  61. package/dist/lib/rego-ast-walker.d.ts.map +1 -1
  62. package/dist/lib/rego-ast-walker.js +181 -55
  63. package/dist/lib/rego-ast-walker.js.map +1 -1
  64. package/dist/lib/rego-counterexample.d.ts +15 -2
  65. package/dist/lib/rego-counterexample.d.ts.map +1 -1
  66. package/dist/lib/rego-counterexample.js +63 -15
  67. package/dist/lib/rego-counterexample.js.map +1 -1
  68. package/dist/lib/rego-input-path.d.ts +20 -0
  69. package/dist/lib/rego-input-path.d.ts.map +1 -0
  70. package/dist/lib/rego-input-path.js +70 -0
  71. package/dist/lib/rego-input-path.js.map +1 -0
  72. package/dist/lib/rego-ir.d.ts +34 -3
  73. package/dist/lib/rego-ir.d.ts.map +1 -1
  74. package/dist/lib/rego-smt-encoder.d.ts +88 -5
  75. package/dist/lib/rego-smt-encoder.d.ts.map +1 -1
  76. package/dist/lib/rego-smt-encoder.js +270 -31
  77. package/dist/lib/rego-smt-encoder.js.map +1 -1
  78. package/dist/lib/rego-type-inferencer.d.ts +27 -2
  79. package/dist/lib/rego-type-inferencer.d.ts.map +1 -1
  80. package/dist/lib/rego-type-inferencer.js +71 -3
  81. package/dist/lib/rego-type-inferencer.js.map +1 -1
  82. package/dist/lib/rego-verify-engine.d.ts.map +1 -1
  83. package/dist/lib/rego-verify-engine.js +203 -90
  84. package/dist/lib/rego-verify-engine.js.map +1 -1
  85. package/dist/lib/rego-z3.d.ts +66 -0
  86. package/dist/lib/rego-z3.d.ts.map +1 -1
  87. package/dist/lib/rego-z3.js +312 -5
  88. package/dist/lib/rego-z3.js.map +1 -1
  89. package/dist/lib/security.d.ts +8 -0
  90. package/dist/lib/security.d.ts.map +1 -1
  91. package/dist/lib/security.js +130 -54
  92. package/dist/lib/security.js.map +1 -1
  93. package/dist/lib/subprocess.d.ts +13 -0
  94. package/dist/lib/subprocess.d.ts.map +1 -1
  95. package/dist/lib/subprocess.js +153 -18
  96. package/dist/lib/subprocess.js.map +1 -1
  97. package/dist/lib/tool-helpers.d.ts +12 -25
  98. package/dist/lib/tool-helpers.d.ts.map +1 -1
  99. package/dist/lib/tool-helpers.js +66 -40
  100. package/dist/lib/tool-helpers.js.map +1 -1
  101. package/dist/prompts/index.js +2 -2
  102. package/dist/resources/index.js +1 -1
  103. package/dist/resources/index.js.map +1 -1
  104. package/dist/resources/patterns.d.ts +1 -1
  105. package/dist/resources/patterns.d.ts.map +1 -1
  106. package/dist/resources/patterns.js +42 -15
  107. package/dist/resources/patterns.js.map +1 -1
  108. package/dist/server.d.ts +2 -2
  109. package/dist/server.d.ts.map +1 -1
  110. package/dist/server.js +50 -7
  111. package/dist/server.js.map +1 -1
  112. package/dist/tools/authoring/capabilities.d.ts +4 -0
  113. package/dist/tools/authoring/capabilities.d.ts.map +1 -1
  114. package/dist/tools/authoring/capabilities.js +41 -4
  115. package/dist/tools/authoring/capabilities.js.map +1 -1
  116. package/dist/tools/authoring/check-schema.d.ts +6 -0
  117. package/dist/tools/authoring/check-schema.d.ts.map +1 -1
  118. package/dist/tools/authoring/check-schema.js +56 -3
  119. package/dist/tools/authoring/check-schema.js.map +1 -1
  120. package/dist/tools/authoring/format.d.ts.map +1 -1
  121. package/dist/tools/authoring/format.js +2 -2
  122. package/dist/tools/authoring/format.js.map +1 -1
  123. package/dist/tools/authoring/lint.d.ts.map +1 -1
  124. package/dist/tools/authoring/lint.js +8 -6
  125. package/dist/tools/authoring/lint.js.map +1 -1
  126. package/dist/tools/authoring/migrate-v1.d.ts.map +1 -1
  127. package/dist/tools/authoring/migrate-v1.js +20 -4
  128. package/dist/tools/authoring/migrate-v1.js.map +1 -1
  129. package/dist/tools/authoring/parse.d.ts.map +1 -1
  130. package/dist/tools/authoring/parse.js +3 -3
  131. package/dist/tools/authoring/parse.js.map +1 -1
  132. package/dist/tools/bundles/build.d.ts.map +1 -1
  133. package/dist/tools/bundles/build.js +12 -4
  134. package/dist/tools/bundles/build.js.map +1 -1
  135. package/dist/tools/bundles/sign.d.ts +9 -2
  136. package/dist/tools/bundles/sign.d.ts.map +1 -1
  137. package/dist/tools/bundles/sign.js +125 -19
  138. package/dist/tools/bundles/sign.js.map +1 -1
  139. package/dist/tools/bundles/verify.d.ts +1 -1
  140. package/dist/tools/bundles/verify.d.ts.map +1 -1
  141. package/dist/tools/bundles/verify.js +58 -19
  142. package/dist/tools/bundles/verify.js.map +1 -1
  143. package/dist/tools/conftest/index.d.ts +5 -0
  144. package/dist/tools/conftest/index.d.ts.map +1 -1
  145. package/dist/tools/conftest/index.js.map +1 -1
  146. package/dist/tools/conftest/pull.d.ts.map +1 -1
  147. package/dist/tools/conftest/pull.js +24 -10
  148. package/dist/tools/conftest/pull.js.map +1 -1
  149. package/dist/tools/conftest/push.d.ts.map +1 -1
  150. package/dist/tools/conftest/push.js +17 -7
  151. package/dist/tools/conftest/push.js.map +1 -1
  152. package/dist/tools/conftest/test.d.ts +10 -6
  153. package/dist/tools/conftest/test.d.ts.map +1 -1
  154. package/dist/tools/conftest/test.js +61 -39
  155. package/dist/tools/conftest/test.js.map +1 -1
  156. package/dist/tools/conftest/verify.d.ts +10 -6
  157. package/dist/tools/conftest/verify.d.ts.map +1 -1
  158. package/dist/tools/conftest/verify.js +40 -32
  159. package/dist/tools/conftest/verify.js.map +1 -1
  160. package/dist/tools/evaluation/_shared.js +9 -12
  161. package/dist/tools/evaluation/_shared.js.map +1 -1
  162. package/dist/tools/evaluation/bench.d.ts +29 -2
  163. package/dist/tools/evaluation/bench.d.ts.map +1 -1
  164. package/dist/tools/evaluation/bench.js +53 -9
  165. package/dist/tools/evaluation/bench.js.map +1 -1
  166. package/dist/tools/evaluation/compile.js +4 -4
  167. package/dist/tools/evaluation/compile.js.map +1 -1
  168. package/dist/tools/evaluation/eval.d.ts.map +1 -1
  169. package/dist/tools/evaluation/eval.js +12 -16
  170. package/dist/tools/evaluation/eval.js.map +1 -1
  171. package/dist/tools/evaluation/exec.d.ts +16 -0
  172. package/dist/tools/evaluation/exec.d.ts.map +1 -1
  173. package/dist/tools/evaluation/exec.js +38 -6
  174. package/dist/tools/evaluation/exec.js.map +1 -1
  175. package/dist/tools/evaluation/test-multiroot.d.ts +6 -0
  176. package/dist/tools/evaluation/test-multiroot.d.ts.map +1 -1
  177. package/dist/tools/evaluation/test-multiroot.js +35 -8
  178. package/dist/tools/evaluation/test-multiroot.js.map +1 -1
  179. package/dist/tools/evaluation/test.d.ts +45 -0
  180. package/dist/tools/evaluation/test.d.ts.map +1 -1
  181. package/dist/tools/evaluation/test.js +153 -32
  182. package/dist/tools/evaluation/test.js.map +1 -1
  183. package/dist/tools/helpers/coverage-gaps.d.ts +11 -3
  184. package/dist/tools/helpers/coverage-gaps.d.ts.map +1 -1
  185. package/dist/tools/helpers/coverage-gaps.js +7 -7
  186. package/dist/tools/helpers/coverage-gaps.js.map +1 -1
  187. package/dist/tools/helpers/describe-policy.d.ts.map +1 -1
  188. package/dist/tools/helpers/describe-policy.js +2 -2
  189. package/dist/tools/helpers/describe-policy.js.map +1 -1
  190. package/dist/tools/helpers/explain-decision.d.ts.map +1 -1
  191. package/dist/tools/helpers/explain-decision.js +3 -4
  192. package/dist/tools/helpers/explain-decision.js.map +1 -1
  193. package/dist/tools/helpers/explain-undefined.d.ts +9 -3
  194. package/dist/tools/helpers/explain-undefined.d.ts.map +1 -1
  195. package/dist/tools/helpers/explain-undefined.js +214 -73
  196. package/dist/tools/helpers/explain-undefined.js.map +1 -1
  197. package/dist/tools/helpers/fix.d.ts +11 -5
  198. package/dist/tools/helpers/fix.d.ts.map +1 -1
  199. package/dist/tools/helpers/fix.js +88 -45
  200. package/dist/tools/helpers/fix.js.map +1 -1
  201. package/dist/tools/helpers/generate-test-skeleton.d.ts.map +1 -1
  202. package/dist/tools/helpers/generate-test-skeleton.js +105 -21
  203. package/dist/tools/helpers/generate-test-skeleton.js.map +1 -1
  204. package/dist/tools/helpers/playground-share.d.ts +4 -2
  205. package/dist/tools/helpers/playground-share.d.ts.map +1 -1
  206. package/dist/tools/helpers/playground-share.js +13 -3
  207. package/dist/tools/helpers/playground-share.js.map +1 -1
  208. package/dist/tools/helpers/policy-diff.d.ts.map +1 -1
  209. package/dist/tools/helpers/policy-diff.js +3 -4
  210. package/dist/tools/helpers/policy-diff.js.map +1 -1
  211. package/dist/tools/helpers/security-audit.d.ts.map +1 -1
  212. package/dist/tools/helpers/security-audit.js +19 -19
  213. package/dist/tools/helpers/security-audit.js.map +1 -1
  214. package/dist/tools/helpers/verify.d.ts.map +1 -1
  215. package/dist/tools/helpers/verify.js +5 -1
  216. package/dist/tools/helpers/verify.js.map +1 -1
  217. package/dist/tools/meta/index.d.ts +1 -1
  218. package/dist/tools/server-management/_shared.d.ts +15 -4
  219. package/dist/tools/server-management/_shared.d.ts.map +1 -1
  220. package/dist/tools/server-management/_shared.js +86 -13
  221. package/dist/tools/server-management/_shared.js.map +1 -1
  222. package/dist/tools/server-management/data.d.ts.map +1 -1
  223. package/dist/tools/server-management/data.js +88 -23
  224. package/dist/tools/server-management/data.js.map +1 -1
  225. package/dist/tools/server-management/decisions.d.ts.map +1 -1
  226. package/dist/tools/server-management/decisions.js +16 -3
  227. package/dist/tools/server-management/decisions.js.map +1 -1
  228. package/dist/tools/server-management/policies.d.ts.map +1 -1
  229. package/dist/tools/server-management/policies.js +44 -7
  230. package/dist/tools/server-management/policies.js.map +1 -1
  231. package/dist/tools/server-management/status.d.ts.map +1 -1
  232. package/dist/tools/server-management/status.js +80 -13
  233. package/dist/tools/server-management/status.js.map +1 -1
  234. package/dist/types.d.ts +10 -1
  235. package/dist/types.d.ts.map +1 -1
  236. package/package.json +2 -1
@@ -1,29 +1,336 @@
1
1
  /**
2
2
  * Z3 singleton initialization.
3
3
  *
4
- * The z3-solver WASM module takes ~500ms to load and must only be
5
- * initialized once per process. Concurrent calls safely await the
6
- * same promise. Each verification call creates its own fresh Solver
7
- * from the shared Context, so contexts never accumulate stale state.
4
+ * The z3-solver WASM module takes ~500ms to load and is initialized once,
5
+ * unless it faults, in which case a fresh one replaces it (see getZ3).
6
+ * Concurrent calls safely await the same promise. Each verification call
7
+ * creates its own fresh Solver from the shared Context and releases it, and
8
+ * its model, as it finishes; that is what keeps the heap bounded.
9
+ *
10
+ * A fresh Solver is NOT enough to make concurrent verification safe. The whole
11
+ * Context lives in one single-threaded WASM heap, and `check()` is async, so two
12
+ * overlapping solves interleave on that heap and corrupt each other: observed
13
+ * symptoms were verdicts degrading to `inconclusive` and an allocator crash
14
+ * inside the WASM module. Everything touching the Context must therefore run
15
+ * under `withZ3Lock`.
8
16
  */
17
+ import { AsyncLocalStorage } from 'node:async_hooks';
9
18
  import { init } from 'z3-solver';
19
+ /**
20
+ * Ceiling on Z3's own allocations, in megabytes. Reached, Z3 raises an
21
+ * ordinary exception that the engine turns into an inconclusive verdict.
22
+ * Without it the WASM heap itself runs out, and that abort surfaces outside
23
+ * every try/catch as an uncaughtException that ends the server.
24
+ */
25
+ export const Z3_MEMORY_MAX_MB = 1024;
26
+ /**
27
+ * The solver's own bound, checked at its checkpoints, where running out
28
+ * degrades to an "unknown" answer. It sits well below the process ceiling on
29
+ * purpose: at the same value the allocator's exception won the race, and an
30
+ * allocation failing inside a destructor ends in an abort rather than an
31
+ * error. Measured in review: equal caps aborted four times in four; a lower
32
+ * solver bound answered "unknown" four times in four.
33
+ */
34
+ export const Z3_SOLVER_MAX_MEMORY_MB = 768;
10
35
  // Module-level singleton
11
36
  let z3InitPromise = null;
37
+ let z3Api;
38
+ /** Set once Z3 has failed outside a try/catch; it is not trusted again. */
39
+ let z3Unusable;
40
+ /** Number of Z3 critical sections currently running (0 or 1). */
41
+ let z3Depth = 0;
42
+ /** Rejects the section in flight; set while one is running. */
43
+ let poisonInFlight;
44
+ /** Generation of the module in use; a finalizer from an earlier one is dropped. */
45
+ let z3Generation = 0;
46
+ /** Finalizers that arrived while a solve was running, to run once it is not. */
47
+ let deferredFinalizers = [];
48
+ /** Fresh modules this process may still bring up after a fault. */
49
+ let recoveriesLeft = 3;
50
+ /**
51
+ * The registry class this process started with, and the identity of each
52
+ * init along its own async chain: z3-solver builds its registry in the
53
+ * continuation after its WASM loads, so the store there names the init that
54
+ * owns it, however inits interleave.
55
+ */
56
+ const NativeRegistry = globalThis.FinalizationRegistry;
57
+ const registryGlobal = globalThis;
58
+ const initInFlight = new AsyncLocalStorage();
59
+ /**
60
+ * Inits whose window is open; the global goes back when the last closes. An
61
+ * init that never settles leaves it on the subclass, which behaves natively
62
+ * for everything off an init's chain: only the class identity differs.
63
+ */
64
+ let initWindows = 0;
65
+ /**
66
+ * The stack at the call, with enough frames kept to reach the caller's module
67
+ * whatever `Error.stackTraceLimit` the host has chosen: a limit of zero, a
68
+ * common speed setting, would otherwise leave nothing to read and the wrap
69
+ * would quietly not happen.
70
+ */
71
+ function stackHere() {
72
+ const limit = Error.stackTraceLimit;
73
+ Error.stackTraceLimit = 20;
74
+ try {
75
+ return new Error().stack ?? '';
76
+ }
77
+ finally {
78
+ Error.stackTraceLimit = limit;
79
+ }
80
+ }
81
+ /**
82
+ * The registry z3-solver gets while an init runs.
83
+ *
84
+ * z3-solver frees every AST, solver and model from one FinalizationRegistry,
85
+ * built inside its createApi, whose callbacks run on the main thread whenever
86
+ * the collector gets to them. A solve runs on a worker over the same shared
87
+ * memory, and a callback landing mid-solve raced it on Z3's allocator and
88
+ * faulted the heap (measured: a collection forced mid-solve crashed inside a
89
+ * hundred solves; the same collection while the worker was idle ran 1500
90
+ * clean). So a finalizer that arrives while a section is open waits for the
91
+ * section to close, and one from a module that has since faulted is dropped.
92
+ *
93
+ * Each construction looks at its own stack and async context: one from
94
+ * z3-solver on an init's chain is wrapped with that init's generation; any
95
+ * other gets the real behaviour. Inits can overlap, since a recovery starts
96
+ * the next while the one given up on is still coming up, and each still
97
+ * gets its own generation: the frees of the abandoned module are dropped
98
+ * and the recovered module's are routed.
99
+ *
100
+ * Releasing the solver and model as each solve ends removes most of the
101
+ * finalizer traffic that made the race reachable; the deferral covers what
102
+ * a collection still frees.
103
+ */
104
+ class DeferringRegistry extends NativeRegistry {
105
+ constructor(callback) {
106
+ const owner = initInFlight.getStore();
107
+ const mine = owner !== undefined && stackHere().includes('z3-solver') ? owner : undefined;
108
+ const generation = mine?.generation;
109
+ super(mine === undefined
110
+ ? callback
111
+ : (held) => {
112
+ if (generation !== z3Generation)
113
+ return;
114
+ if (z3Depth > 0)
115
+ deferredFinalizers.push(() => callback(held));
116
+ else
117
+ callback(held);
118
+ });
119
+ }
120
+ }
121
+ /** Bring up z3-solver with its finalizers routed through this module. */
122
+ async function initModule() {
123
+ initWindows++;
124
+ registryGlobal.FinalizationRegistry = DeferringRegistry;
125
+ try {
126
+ return await initInFlight.run({ generation: z3Generation }, () => init());
127
+ }
128
+ finally {
129
+ if (--initWindows === 0)
130
+ registryGlobal.FinalizationRegistry = NativeRegistry;
131
+ }
132
+ }
133
+ /**
134
+ * Run the finalizers held back during a solve; only called with the worker
135
+ * idle. Each is a dec_ref; one that throws must not take the rest, or the
136
+ * lock, with it.
137
+ */
138
+ function runDeferredFinalizers() {
139
+ const run = deferredFinalizers;
140
+ deferredFinalizers = [];
141
+ for (const finalizer of run) {
142
+ try {
143
+ finalizer();
144
+ }
145
+ catch {
146
+ // A free that fails leaks one object; the section still closes.
147
+ }
148
+ }
149
+ }
12
150
  /**
13
151
  * Return the shared Z3 Context, initializing WASM on first call.
14
152
  * Safe to call from concurrent async paths.
15
153
  */
16
154
  export async function getZ3() {
155
+ if (z3Unusable !== undefined) {
156
+ if (recoveriesLeft === 0) {
157
+ throw new Error(`Z3 is unavailable in this process after repeated failures (${z3Unusable}). Restart the server to verify again.`);
158
+ }
159
+ // The heap that faulted cannot be trusted, so bring up a fresh module.
160
+ // Every init() is a new WASM instance with its own memory and worker
161
+ // (measured: two live side by side, independent, ~100 ms each), so
162
+ // nothing is shared with the one that faulted; its finalizers were
163
+ // dropped by generation when it was given up on. The faulted instance
164
+ // is retained, about 30 MB and one worker each, which is why the number
165
+ // of recoveries is bounded for the life of the process.
166
+ recoveriesLeft--;
167
+ z3Unusable = undefined;
168
+ z3InitPromise = null;
169
+ z3Api = undefined;
170
+ }
17
171
  if (z3InitPromise === null) {
18
- z3InitPromise = init().then(({ Context }) => Context('main'));
172
+ const started = initModule().then((api) => {
173
+ // An init a recovery superseded while it was coming up must not
174
+ // become the module in use; whoever was waiting on it is told.
175
+ if (z3InitPromise !== started)
176
+ throw new Error('Z3 init superseded by a recovery');
177
+ api.setParam('memory_max_size', Z3_MEMORY_MAX_MB);
178
+ z3Api = api;
179
+ return api.Context('main');
180
+ });
181
+ z3InitPromise = started;
19
182
  }
20
183
  return z3InitPromise;
21
184
  }
185
+ /** Read back a Z3 global parameter, after initialisation. */
186
+ export async function getZ3Param(name) {
187
+ await getZ3();
188
+ return z3Api?.getParam(name) ?? null;
189
+ }
190
+ /** Whether a Z3 critical section is running right now. */
191
+ export function isZ3Busy() {
192
+ return z3Depth > 0;
193
+ }
194
+ /**
195
+ * Give up on the Z3 module in use. Called when it failed outside a try/catch:
196
+ * the heap it lives in cannot be trusted. The next call brings up a fresh
197
+ * module, a bounded number of times per process.
198
+ */
199
+ export function markZ3Unusable(reason) {
200
+ z3Unusable = reason;
201
+ // Move the generation now, before anything unwinds: the poison below ends
202
+ // the section, whose close would otherwise run every deferred free into
203
+ // the heap that just faulted, and any finalizer that arrives later from
204
+ // this module must be dropped rather than run.
205
+ z3Generation++;
206
+ deferredFinalizers = [];
207
+ // A heap abort arrives from the WASM worker and leaves the solve's promise
208
+ // unsettled for good. Settle it here, so the section releases the lock and
209
+ // the calls queued behind it are answered instead of hanging.
210
+ poisonInFlight?.(new Error(`Z3 became unusable during the solve (${reason}).`));
211
+ }
212
+ /**
213
+ * Whether an error message is one of the two this module produces once Z3
214
+ * has been given up on. The engine passes such a message through to the
215
+ * caller verbatim, since a restart is the one thing they can do about it.
216
+ */
217
+ export function isZ3UnavailableMessage(detail) {
218
+ return /Z3 (is unavailable|became unusable)/.test(detail);
219
+ }
220
+ /**
221
+ * Whether a failure is the solve in flight being cut short by a fault, which
222
+ * a fresh module can answer: the engine retries such a call once.
223
+ */
224
+ export function isZ3RecoverableMessage(detail) {
225
+ return /Z3 became unusable during the solve/.test(detail);
226
+ }
227
+ /**
228
+ * How many fresh modules this process may still bring up after a fault.
229
+ * Three for the life of the process, however far apart the faults: each
230
+ * retained instance costs memory, and a fourth fault is a pattern to
231
+ * restart out of, not to ride.
232
+ */
233
+ export function z3RecoveriesLeft() {
234
+ return recoveriesLeft;
235
+ }
236
+ /** For tests: how many finalizers are waiting for the open section to close. */
237
+ export function deferredFinalizersForTesting() {
238
+ return deferredFinalizers.length;
239
+ }
240
+ /**
241
+ * For tests: what z3-solver's finalizers go through. Runs at once when no
242
+ * section is open, otherwise once the open one closes.
243
+ */
244
+ export function enqueueFinalizerForTesting(finalizer) {
245
+ if (z3Depth > 0)
246
+ deferredFinalizers.push(finalizer);
247
+ else
248
+ finalizer();
249
+ }
250
+ /**
251
+ * Whether an uncaught error is the WASM heap giving out, rather than some
252
+ * unrelated failure that happened while a solve was running.
253
+ */
254
+ export function isZ3Failure(e) {
255
+ // A WebAssembly.RuntimeError is named just that; the global is not in this
256
+ // project's compiler libs, so the name is the check.
257
+ if (e instanceof Error && e.name === 'RuntimeError')
258
+ return true;
259
+ const message = e instanceof Error ? e.message : String(e);
260
+ // A call-stack overflow was also seen once as a symptom of a corrupted
261
+ // heap; it counts only when the stack shows it came from the WASM glue,
262
+ // so an overflow elsewhere does not spend a recovery. With no frames to
263
+ // read (a host that keeps none) the conservative answer stands: while a
264
+ // solve is running, the overflow is Z3's.
265
+ if (/Maximum call stack size exceeded/.test(message)) {
266
+ if (!(e instanceof Error))
267
+ return false;
268
+ const stack = e.stack ?? '';
269
+ if (!/\n\s+at /.test(stack))
270
+ return true;
271
+ return stack.includes('z3-built');
272
+ }
273
+ return /Aborted\(|memory access out of bounds|out of memory|unreachable executed/i.test(message);
274
+ }
275
+ /**
276
+ * Tail of the queue of pending Z3 critical sections. Each acquirer waits on the
277
+ * current tail and installs its own completion as the new tail, so sections run
278
+ * in call order with no overlap.
279
+ */
280
+ let z3LockTail = Promise.resolve();
281
+ /**
282
+ * Run `fn` with exclusive access to the shared Z3 Context.
283
+ *
284
+ * Serializes rather than parallelizes: an MCP client is free to issue several
285
+ * rego_verify calls at once, and letting them into the WASM heap together
286
+ * crashes it. Solve times are in the tens of milliseconds, so queueing is
287
+ * cheaper than the alternative of one Context per call, which would re-pay the
288
+ * WASM init cost every time.
289
+ */
290
+ export async function withZ3Lock(fn) {
291
+ let release;
292
+ const mine = new Promise((resolve) => {
293
+ release = resolve;
294
+ });
295
+ const predecessor = z3LockTail;
296
+ z3LockTail = predecessor.then(() => mine);
297
+ await predecessor;
298
+ z3Depth++;
299
+ const poisoned = new Promise((_resolve, reject) => {
300
+ poisonInFlight = reject;
301
+ });
302
+ try {
303
+ return await Promise.race([fn(), poisoned]);
304
+ }
305
+ finally {
306
+ poisonInFlight = undefined;
307
+ z3Depth--;
308
+ try {
309
+ // The worker is idle: whatever the collector noticed mid-solve can be
310
+ // freed now.
311
+ if (z3Depth === 0)
312
+ runDeferredFinalizers();
313
+ }
314
+ finally {
315
+ // Whatever happened above, the lock must pass on, or every later call
316
+ // waits for ever.
317
+ release();
318
+ }
319
+ }
320
+ }
22
321
  /**
23
322
  * Reset the singleton - intended for test teardown only.
24
323
  * Do NOT call in production paths; re-init is expensive.
25
324
  */
26
325
  export function resetZ3ForTesting() {
27
326
  z3InitPromise = null;
327
+ z3Api = undefined;
328
+ z3Unusable = undefined;
329
+ z3Depth = 0;
330
+ poisonInFlight = undefined;
331
+ z3LockTail = Promise.resolve();
332
+ z3Generation++;
333
+ deferredFinalizers = [];
334
+ recoveriesLeft = 3;
28
335
  }
29
336
  //# sourceMappingURL=rego-z3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rego-z3.js","sourceRoot":"","sources":["../../src/lib/rego-z3.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,yBAAyB;AACzB,IAAI,aAAa,GAA4B,IAAI,CAAC;AAElD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,aAAa,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,aAAmC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,aAAa,GAAG,IAAI,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"rego-z3.js","sourceRoot":"","sources":["../../src/lib/rego-z3.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,14 @@ export interface PathValidationResult {
7
7
  /**
8
8
  * Resolve `inputPath` and confirm it is contained within at least one
9
9
  * `allowedRoots` entry. Returns the resolved absolute path on success.
10
+ *
11
+ * Two checks, both of which must pass. The first is syntactic: `..` segments
12
+ * are collapsed without touching the disk and the result must sit under a
13
+ * root as written. The second follows the filesystem: the path's real
14
+ * location, including the part of it that does not exist yet, must sit under
15
+ * a root's real location. A link is still followed at read or write time by
16
+ * the tool that receives the path, so the check is against the layout at the
17
+ * moment of validation.
10
18
  */
11
19
  export declare function validatePath(inputPath: string, allowedRoots: string[], options?: {
12
20
  mustExist?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,oBAAoB,CAwGtB;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD"}
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;CAC7B;AA6ED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,oBAAoB,CAsGtB;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD"}
@@ -5,12 +5,90 @@
5
5
  * `validatePath` to prevent reading or writing outside the
6
6
  * agreed-upon roots.
7
7
  */
8
- import { existsSync, realpathSync, statSync } from 'node:fs';
9
- import { isAbsolute, resolve, sep } from 'node:path';
8
+ import { lstatSync, realpathSync, statSync } from 'node:fs';
9
+ import { basename, dirname, isAbsolute, join, resolve, sep } from 'node:path';
10
10
  import { err } from './errors.js';
11
+ const WINDOWS = process.platform === 'win32';
12
+ /**
13
+ * Canonical form of an existing path: every link resolved, and on Windows
14
+ * 8.3 short names expanded and on-disk casing restored. The JavaScript
15
+ * `realpathSync` resolves links but leaves short names and casing alone, so
16
+ * two spellings of one directory could compare unequal.
17
+ */
18
+ function canonical(p) {
19
+ return realpathSync.native(p);
20
+ }
21
+ /** Does `p` fall under at least one of `roots`? */
22
+ function isUnderRoots(p, roots, caseInsensitive) {
23
+ const fold = (s) => (caseInsensitive ? s.toLowerCase() : s);
24
+ const candidate = fold(p);
25
+ return roots.some((root) => {
26
+ const r = fold(root);
27
+ const rootWithSep = r.endsWith(sep) ? r : r + sep;
28
+ return candidate === r || candidate.startsWith(rootWithSep);
29
+ });
30
+ }
31
+ /**
32
+ * Where a path really leads, whether or not it exists yet.
33
+ *
34
+ * Walks up from `p` to the nearest entry that exists, resolves that entry's
35
+ * real location, and re-attaches the segments below it. A write to `p` lands
36
+ * under that real location, so that is what has to be checked against the
37
+ * roots. Checking only paths that already exist left every not-yet-created
38
+ * write target unchecked, and a junction or symlink inside a root could point
39
+ * it anywhere.
40
+ *
41
+ * An entry that exists only as a link to something that does not exist (a
42
+ * dangling link) is reported on its own: a write through it would create the
43
+ * target wherever the link points.
44
+ */
45
+ function realLocation(p) {
46
+ const below = [];
47
+ let cur = p;
48
+ for (;;) {
49
+ let entry;
50
+ try {
51
+ entry = lstatSync(cur);
52
+ }
53
+ catch (e) {
54
+ const code = e.code;
55
+ if (code === 'ENOENT' || code === 'ENOTDIR') {
56
+ const parent = dirname(cur);
57
+ if (parent === cur)
58
+ return { kind: 'unresolvable', reason: 'no existing ancestor' };
59
+ below.unshift(basename(cur));
60
+ cur = parent;
61
+ continue;
62
+ }
63
+ return { kind: 'unresolvable', reason: code ?? String(e) };
64
+ }
65
+ let real;
66
+ try {
67
+ real = canonical(cur);
68
+ }
69
+ catch (e) {
70
+ if (entry.isSymbolicLink())
71
+ return { kind: 'dangling', link: cur };
72
+ return { kind: 'unresolvable', reason: e.code ?? String(e) };
73
+ }
74
+ return {
75
+ kind: 'real',
76
+ path: below.length === 0 ? real : join(real, ...below),
77
+ exists: below.length === 0,
78
+ };
79
+ }
80
+ }
11
81
  /**
12
82
  * Resolve `inputPath` and confirm it is contained within at least one
13
83
  * `allowedRoots` entry. Returns the resolved absolute path on success.
84
+ *
85
+ * Two checks, both of which must pass. The first is syntactic: `..` segments
86
+ * are collapsed without touching the disk and the result must sit under a
87
+ * root as written. The second follows the filesystem: the path's real
88
+ * location, including the part of it that does not exist yet, must sit under
89
+ * a root's real location. A link is still followed at read or write time by
90
+ * the tool that receives the path, so the check is against the layout at the
91
+ * moment of validation.
14
92
  */
15
93
  export function validatePath(inputPath, allowedRoots, options = {}) {
16
94
  if (typeof inputPath !== 'string' || inputPath.length === 0) {
@@ -28,24 +106,42 @@ export function validatePath(inputPath, allowedRoots, options = {}) {
28
106
  };
29
107
  }
30
108
  const resolved = isAbsolute(inputPath) ? resolve(inputPath) : resolve(process.cwd(), inputPath);
31
- // Helper: confirm p falls under at least one of the provided root strings.
32
- const isUnderRoots = (p, roots) => roots.some((root) => {
33
- const rootWithSep = root.endsWith(sep) ? root : root + sep;
34
- return p === root || p.startsWith(rootWithSep);
35
- });
36
- // Phase 1: syntactic traversal check. path.resolve() collapses `..`
37
- // segments without I/O, so this catches plain traversal attacks cheaply.
38
109
  const resolvedRoots = allowedRoots.map((r) => resolve(r));
39
- if (!isUnderRoots(resolved, resolvedRoots)) {
110
+ const outsideHint = 'Set OPA_MCP_ALLOWED_PATHS to a comma-separated list of directories, or use a path under the current allowed roots.';
111
+ // Phase 1: syntactic containment, cheap and free of I/O. It is not the
112
+ // decision on its own: a root spelled through a link (macOS /var, a Windows
113
+ // short name, a junction) and a path spelled through the target land in the
114
+ // same real place and must be accepted. It settles which message an
115
+ // escaping path gets. Windows compares without regard to case.
116
+ const syntacticallyInside = isUnderRoots(resolved, resolvedRoots, WINDOWS);
117
+ const outsideRoots = () => err('PATH_NOT_ALLOWED', `Path is outside allowed roots: ${inputPath}`, {
118
+ hint: outsideHint,
119
+ details: { resolved, allowedRoots },
120
+ });
121
+ // Phase 2: real location. This is the decision.
122
+ const location = realLocation(resolved);
123
+ if (location.kind === 'dangling') {
124
+ return {
125
+ ok: false,
126
+ error: err('PATH_NOT_ALLOWED', `Path goes through a link whose target does not exist: ${inputPath}`, {
127
+ hint: 'A write through a dangling link would create its target wherever the link points. Remove the link or use a path that does not go through it.',
128
+ details: { resolved, link: location.link },
129
+ }),
130
+ };
131
+ }
132
+ if (location.kind === 'unresolvable') {
133
+ if (!syntacticallyInside)
134
+ return { ok: false, error: outsideRoots() };
40
135
  return {
41
136
  ok: false,
42
- error: err('PATH_NOT_ALLOWED', `Path is outside allowed roots: ${inputPath}`, {
43
- hint: 'Set OPA_MCP_ALLOWED_PATHS to a comma-separated list of directories, or use a path under the current allowed roots.',
44
- details: { resolved, allowedRoots },
137
+ error: err('PATH_NOT_FOUND', `Path could not be fully resolved: ${inputPath}`, {
138
+ details: { resolved, reason: location.reason },
45
139
  }),
46
140
  };
47
141
  }
48
- if (options.mustExist && !existsSync(resolved)) {
142
+ if (options.mustExist && !location.exists) {
143
+ if (!syntacticallyInside)
144
+ return { ok: false, error: outsideRoots() };
49
145
  return {
50
146
  ok: false,
51
147
  error: err('PATH_NOT_FOUND', `Path does not exist: ${inputPath}`, {
@@ -53,51 +149,31 @@ export function validatePath(inputPath, allowedRoots, options = {}) {
53
149
  }),
54
150
  };
55
151
  }
56
- // Phase 2: symlink resolution. path.resolve() is purely syntactic and
57
- // does not follow symlinks. A symlink inside an allowed root can point
58
- // arbitrarily outside. For any path that exists on disk, resolve the
59
- // real target and re-validate against the real allowed-root locations.
60
- //
61
- // We validate via realpath but return `resolved` (the syntactic path)
62
- // for backward compatibility across OSes where system directories are
63
- // themselves symlinks (e.g. macOS /var -> /private/var). Callers pass
64
- // the returned path to OPA/regal; the OS follows any remaining symlinks
65
- // at read time, which is safe because we have verified the real target.
66
- if (existsSync(resolved)) {
67
- let realPath;
152
+ // A root that is itself a link, or spelled with a short name, compares
153
+ // correctly only in canonical form. A root that does not exist stays as
154
+ // written; nothing under it can resolve to it anyway.
155
+ const realRoots = resolvedRoots.map((r) => {
68
156
  try {
69
- realPath = realpathSync(resolved);
157
+ return canonical(r);
70
158
  }
71
159
  catch {
72
- // Rare race: path removed between existsSync and realpathSync, or
73
- // insufficient permissions to read the symlink chain. Fail closed.
74
- return {
75
- ok: false,
76
- error: err('PATH_NOT_FOUND', `Path could not be fully resolved: ${inputPath}`, {
77
- details: { resolved },
78
- }),
79
- };
80
- }
81
- // Also realpath the roots themselves so that a root that is a symlink
82
- // compares correctly against the realpath of the input.
83
- const realRoots = resolvedRoots.map((r) => {
84
- try {
85
- return existsSync(r) ? realpathSync(r) : r;
86
- }
87
- catch {
88
- return r;
89
- }
90
- });
91
- if (!isUnderRoots(realPath, realRoots)) {
92
- return {
93
- ok: false,
94
- error: err('PATH_NOT_ALLOWED', `Path resolves outside allowed roots via symlink: ${inputPath}`, {
95
- hint: 'Set OPA_MCP_ALLOWED_PATHS to a comma-separated list of directories, or use a path under the current allowed roots.',
96
- details: { resolved, realPath, allowedRoots },
97
- }),
98
- };
160
+ return r;
99
161
  }
162
+ });
163
+ if (!isUnderRoots(location.path, realRoots, false)) {
164
+ if (!syntacticallyInside)
165
+ return { ok: false, error: outsideRoots() };
166
+ return {
167
+ ok: false,
168
+ error: err('PATH_NOT_ALLOWED', `Path resolves outside allowed roots via a link: ${inputPath}`, {
169
+ hint: outsideHint,
170
+ details: { resolved, realPath: location.path, allowedRoots },
171
+ }),
172
+ };
100
173
  }
174
+ // The syntactic path is returned, not the canonical one: on macOS the
175
+ // system temp directory is itself a link, and tools compare and report
176
+ // paths as the caller spelled them.
101
177
  return { ok: true, resolved };
102
178
  }
103
179
  /** Convenience: returns true if the path is a directory (after validation). */
@@ -1 +1 @@
1
- {"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"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"}
@@ -54,7 +54,20 @@ export interface SpawnResult {
54
54
  * valid; absent means "not truncated".
55
55
  */
56
56
  outputTruncated?: boolean;
57
+ /**
58
+ * The signal that ended the child, when one did. Set for the server's own
59
+ * kills (timeout, cancellation, output cap) and for kills from outside it,
60
+ * such as the kernel's out-of-memory killer. Optional for the same reason as
61
+ * `outputTruncated`.
62
+ */
63
+ signal?: NodeJS.Signals | null;
57
64
  }
65
+ /**
66
+ * Signal every child still running. A child sits in its own process group on
67
+ * POSIX, so a signal that stops the server does not reach it on its own; the
68
+ * server calls this on the way out. Returns how many were signalled.
69
+ */
70
+ export declare function terminateChildren(sig?: NodeJS.Signals): number;
58
71
  /**
59
72
  * Run a binary and return its captured output. Never throws -- failures are
60
73
  * reflected in `exitCode` / `timedOut` / `outputTruncated`.
@@ -1 +1 @@
1
- {"version":3,"file":"subprocess.d.ts","sourceRoot":"","sources":["../../src/lib/subprocess.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,wDAAwD;IACxD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA6CD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAmIxF"}
1
+ {"version":3,"file":"subprocess.d.ts","sourceRoot":"","sources":["../../src/lib/subprocess.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,wDAAwD;IACxD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CAChC;AAgDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,OAAmB,GAAG,MAAM,CAOzE;AA6CD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA4MxF"}