modality-ts 0.0.10 → 0.0.11

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 (200) hide show
  1. package/README.md +3 -1
  2. package/dist/check/diagnostics/bounds.d.ts +2 -1
  3. package/dist/check/diagnostics/bounds.d.ts.map +1 -1
  4. package/dist/check/diagnostics/bounds.js +3 -3
  5. package/dist/check/diagnostics/bounds.js.map +1 -1
  6. package/dist/check/engine/check-model.d.ts +3 -0
  7. package/dist/check/engine/check-model.d.ts.map +1 -1
  8. package/dist/check/engine/check-model.js +408 -43
  9. package/dist/check/engine/check-model.js.map +1 -1
  10. package/dist/check/engine/model-api.d.ts.map +1 -1
  11. package/dist/check/engine/model-api.js +5 -3
  12. package/dist/check/engine/model-api.js.map +1 -1
  13. package/dist/check/engine/stabilize.d.ts +2 -1
  14. package/dist/check/engine/stabilize.d.ts.map +1 -1
  15. package/dist/check/engine/stabilize.js +31 -9
  16. package/dist/check/engine/stabilize.js.map +1 -1
  17. package/dist/check/engine/state-utils.d.ts +3 -2
  18. package/dist/check/engine/state-utils.d.ts.map +1 -1
  19. package/dist/check/engine/state-utils.js +20 -7
  20. package/dist/check/engine/state-utils.js.map +1 -1
  21. package/dist/check/engine/transitions.d.ts +9 -1
  22. package/dist/check/engine/transitions.d.ts.map +1 -1
  23. package/dist/check/engine/transitions.js +32 -5
  24. package/dist/check/engine/transitions.js.map +1 -1
  25. package/dist/check/index.d.ts +1 -1
  26. package/dist/check/index.d.ts.map +1 -1
  27. package/dist/check/index.js.map +1 -1
  28. package/dist/check/properties/finalize.d.ts +4 -3
  29. package/dist/check/properties/finalize.d.ts.map +1 -1
  30. package/dist/check/properties/finalize.js +45 -9
  31. package/dist/check/properties/finalize.js.map +1 -1
  32. package/dist/check/properties/leads-to.d.ts.map +1 -1
  33. package/dist/check/properties/leads-to.js +4 -3
  34. package/dist/check/properties/leads-to.js.map +1 -1
  35. package/dist/check/properties/observe.d.ts +4 -3
  36. package/dist/check/properties/observe.d.ts.map +1 -1
  37. package/dist/check/properties/observe.js +5 -5
  38. package/dist/check/properties/observe.js.map +1 -1
  39. package/dist/check/properties/reachable-from.d.ts +4 -2
  40. package/dist/check/properties/reachable-from.d.ts.map +1 -1
  41. package/dist/check/properties/reachable-from.js +2 -2
  42. package/dist/check/properties/reachable-from.js.map +1 -1
  43. package/dist/check/slicing/slice-model.d.ts +1 -0
  44. package/dist/check/slicing/slice-model.d.ts.map +1 -1
  45. package/dist/check/slicing/slice-model.js +38 -12
  46. package/dist/check/slicing/slice-model.js.map +1 -1
  47. package/dist/check/traces/trace.d.ts +7 -2
  48. package/dist/check/traces/trace.d.ts.map +1 -1
  49. package/dist/check/traces/trace.js +9 -4
  50. package/dist/check/traces/trace.js.map +1 -1
  51. package/dist/check/types.d.ts +83 -3
  52. package/dist/check/types.d.ts.map +1 -1
  53. package/dist/cli/cli.js +76 -4
  54. package/dist/cli/cli.js.map +1 -1
  55. package/dist/cli/features/check/command.d.ts +6 -0
  56. package/dist/cli/features/check/command.d.ts.map +1 -1
  57. package/dist/cli/features/check/command.js +74 -1
  58. package/dist/cli/features/check/command.js.map +1 -1
  59. package/dist/cli/features/conform/command.d.ts.map +1 -1
  60. package/dist/cli/features/conform/command.js +3 -1
  61. package/dist/cli/features/conform/command.js.map +1 -1
  62. package/dist/cli/features/export/command.js +12 -4
  63. package/dist/cli/features/export/command.js.map +1 -1
  64. package/dist/cli/features/extract/command.d.ts.map +1 -1
  65. package/dist/cli/features/extract/command.js +302 -92
  66. package/dist/cli/features/extract/command.js.map +1 -1
  67. package/dist/cli/features/init/command.js +1 -1
  68. package/dist/cli/features/init/command.js.map +1 -1
  69. package/dist/cli/harness/index.d.ts.map +1 -1
  70. package/dist/cli/harness/index.js +17 -4
  71. package/dist/cli/harness/index.js.map +1 -1
  72. package/dist/cli/registry/index.d.ts +4 -4
  73. package/dist/cli/registry/index.d.ts.map +1 -1
  74. package/dist/cli/registry/index.js +6 -4
  75. package/dist/cli/registry/index.js.map +1 -1
  76. package/dist/core/ir/domains.d.ts +2 -0
  77. package/dist/core/ir/domains.d.ts.map +1 -1
  78. package/dist/core/ir/domains.js +71 -0
  79. package/dist/core/ir/domains.js.map +1 -1
  80. package/dist/core/ir/validator.d.ts +4 -1
  81. package/dist/core/ir/validator.d.ts.map +1 -1
  82. package/dist/core/ir/validator.js +45 -12
  83. package/dist/core/ir/validator.js.map +1 -1
  84. package/dist/core/props/index.d.ts.map +1 -1
  85. package/dist/core/props/index.js.map +1 -1
  86. package/dist/core/report/types.d.ts +68 -0
  87. package/dist/core/report/types.d.ts.map +1 -1
  88. package/dist/extract/engine/pipeline/index.d.ts +3 -1
  89. package/dist/extract/engine/pipeline/index.d.ts.map +1 -1
  90. package/dist/extract/engine/pipeline/index.js +10 -23
  91. package/dist/extract/engine/pipeline/index.js.map +1 -1
  92. package/dist/extract/engine/spi/index.d.ts +38 -7
  93. package/dist/extract/engine/spi/index.d.ts.map +1 -1
  94. package/dist/extract/engine/spi/index.js +1 -1
  95. package/dist/extract/engine/spi/index.js.map +1 -1
  96. package/dist/extract/engine/ts/domains.d.ts +1 -1
  97. package/dist/extract/engine/ts/domains.d.ts.map +1 -1
  98. package/dist/extract/engine/ts/domains.js +25 -17
  99. package/dist/extract/engine/ts/domains.js.map +1 -1
  100. package/dist/extract/engine/ts/react-source-transitions.d.ts +2 -1
  101. package/dist/extract/engine/ts/react-source-transitions.d.ts.map +1 -1
  102. package/dist/extract/engine/ts/react-source-transitions.js +17 -7
  103. package/dist/extract/engine/ts/react-source-transitions.js.map +1 -1
  104. package/dist/extract/engine/ts/routes.d.ts +2 -2
  105. package/dist/extract/engine/ts/routes.d.ts.map +1 -1
  106. package/dist/extract/engine/ts/routes.js +5 -24
  107. package/dist/extract/engine/ts/routes.js.map +1 -1
  108. package/dist/extract/engine/ts/static-navigation.d.ts +2 -1
  109. package/dist/extract/engine/ts/static-navigation.d.ts.map +1 -1
  110. package/dist/extract/engine/ts/static-navigation.js +30 -20
  111. package/dist/extract/engine/ts/static-navigation.js.map +1 -1
  112. package/dist/extract/engine/ts/transition/async.d.ts +2 -10
  113. package/dist/extract/engine/ts/transition/async.d.ts.map +1 -1
  114. package/dist/extract/engine/ts/transition/async.js +30 -61
  115. package/dist/extract/engine/ts/transition/async.js.map +1 -1
  116. package/dist/extract/engine/ts/transition/effects.d.ts.map +1 -1
  117. package/dist/extract/engine/ts/transition/effects.js +7 -4
  118. package/dist/extract/engine/ts/transition/effects.js.map +1 -1
  119. package/dist/extract/engine/ts/transition/expressions.d.ts.map +1 -1
  120. package/dist/extract/engine/ts/transition/expressions.js +3 -1
  121. package/dist/extract/engine/ts/transition/expressions.js.map +1 -1
  122. package/dist/extract/engine/ts/transition/guards.d.ts.map +1 -1
  123. package/dist/extract/engine/ts/transition/guards.js +1 -1
  124. package/dist/extract/engine/ts/transition/guards.js.map +1 -1
  125. package/dist/extract/engine/ts/transition/handlers.d.ts.map +1 -1
  126. package/dist/extract/engine/ts/transition/handlers.js +7 -5
  127. package/dist/extract/engine/ts/transition/handlers.js.map +1 -1
  128. package/dist/extract/engine/ts/transition/locals.d.ts.map +1 -1
  129. package/dist/extract/engine/ts/transition/locals.js +2 -1
  130. package/dist/extract/engine/ts/transition/locals.js.map +1 -1
  131. package/dist/extract/engine/ts/transition/navigation.d.ts +7 -5
  132. package/dist/extract/engine/ts/transition/navigation.d.ts.map +1 -1
  133. package/dist/extract/engine/ts/transition/navigation.js +85 -41
  134. package/dist/extract/engine/ts/transition/navigation.js.map +1 -1
  135. package/dist/extract/engine/ts/transition/plugin-calls.d.ts.map +1 -1
  136. package/dist/extract/engine/ts/transition/plugin-calls.js +1 -1
  137. package/dist/extract/engine/ts/transition/plugin-calls.js.map +1 -1
  138. package/dist/extract/engine/ts/transition/timers.d.ts.map +1 -1
  139. package/dist/extract/engine/ts/transition/timers.js +5 -3
  140. package/dist/extract/engine/ts/transition/timers.js.map +1 -1
  141. package/dist/extract/sources/jotai/domains.d.ts +2 -1
  142. package/dist/extract/sources/jotai/domains.d.ts.map +1 -1
  143. package/dist/extract/sources/jotai/domains.js +2 -164
  144. package/dist/extract/sources/jotai/domains.js.map +1 -1
  145. package/dist/extract/sources/jotai/harness.d.ts.map +1 -1
  146. package/dist/extract/sources/jotai/harness.js +5 -2
  147. package/dist/extract/sources/jotai/harness.js.map +1 -1
  148. package/dist/extract/sources/jotai/transitions.d.ts.map +1 -1
  149. package/dist/extract/sources/jotai/transitions.js +1 -4
  150. package/dist/extract/sources/jotai/transitions.js.map +1 -1
  151. package/dist/extract/sources/router/discover.d.ts +9 -0
  152. package/dist/extract/sources/router/discover.d.ts.map +1 -0
  153. package/dist/extract/sources/router/discover.js +135 -0
  154. package/dist/extract/sources/router/discover.js.map +1 -0
  155. package/dist/extract/sources/router/index.d.ts +7 -3
  156. package/dist/extract/sources/router/index.d.ts.map +1 -1
  157. package/dist/extract/sources/router/index.js +21 -12
  158. package/dist/extract/sources/router/index.js.map +1 -1
  159. package/dist/extract/sources/router/navigation.d.ts +3 -4
  160. package/dist/extract/sources/router/navigation.d.ts.map +1 -1
  161. package/dist/extract/sources/router/navigation.js +33 -1
  162. package/dist/extract/sources/router/navigation.js.map +1 -1
  163. package/dist/extract/sources/router/redirects.d.ts +4 -0
  164. package/dist/extract/sources/router/redirects.d.ts.map +1 -0
  165. package/dist/extract/sources/router/redirects.js +37 -0
  166. package/dist/extract/sources/router/redirects.js.map +1 -0
  167. package/dist/extract/sources/router/routes.d.ts +2 -2
  168. package/dist/extract/sources/router/routes.d.ts.map +1 -1
  169. package/dist/extract/sources/router/routes.js +24 -6
  170. package/dist/extract/sources/router/routes.js.map +1 -1
  171. package/dist/extract/sources/shared/react-transition-extract.d.ts +1 -0
  172. package/dist/extract/sources/shared/react-transition-extract.d.ts.map +1 -1
  173. package/dist/extract/sources/shared/react-transition-extract.js +1 -0
  174. package/dist/extract/sources/shared/react-transition-extract.js.map +1 -1
  175. package/dist/extract/sources/swr/domains.d.ts +3 -2
  176. package/dist/extract/sources/swr/domains.d.ts.map +1 -1
  177. package/dist/extract/sources/swr/domains.js +3 -96
  178. package/dist/extract/sources/swr/domains.js.map +1 -1
  179. package/dist/extract/sources/swr/harness.d.ts.map +1 -1
  180. package/dist/extract/sources/swr/harness.js +13 -6
  181. package/dist/extract/sources/swr/harness.js.map +1 -1
  182. package/dist/extract/sources/swr/template.js +1 -1
  183. package/dist/extract/sources/swr/template.js.map +1 -1
  184. package/dist/extract/sources/swr/transitions.d.ts.map +1 -1
  185. package/dist/extract/sources/swr/transitions.js.map +1 -1
  186. package/dist/extract/sources/swr/writes.d.ts.map +1 -1
  187. package/dist/extract/sources/swr/writes.js +1 -1
  188. package/dist/extract/sources/swr/writes.js.map +1 -1
  189. package/dist/extract/sources/use-state/harness.d.ts.map +1 -1
  190. package/dist/extract/sources/use-state/harness.js +5 -2
  191. package/dist/extract/sources/use-state/harness.js.map +1 -1
  192. package/dist/extract/sources/use-state/index.d.ts.map +1 -1
  193. package/dist/extract/sources/use-state/index.js +1 -166
  194. package/dist/extract/sources/use-state/index.js.map +1 -1
  195. package/dist/extract/sources/use-state/transitions.d.ts.map +1 -1
  196. package/dist/extract/sources/use-state/transitions.js +1 -0
  197. package/dist/extract/sources/use-state/transitions.js.map +1 -1
  198. package/dist/extract/sources/use-state/types.d.ts +1 -0
  199. package/dist/extract/sources/use-state/types.d.ts.map +1 -1
  200. package/package.json +2 -2
package/README.md CHANGED
@@ -36,6 +36,8 @@ Check the extracted model against a property file:
36
36
  modality check
37
37
  ```
38
38
 
39
+ `modality check` applies conservative default search limits (`--max-states`, `--max-edges`, `--max-frontier`, `--memory-guard-mb`). Use `--no-search-limits` for intentionally unbounded runs.
40
+
39
41
  When a property fails, replay the generated counterexample trace:
40
42
 
41
43
  ```bash
@@ -53,7 +55,7 @@ Useful commands:
53
55
  ```bash
54
56
  modality init
55
57
  modality extract [source.tsx ...]
56
- modality check [model.json] [props.mjs ...]
58
+ modality check [model.json] [props.mjs ...] [--max-states N] [--max-edges N] [--max-frontier N] [--memory-guard-mb N] [--no-search-limits]
57
59
  modality replay <trace.json>
58
60
  modality conform --count 8 --depth 4
59
61
  modality export
@@ -1,4 +1,5 @@
1
1
  import type { Model, ModelState, Transition } from "modality-ts/core";
2
- export declare function recordMaxDepthBoundHits(model: Model, frontier: readonly ModelState[], enabledTransitionIds: Set<string>, boundHits: Set<string>): void;
2
+ import { type TransitionIndex } from "../engine/transitions.js";
3
+ export declare function recordMaxDepthBoundHits(model: Model, frontier: readonly ModelState[], enabledTransitionIds: Set<string>, boundHits: Set<string>, index?: TransitionIndex): void;
3
4
  export declare function effectContainsEnqueue(effect: Transition["effect"]): boolean;
4
5
  //# sourceMappingURL=bounds.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bounds.d.ts","sourceRoot":"","sources":["../../../src/check/diagnostics/bounds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGtE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,EACjC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,IAAI,CAYN;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,OAAO,CAQ3E"}
1
+ {"version":3,"file":"bounds.d.ts","sourceRoot":"","sources":["../../../src/check/diagnostics/bounds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,EACjC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EACtB,KAAK,CAAC,EAAE,eAAe,GACtB,IAAI,CAYN;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,OAAO,CAQ3E"}
@@ -1,10 +1,10 @@
1
- import { enabledTransitions } from "../engine/transitions.js";
2
- export function recordMaxDepthBoundHits(model, frontier, enabledTransitionIds, boundHits) {
1
+ import { enabledTransitions, } from "../engine/transitions.js";
2
+ export function recordMaxDepthBoundHits(model, frontier, enabledTransitionIds, boundHits, index) {
3
3
  if (frontier.length === 0)
4
4
  return;
5
5
  const blockedTransitions = new Set();
6
6
  for (const state of frontier) {
7
- for (const transition of enabledTransitions(model, state)) {
7
+ for (const transition of enabledTransitions(model, state, index)) {
8
8
  enabledTransitionIds.add(transition.id);
9
9
  blockedTransitions.add(transition.id);
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bounds.js","sourceRoot":"","sources":["../../../src/check/diagnostics/bounds.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,UAAU,uBAAuB,CACrC,KAAY,EACZ,QAA+B,EAC/B,oBAAiC,EACjC,SAAsB;IAEtB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAClC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC7C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,UAAU,IAAI,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;YAC1D,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxC,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,kBAAkB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,SAAS,CAAC,GAAG,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA4B;IAChE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC7E,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;QACtB,OAAO,CACL,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CACzE,CAAC;IACJ,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"bounds.js","sourceRoot":"","sources":["../../../src/check/diagnostics/bounds.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAElC,MAAM,UAAU,uBAAuB,CACrC,KAAY,EACZ,QAA+B,EAC/B,oBAAiC,EACjC,SAAsB,EACtB,KAAuB;IAEvB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAClC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC7C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,UAAU,IAAI,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;YACjE,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACxC,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,kBAAkB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,SAAS,CAAC,GAAG,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA4B;IAChE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC7E,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;QACtB,OAAO,CACL,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CACzE,CAAC;IACJ,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,4 +1,7 @@
1
1
  import type { Model, Property } from "modality-ts/core";
2
2
  import type { CheckOptions, CheckResult } from "../types.js";
3
+ export declare function needsRecordedEdges(properties: readonly Property[]): boolean;
4
+ export declare function needsReverseGraph(properties: readonly Property[]): boolean;
5
+ export declare function needsStepMonitoring(properties: readonly Property[]): boolean;
3
6
  export declare function checkModel(model: Model, properties: readonly Property[], options?: CheckOptions): CheckResult;
4
7
  //# sourceMappingURL=check-model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"check-model.d.ts","sourceRoot":"","sources":["../../../src/check/engine/check-model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAc,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAWpE,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EAIZ,MAAM,aAAa,CAAC;AAUrB,wBAAgB,UAAU,CACxB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,SAAS,QAAQ,EAAE,EAC/B,OAAO,GAAE,YAAiB,GACzB,WAAW,CASb"}
1
+ {"version":3,"file":"check-model.d.ts","sourceRoot":"","sources":["../../../src/check/engine/check-model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,KAAK,EAEL,QAAQ,EAGT,MAAM,kBAAkB,CAAC;AAe1B,OAAO,KAAK,EAEV,YAAY,EACZ,WAAW,EAOZ,MAAM,aAAa,CAAC;AAuBrB,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAE1E;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAE5E;AAqFD,wBAAgB,UAAU,CACxB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,SAAS,QAAQ,EAAE,EAC/B,OAAO,GAAE,YAAiB,GACzB,WAAW,CAyBb"}
@@ -4,44 +4,184 @@ import { vacuityWarnings } from "../diagnostics/vacuity.js";
4
4
  import { finalizeProperties } from "../properties/finalize.js";
5
5
  import { observeEdge, observeStates } from "../properties/observe.js";
6
6
  import { applyEffect } from "../runtime/effects.js";
7
- import { sliceModelForProperty } from "../slicing/slice-model.js";
7
+ import { canSliceProperty, sliceModelForProperty, } from "../slicing/slice-model.js";
8
8
  import { facts } from "../traces/step-facts.js";
9
9
  import { initialStates } from "./initial-states.js";
10
10
  import { stabilize } from "./stabilize.js";
11
- import { changedVars, compareStates, initialChangedVars, } from "./state-utils.js";
12
- import { enabledTransitions, installEnabledHook } from "./transitions.js";
11
+ import { changedVars, initialChangedVars, sortStatesByCanon, } from "./state-utils.js";
12
+ import { buildTransitionIndex, enabledTransitions, installEnabledHook, } from "./transitions.js";
13
+ export function needsRecordedEdges(properties) {
14
+ return properties.some((property) => property.kind === "leadsToWithin");
15
+ }
16
+ export function needsReverseGraph(properties) {
17
+ return properties.some((property) => property.kind === "reachableFrom");
18
+ }
19
+ export function needsStepMonitoring(properties) {
20
+ return properties.some((property) => property.kind === "alwaysStep");
21
+ }
22
+ function resolveEdgeRecordingMode(properties) {
23
+ if (needsRecordedEdges(properties))
24
+ return "compact";
25
+ if (needsReverseGraph(properties))
26
+ return "reverse";
27
+ return "none";
28
+ }
29
+ function createGraphRecording(mode) {
30
+ return {
31
+ mode,
32
+ compactEdges: [],
33
+ reverseEdges: [],
34
+ fullEdges: [],
35
+ };
36
+ }
37
+ function recordExploredEdge(graph, properties, preCanon, postCanon, pre, post, transition, step) {
38
+ switch (graph.mode) {
39
+ case "full":
40
+ graph.fullEdges.push({
41
+ preCanon,
42
+ postCanon,
43
+ pre,
44
+ post,
45
+ transition,
46
+ step,
47
+ });
48
+ break;
49
+ case "compact":
50
+ graph.compactEdges.push({
51
+ preCanon,
52
+ postCanon,
53
+ transitionId: transition.id,
54
+ triggeredProperties: properties
55
+ .filter((property) => property.kind === "leadsToWithin")
56
+ .filter((property) => property.trigger(step))
57
+ .map((property) => property.name),
58
+ });
59
+ break;
60
+ case "reverse":
61
+ graph.reverseEdges.push({ preCanon, postCanon });
62
+ break;
63
+ case "none":
64
+ break;
65
+ }
66
+ }
67
+ function buildStorageDiagnostics(parents, states, graph) {
68
+ const recordedEdges = graph.mode === "none"
69
+ ? 0
70
+ : graph.mode === "reverse"
71
+ ? graph.reverseEdges.length
72
+ : graph.mode === "compact"
73
+ ? graph.compactEdges.length
74
+ : graph.fullEdges.length;
75
+ return {
76
+ recordedEdges,
77
+ storedStates: states.size,
78
+ parentEntries: parents.size,
79
+ edgeRecordingMode: graph.mode,
80
+ };
81
+ }
13
82
  export function checkModel(model, properties, options = {}) {
83
+ const slicingDiagnostics = buildSlicingRequestDiagnostics(properties, options.slicing === true);
14
84
  if (options.slicing &&
15
85
  properties.length > 0 &&
16
86
  properties.every((property) => property.reads !== undefined)) {
17
- return checkModelSliced(model, properties);
87
+ const result = checkModelSliced(model, properties, options);
88
+ return {
89
+ ...result,
90
+ diagnostics: mergeDiagnostics(result.diagnostics, {
91
+ slicing: slicingDiagnostics,
92
+ }),
93
+ };
18
94
  }
19
- return checkModelCore(model, properties);
95
+ const result = checkModelCore(model, properties, options);
96
+ return {
97
+ ...result,
98
+ diagnostics: mergeDiagnostics(result.diagnostics, {
99
+ slicing: slicingDiagnostics,
100
+ }),
101
+ };
20
102
  }
21
- function checkModelCore(model, properties) {
22
- const validation = validateModel(model);
103
+ function buildSlicingRequestDiagnostics(properties, slicingRequested) {
104
+ if (!slicingRequested) {
105
+ return { enabled: false };
106
+ }
107
+ if (properties.length === 0) {
108
+ return { enabled: false, skipped: true, skipReason: "no properties" };
109
+ }
110
+ if (!properties.every((property) => property.reads !== undefined)) {
111
+ return {
112
+ enabled: false,
113
+ skipped: true,
114
+ skipReason: "property missing reads",
115
+ };
116
+ }
117
+ return { enabled: true };
118
+ }
119
+ function checkModelCore(model, properties, options = {}) {
120
+ const validation = validateModel(model, { sliced: options.slicedModel });
23
121
  if (!validation.ok)
24
122
  return invalidModelResult(properties, validation.errors);
123
+ const startedAt = options.trackElapsed ? Date.now() : undefined;
25
124
  installEnabledHook(model);
125
+ const transitionIndex = buildTransitionIndex(model);
126
+ const canonCache = new WeakMap();
127
+ const canon = (state) => {
128
+ const cached = canonCache.get(state);
129
+ if (cached !== undefined)
130
+ return cached;
131
+ const encoded = canonicalState(model, state);
132
+ canonCache.set(state, encoded);
133
+ return encoded;
134
+ };
26
135
  const parents = new Map();
27
136
  const states = new Map();
28
- const edges = [];
137
+ const graph = createGraphRecording(resolveEdgeRecordingMode(properties));
138
+ const traceCtx = { model, parents, states };
29
139
  const enabledTransitionIds = new Set();
30
140
  const boundHits = new Set();
31
- let frontier = seedFrontier(model, parents, states);
141
+ const tracker = createSearchTracker(model);
142
+ let frontier = seedFrontier(model, parents, states, tracker, transitionIndex, canon);
32
143
  const verdicts = new Map();
33
144
  let depth = 0;
34
145
  let edgeCount = 0;
35
- observeStates(model, properties, frontier, parents, verdicts);
36
- while (frontier.length > 0 && depth < model.bounds.maxDepth) {
37
- const result = exploreDepth(model, properties, frontier, parents, states, edges, verdicts, enabledTransitionIds, boundHits);
146
+ observeStates(model, properties, frontier, traceCtx, verdicts);
147
+ recordDominantVars(model, frontier, tracker);
148
+ while (frontier.length > 0 &&
149
+ depth < model.bounds.maxDepth &&
150
+ tracker.limitHit === null) {
151
+ tracker.maxFrontier = Math.max(tracker.maxFrontier, frontier.length);
152
+ tracker.finalFrontier = frontier.length;
153
+ const limit = checkSearchLimits(options, parents.size, edgeCount, frontier.length, depth);
154
+ if (limit) {
155
+ tracker.limitHit = limit;
156
+ break;
157
+ }
158
+ const result = exploreDepth(model, properties, frontier, parents, states, graph, traceCtx, verdicts, enabledTransitionIds, boundHits, tracker, options, edgeCount, depth, transitionIndex, canon);
38
159
  frontier = result.next;
39
160
  edgeCount += result.edges;
40
- observeStates(model, properties, frontier, parents, verdicts);
161
+ observeStates(model, properties, frontier, traceCtx, verdicts);
162
+ recordDominantVars(model, frontier, tracker);
41
163
  depth += 1;
164
+ tracker.expandedDepths = depth;
165
+ options.onProgress?.({
166
+ depth,
167
+ frontier: frontier.length,
168
+ nextFrontier: frontier.length,
169
+ states: parents.size,
170
+ edges: edgeCount,
171
+ });
172
+ const postLimit = checkSearchLimits(options, parents.size, edgeCount, frontier.length, depth);
173
+ if (postLimit) {
174
+ tracker.limitHit = postLimit;
175
+ break;
176
+ }
177
+ }
178
+ recordMaxDepthBoundHits(model, frontier, enabledTransitionIds, boundHits, transitionIndex);
179
+ if (tracker.limitHit) {
180
+ applySearchLimitVerdicts(properties, verdicts, tracker.limitHit);
181
+ }
182
+ else {
183
+ finalizeProperties(model, properties, traceCtx, graph, verdicts);
42
184
  }
43
- recordMaxDepthBoundHits(model, frontier, enabledTransitionIds, boundHits);
44
- finalizeProperties(model, properties, parents, states, edges, verdicts);
45
185
  return {
46
186
  verdicts: properties.map((property) => verdicts.get(property.name) ?? {
47
187
  status: "verified-within-bounds",
@@ -50,6 +190,7 @@ function checkModelCore(model, properties) {
50
190
  stats: { states: parents.size, edges: edgeCount, depth },
51
191
  vacuityWarnings: vacuityWarnings(model, states, enabledTransitionIds),
52
192
  boundHits: [...boundHits].sort(),
193
+ diagnostics: buildSearchDiagnostics(tracker, startedAt, buildStorageDiagnostics(parents, states, graph), transitionIndex),
53
194
  };
54
195
  }
55
196
  function invalidModelResult(properties, errors) {
@@ -64,30 +205,125 @@ function invalidModelResult(properties, errors) {
64
205
  boundHits: [],
65
206
  };
66
207
  }
67
- function seedFrontier(model, parents, states) {
68
- const frontier = initialStates(model)
69
- .flatMap((state) => stabilize(model, state, initialChangedVars(model)))
70
- .sort(compareStates(model));
208
+ function createSearchTracker(model) {
209
+ return {
210
+ maxFrontier: 0,
211
+ finalFrontier: 0,
212
+ expandedDepths: 0,
213
+ dominantVarValues: new Map(model.vars.map((decl) => [decl.id, new Set()])),
214
+ limitHit: null,
215
+ };
216
+ }
217
+ function recordDominantVars(model, frontier, tracker) {
71
218
  for (const state of frontier) {
72
- const canon = canonicalState(model, state);
73
- if (!parents.has(canon)) {
74
- parents.set(canon, {
75
- parent: null,
76
- transition: null,
77
- pre: null,
78
- post: state,
79
- });
80
- states.set(canon, state);
219
+ for (const decl of model.vars) {
220
+ const value = JSON.stringify(state[decl.id]);
221
+ tracker.dominantVarValues.get(decl.id)?.add(value);
222
+ }
223
+ }
224
+ }
225
+ function buildSearchDiagnostics(tracker, startedAt, storage, transitionIndex) {
226
+ const dominantVars = [...tracker.dominantVarValues.entries()]
227
+ .map(([varId, values]) => ({ varId, distinctValues: values.size }))
228
+ .filter((entry) => entry.distinctValues > 0)
229
+ .sort((left, right) => right.distinctValues - left.distinctValues)
230
+ .slice(0, 5);
231
+ const search = {
232
+ maxFrontier: tracker.maxFrontier,
233
+ finalFrontier: tracker.finalFrontier,
234
+ expandedDepths: tracker.expandedDepths,
235
+ };
236
+ if (startedAt !== undefined) {
237
+ search.elapsedMs = Date.now() - startedAt;
238
+ }
239
+ return {
240
+ search,
241
+ storage,
242
+ hotPath: {
243
+ canonicalCache: true,
244
+ transitionIndex: true,
245
+ internalTransitionIndex: transitionIndex.internalTransitions.length > 0,
246
+ },
247
+ ...(tracker.limitHit ? { limits: tracker.limitHit } : {}),
248
+ ...(dominantVars.length > 0 ? { dominantVars } : {}),
249
+ };
250
+ }
251
+ function hitSearchLimit(tracker, options, states, edges, frontier, depth) {
252
+ if (tracker.limitHit !== null)
253
+ return true;
254
+ const limit = checkSearchLimits(options, states, edges, frontier, depth);
255
+ if (!limit)
256
+ return false;
257
+ tracker.limitHit = limit;
258
+ return true;
259
+ }
260
+ function checkSearchLimits(options, states, edges, frontier, depth) {
261
+ if (options.maxStates !== undefined && states >= options.maxStates) {
262
+ return {
263
+ reason: `search limit exceeded: maxStates=${options.maxStates}`,
264
+ maxStates: options.maxStates,
265
+ };
266
+ }
267
+ if (options.maxEdges !== undefined && edges >= options.maxEdges) {
268
+ return {
269
+ reason: `search limit exceeded: maxEdges=${options.maxEdges}`,
270
+ maxEdges: options.maxEdges,
271
+ };
272
+ }
273
+ if (options.maxFrontier !== undefined && frontier >= options.maxFrontier) {
274
+ return {
275
+ reason: `search limit exceeded: maxFrontier=${options.maxFrontier}`,
276
+ maxFrontier: options.maxFrontier,
277
+ };
278
+ }
279
+ const maxHeap = options.memoryGuard?.maxHeapUsedBytes;
280
+ if (maxHeap !== undefined && process.memoryUsage().heapUsed >= maxHeap) {
281
+ return {
282
+ reason: `search limit exceeded: memoryGuard=${maxHeap}`,
283
+ memoryGuardBytes: maxHeap,
284
+ };
285
+ }
286
+ void depth;
287
+ return null;
288
+ }
289
+ function applySearchLimitVerdicts(properties, verdicts, limit) {
290
+ for (const property of properties) {
291
+ const verdict = verdicts.get(property.name);
292
+ if (verdict &&
293
+ (verdict.status === "violated" ||
294
+ verdict.status === "reachable" ||
295
+ verdict.status === "vacuous-warning" ||
296
+ verdict.status === "error")) {
297
+ continue;
298
+ }
299
+ verdicts.set(property.name, {
300
+ status: "error",
301
+ property: property.name,
302
+ message: limit.reason,
303
+ });
304
+ }
305
+ }
306
+ function seedFrontier(model, parents, states, tracker, index, canon) {
307
+ const frontier = sortStatesByCanon(initialStates(model).flatMap((state) => stabilize(model, state, initialChangedVars(model), index, canon)), canon);
308
+ tracker.maxFrontier = Math.max(tracker.maxFrontier, frontier.length);
309
+ tracker.finalFrontier = frontier.length;
310
+ for (const state of frontier) {
311
+ const key = canon(state);
312
+ if (!parents.has(key)) {
313
+ parents.set(key, { parent: null, transitionId: null });
314
+ states.set(key, state);
81
315
  }
82
316
  }
83
317
  return frontier;
84
318
  }
85
- function exploreDepth(model, properties, frontier, parents, states, edges, verdicts, enabledTransitionIds, boundHits) {
319
+ function exploreDepth(model, properties, frontier, parents, states, graph, traceCtx, verdicts, enabledTransitionIds, boundHits, tracker, options, startingEdgeCount, depth, index, canon) {
86
320
  const next = [];
87
321
  let edgeCount = 0;
88
322
  for (const pre of frontier) {
89
- const preCanon = canonicalState(model, pre);
90
- for (const transition of enabledTransitions(model, pre)) {
323
+ if (tracker.limitHit !== null)
324
+ break;
325
+ const preCanon = canon(pre);
326
+ for (const transition of enabledTransitions(model, pre, index)) {
91
327
  enabledTransitionIds.add(transition.id);
92
328
  const rawPosts = applyEffect(model, pre, transition.effect, {
93
329
  onBoundHit: (hit) => {
@@ -100,35 +336,92 @@ function exploreDepth(model, properties, frontier, parents, states, edges, verdi
100
336
  boundHits.add(`pending cap saturated at ${transition.id}`);
101
337
  }
102
338
  for (const rawPost of rawPosts) {
103
- for (const post of stabilize(model, rawPost, changedVars(pre, rawPost))) {
339
+ for (const post of stabilize(model, rawPost, changedVars(pre, rawPost, model), index, canon)) {
104
340
  edgeCount += 1;
105
- const postCanon = canonicalState(model, post);
341
+ const postCanon = canon(post);
106
342
  const step = facts(pre, post, transition);
107
- edges.push({ preCanon, postCanon, pre, post, transition, step });
108
- observeEdge(model, properties, pre, post, transition, step, parents, verdicts);
343
+ recordExploredEdge(graph, properties, preCanon, postCanon, pre, post, transition, step);
344
+ observeEdge(model, properties, pre, post, transition, step, traceCtx, verdicts);
345
+ if (hitSearchLimit(tracker, options, parents.size, startingEdgeCount + edgeCount, next.length, depth)) {
346
+ break;
347
+ }
109
348
  if (!parents.has(postCanon)) {
110
- parents.set(postCanon, { parent: preCanon, transition, pre, post });
349
+ parents.set(postCanon, {
350
+ parent: preCanon,
351
+ transitionId: transition.id,
352
+ });
111
353
  states.set(postCanon, post);
112
354
  next.push(post);
355
+ tracker.maxFrontier = Math.max(tracker.maxFrontier, next.length);
356
+ if (hitSearchLimit(tracker, options, parents.size, startingEdgeCount + edgeCount, next.length, depth)) {
357
+ break;
358
+ }
113
359
  }
114
360
  }
361
+ if (tracker.limitHit !== null)
362
+ break;
115
363
  }
364
+ if (tracker.limitHit !== null)
365
+ break;
116
366
  }
367
+ if (tracker.limitHit !== null)
368
+ break;
117
369
  }
118
- return { next: next.sort(compareStates(model)), edges: edgeCount };
370
+ return { next: sortStatesByCanon(next, canon), edges: edgeCount };
119
371
  }
120
- function checkModelSliced(model, properties) {
372
+ function checkModelSliced(model, properties, options = {}) {
121
373
  const groups = new Map();
374
+ const sliceSummaries = [];
375
+ let sliceIndex = 0;
122
376
  for (const property of properties) {
123
- const slice = sliceModelForProperty(model, property);
377
+ const slice = canSliceProperty(property)
378
+ ? sliceModelForProperty(model, property)
379
+ : model;
124
380
  const key = slice.vars.map((decl) => decl.id).join("\0");
125
381
  const group = groups.get(key);
126
- if (group)
382
+ if (group) {
127
383
  group.properties.push(property);
128
- else
129
- groups.set(key, { model: slice, properties: [property] });
384
+ }
385
+ else {
386
+ groups.set(key, {
387
+ model: slice,
388
+ properties: [property],
389
+ index: sliceIndex,
390
+ });
391
+ sliceIndex += 1;
392
+ }
130
393
  }
131
- return combineSlicedResults(properties, [...groups.values()].map((group) => checkModelCore(group.model, group.properties)));
394
+ const results = [...groups.values()].map((group) => {
395
+ const result = checkModelCore(group.model, group.properties, {
396
+ ...options,
397
+ slicedModel: true,
398
+ });
399
+ sliceSummaries.push({
400
+ index: group.index,
401
+ properties: group.properties.map((property) => property.name),
402
+ vars: group.model.vars.length,
403
+ transitions: group.model.transitions.length,
404
+ states: result.stats.states,
405
+ edges: result.stats.edges,
406
+ depth: result.stats.depth,
407
+ });
408
+ return result;
409
+ });
410
+ sliceSummaries.sort((left, right) => left.index - right.index);
411
+ const combined = combineSlicedResults(properties, results);
412
+ return {
413
+ ...combined,
414
+ diagnostics: mergeDiagnostics(combined.diagnostics, {
415
+ slicing: {
416
+ enabled: true,
417
+ slices: sliceSummaries.length,
418
+ sliceSummaries,
419
+ },
420
+ search: combined.diagnostics?.search,
421
+ limits: combined.diagnostics?.limits,
422
+ dominantVars: combined.diagnostics?.dominantVars,
423
+ }),
424
+ };
132
425
  }
133
426
  function combineSlicedResults(properties, results) {
134
427
  const verdicts = new Map();
@@ -137,6 +430,7 @@ function combineSlicedResults(properties, results) {
137
430
  let depth = 0;
138
431
  const vacuity = new Set();
139
432
  const boundHits = new Set();
433
+ let diagnostics;
140
434
  for (const result of results) {
141
435
  for (const verdict of result.verdicts)
142
436
  verdicts.set(verdict.property, verdict);
@@ -147,6 +441,7 @@ function combineSlicedResults(properties, results) {
147
441
  states += result.stats.states;
148
442
  edges += result.stats.edges;
149
443
  depth = Math.max(depth, result.stats.depth);
444
+ diagnostics = mergeSearchDiagnostics(diagnostics, result.diagnostics);
150
445
  }
151
446
  return {
152
447
  verdicts: properties.map((property) => verdicts.get(property.name) ?? {
@@ -157,6 +452,76 @@ function combineSlicedResults(properties, results) {
157
452
  stats: { states, edges, depth },
158
453
  vacuityWarnings: [...vacuity].sort(),
159
454
  boundHits: [...boundHits].sort(),
455
+ diagnostics,
456
+ };
457
+ }
458
+ function mergeDiagnostics(base, overlay) {
459
+ if (!base && !overlay)
460
+ return undefined;
461
+ return {
462
+ ...base,
463
+ ...overlay,
464
+ slicing: base?.slicing || overlay?.slicing
465
+ ? {
466
+ enabled: overlay?.slicing?.enabled ?? base?.slicing?.enabled ?? false,
467
+ slices: overlay?.slicing?.slices ?? base?.slicing?.slices,
468
+ skipped: overlay?.slicing?.skipped ?? base?.slicing?.skipped,
469
+ skipReason: overlay?.slicing?.skipReason ?? base?.slicing?.skipReason,
470
+ sliceSummaries: overlay?.slicing?.sliceSummaries ?? base?.slicing?.sliceSummaries,
471
+ }
472
+ : undefined,
473
+ search: overlay?.search ?? base?.search,
474
+ limits: overlay?.limits ?? base?.limits,
475
+ dominantVars: overlay?.dominantVars ?? base?.dominantVars,
476
+ storage: overlay?.storage ?? base?.storage,
477
+ hotPath: overlay?.hotPath ?? base?.hotPath,
160
478
  };
161
479
  }
480
+ function mergeSearchDiagnostics(left, right) {
481
+ if (!left)
482
+ return right;
483
+ if (!right)
484
+ return left;
485
+ const dominant = mergeDominantVars(left.dominantVars, right.dominantVars);
486
+ return {
487
+ slicing: left.slicing ?? right.slicing,
488
+ search: {
489
+ maxFrontier: Math.max(left.search?.maxFrontier ?? 0, right.search?.maxFrontier ?? 0),
490
+ finalFrontier: Math.max(left.search?.finalFrontier ?? 0, right.search?.finalFrontier ?? 0),
491
+ expandedDepths: Math.max(left.search?.expandedDepths ?? 0, right.search?.expandedDepths ?? 0),
492
+ elapsedMs: left.search?.elapsedMs !== undefined ||
493
+ right.search?.elapsedMs !== undefined
494
+ ? (left.search?.elapsedMs ?? 0) + (right.search?.elapsedMs ?? 0)
495
+ : undefined,
496
+ },
497
+ limits: left.limits ?? right.limits,
498
+ dominantVars: dominant,
499
+ storage: mergeStorageDiagnostics(left.storage, right.storage),
500
+ hotPath: left.hotPath ?? right.hotPath,
501
+ };
502
+ }
503
+ function mergeStorageDiagnostics(left, right) {
504
+ if (!left)
505
+ return right;
506
+ if (!right)
507
+ return left;
508
+ return {
509
+ recordedEdges: left.recordedEdges + right.recordedEdges,
510
+ storedStates: left.storedStates + right.storedStates,
511
+ parentEntries: left.parentEntries + right.parentEntries,
512
+ edgeRecordingMode: left.edgeRecordingMode === right.edgeRecordingMode
513
+ ? left.edgeRecordingMode
514
+ : "property-specific",
515
+ };
516
+ }
517
+ function mergeDominantVars(left, right) {
518
+ const counts = new Map();
519
+ for (const entry of [...(left ?? []), ...(right ?? [])]) {
520
+ counts.set(entry.varId, Math.max(counts.get(entry.varId) ?? 0, entry.distinctValues));
521
+ }
522
+ return [...counts.entries()]
523
+ .map(([varId, distinctValues]) => ({ varId, distinctValues }))
524
+ .sort((a, b) => b.distinctValues - a.distinctValues)
525
+ .slice(0, 5);
526
+ }
162
527
  //# sourceMappingURL=check-model.js.map