@kirrosh/zond 0.22.0 → 0.23.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 (256) hide show
  1. package/CHANGELOG.md +648 -0
  2. package/README.md +58 -6
  3. package/package.json +9 -6
  4. package/src/cli/argv.ts +122 -0
  5. package/src/cli/commands/add-api.ts +134 -0
  6. package/src/cli/commands/api/annotate/idempotency.ts +59 -0
  7. package/src/cli/commands/api/annotate/index.ts +525 -0
  8. package/src/cli/commands/api/annotate/lifecycle.ts +74 -0
  9. package/src/cli/commands/api/annotate/overlay.ts +206 -0
  10. package/src/cli/commands/api/annotate/pagination.ts +60 -0
  11. package/src/cli/commands/api/annotate/prompts.ts +183 -0
  12. package/src/cli/commands/api/annotate/readback.ts +58 -0
  13. package/src/cli/commands/api/annotate/resources.ts +91 -0
  14. package/src/cli/commands/api/annotate/seed-bodies.ts +61 -0
  15. package/src/cli/commands/audit.ts +480 -0
  16. package/src/cli/commands/bootstrap.ts +710 -0
  17. package/src/cli/commands/catalog.ts +35 -0
  18. package/src/cli/commands/check.ts +348 -0
  19. package/src/cli/commands/checks.ts +756 -0
  20. package/src/cli/commands/ci-init.ts +43 -0
  21. package/src/cli/commands/clean.ts +212 -0
  22. package/src/cli/commands/cleanup.ts +262 -0
  23. package/src/cli/commands/completions.ts +16 -0
  24. package/src/cli/commands/coverage.ts +605 -132
  25. package/src/cli/commands/db.ts +178 -7
  26. package/src/cli/commands/describe.ts +37 -2
  27. package/src/cli/commands/discover.ts +1236 -0
  28. package/src/cli/commands/doctor.ts +607 -0
  29. package/src/cli/commands/fixtures.ts +402 -0
  30. package/src/cli/commands/generate.ts +420 -46
  31. package/src/cli/commands/init/bootstrap.ts +30 -1
  32. package/src/cli/commands/{init.ts → init/index.ts} +99 -5
  33. package/src/cli/commands/init/skills.ts +56 -3
  34. package/src/cli/commands/init/templates/agents.md +65 -61
  35. package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
  36. package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
  37. package/src/cli/commands/init/templates/skills/zond.md +592 -125
  38. package/src/cli/commands/init/templates/zond-config.yml +8 -9
  39. package/src/cli/commands/prepare-fixtures.ts +135 -0
  40. package/src/cli/commands/probe/mass-assignment.ts +503 -0
  41. package/src/cli/commands/probe/security.ts +454 -0
  42. package/src/cli/commands/probe/static.ts +255 -0
  43. package/src/cli/commands/probe/webhooks.ts +161 -0
  44. package/src/cli/commands/probe.ts +459 -0
  45. package/src/cli/commands/reference.ts +87 -0
  46. package/src/cli/commands/refresh-api.ts +169 -0
  47. package/src/cli/commands/remove-api.ts +150 -0
  48. package/src/cli/commands/report-bundle.ts +318 -0
  49. package/src/cli/commands/report.ts +241 -0
  50. package/src/cli/commands/request.ts +379 -4
  51. package/src/cli/commands/run.ts +842 -53
  52. package/src/cli/commands/session.ts +244 -0
  53. package/src/cli/commands/use.ts +18 -1
  54. package/src/cli/index.ts +20 -3
  55. package/src/cli/json-envelope.ts +112 -3
  56. package/src/cli/json-schemas.ts +263 -0
  57. package/src/cli/program.ts +198 -635
  58. package/src/cli/resolve.ts +105 -0
  59. package/src/cli/status-filter.ts +124 -0
  60. package/src/cli/util/api-context.ts +85 -0
  61. package/src/cli/version.ts +5 -0
  62. package/src/core/anti-fp/bootstrap.ts +34 -0
  63. package/src/core/anti-fp/index.ts +33 -0
  64. package/src/core/anti-fp/registry.ts +44 -0
  65. package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
  66. package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
  67. package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
  68. package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
  69. package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
  70. package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
  71. package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
  72. package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
  73. package/src/core/anti-fp/types.ts +68 -0
  74. package/src/core/checks/checks/_crud-helpers.ts +133 -0
  75. package/src/core/checks/checks/_negative_mutator.ts +133 -0
  76. package/src/core/checks/checks/_readback-helpers.ts +133 -0
  77. package/src/core/checks/checks/content_type_conformance.ts +39 -0
  78. package/src/core/checks/checks/cross_call_references.ts +134 -0
  79. package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
  80. package/src/core/checks/checks/idempotency_replay.ts +246 -0
  81. package/src/core/checks/checks/ignored_auth.ts +211 -0
  82. package/src/core/checks/checks/index.ts +65 -0
  83. package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
  84. package/src/core/checks/checks/missing_required_header.ts +40 -0
  85. package/src/core/checks/checks/negative_data_rejection.ts +45 -0
  86. package/src/core/checks/checks/not_a_server_error.ts +27 -0
  87. package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
  88. package/src/core/checks/checks/pagination_invariants.ts +238 -0
  89. package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
  90. package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
  91. package/src/core/checks/checks/response_headers_conformance.ts +74 -0
  92. package/src/core/checks/checks/response_schema_conformance.ts +30 -0
  93. package/src/core/checks/checks/status_code_conformance.ts +61 -0
  94. package/src/core/checks/checks/unsupported_method.ts +63 -0
  95. package/src/core/checks/checks/use_after_free.ts +78 -0
  96. package/src/core/checks/index.ts +30 -0
  97. package/src/core/checks/mode.ts +79 -0
  98. package/src/core/checks/recommended-action.ts +64 -0
  99. package/src/core/checks/registry.ts +78 -0
  100. package/src/core/checks/runner.ts +874 -0
  101. package/src/core/checks/sarif.ts +230 -0
  102. package/src/core/checks/stateful.ts +121 -0
  103. package/src/core/checks/types.ts +189 -0
  104. package/src/core/classifier/recommended-action.ts +222 -0
  105. package/src/core/context/current.ts +22 -6
  106. package/src/core/context/session.ts +78 -0
  107. package/src/core/coverage/loader.ts +185 -0
  108. package/src/core/coverage/reasons.ts +300 -0
  109. package/src/core/diagnostics/db-analysis.ts +151 -11
  110. package/src/core/diagnostics/failure-class.ts +120 -0
  111. package/src/core/diagnostics/failure-hints.ts +212 -9
  112. package/src/core/diagnostics/spec-pointer.ts +99 -0
  113. package/src/core/diagnostics/suggested-fixes.ts +156 -0
  114. package/src/core/exporter/case-study/index.ts +270 -0
  115. package/src/core/exporter/curl.ts +40 -0
  116. package/src/core/exporter/exporter.ts +48 -0
  117. package/src/core/exporter/html-report/escape.ts +24 -0
  118. package/src/core/exporter/html-report/index.ts +479 -0
  119. package/src/core/exporter/html-report/script.ts +100 -0
  120. package/src/core/exporter/html-report/styles.ts +408 -0
  121. package/src/core/generator/chunker.ts +42 -16
  122. package/src/core/generator/coverage-phase.ts +0 -0
  123. package/src/core/generator/create-body.ts +89 -0
  124. package/src/core/generator/data-factory.ts +445 -19
  125. package/src/core/generator/describe.ts +1 -1
  126. package/src/core/generator/fixtures-builder.ts +325 -0
  127. package/src/core/generator/index.ts +7 -5
  128. package/src/core/generator/openapi-reader.ts +37 -3
  129. package/src/core/generator/path-param-disambig.ts +114 -0
  130. package/src/core/generator/resources-builder.ts +648 -0
  131. package/src/core/generator/schema-utils.ts +11 -3
  132. package/src/core/generator/serializer.ts +103 -13
  133. package/src/core/generator/suite-generator.ts +419 -111
  134. package/src/core/generator/types.ts +8 -0
  135. package/src/core/identity/identity-file.ts +129 -0
  136. package/src/core/lint/affects.ts +28 -0
  137. package/src/core/lint/config.ts +96 -0
  138. package/src/core/lint/format.ts +42 -0
  139. package/src/core/lint/index.ts +94 -0
  140. package/src/core/lint/reporter.ts +128 -0
  141. package/src/core/lint/rules/consistency.ts +158 -0
  142. package/src/core/lint/rules/heuristics.ts +97 -0
  143. package/src/core/lint/rules/strictness.ts +109 -0
  144. package/src/core/lint/types.ts +96 -0
  145. package/src/core/lint/walker.ts +248 -0
  146. package/src/core/meta/meta-store.ts +6 -73
  147. package/src/core/output/README.md +91 -0
  148. package/src/core/output/index.ts +13 -0
  149. package/src/core/output/run.ts +126 -0
  150. package/src/core/output/types.ts +129 -0
  151. package/src/core/parser/env-interpolation.ts +104 -0
  152. package/src/core/parser/filter.ts +57 -0
  153. package/src/core/parser/schema.ts +129 -4
  154. package/src/core/parser/types.ts +19 -1
  155. package/src/core/parser/variables.ts +0 -0
  156. package/src/core/parser/yaml-parser.ts +58 -12
  157. package/src/core/probe/bootstrap.ts +34 -0
  158. package/src/core/probe/dry-run-envelope.ts +57 -0
  159. package/src/core/probe/mass-assignment-probe-class.ts +198 -0
  160. package/src/core/probe/mass-assignment-probe.ts +1122 -0
  161. package/src/core/probe/mass-assignment-template.ts +212 -0
  162. package/src/core/probe/method-probe.ts +43 -76
  163. package/src/core/probe/method-shared.ts +69 -0
  164. package/src/core/probe/negative-probe.ts +183 -149
  165. package/src/core/probe/orphan-tracker.ts +188 -0
  166. package/src/core/probe/path-discovery.ts +440 -0
  167. package/src/core/probe/probe-harness.ts +120 -0
  168. package/src/core/probe/registry.ts +89 -0
  169. package/src/core/probe/runner.ts +136 -0
  170. package/src/core/probe/security-probe-class.ts +201 -0
  171. package/src/core/probe/security-probe.ts +1453 -0
  172. package/src/core/probe/shared.ts +505 -0
  173. package/src/core/probe/static-probe-class.ts +125 -0
  174. package/src/core/probe/types.ts +165 -0
  175. package/src/core/probe/verdict-aggregator.ts +33 -0
  176. package/src/core/probe/webhooks-probe.ts +284 -0
  177. package/src/core/reporter/console.ts +41 -2
  178. package/src/core/reporter/index.ts +2 -3
  179. package/src/core/reporter/json.ts +11 -1
  180. package/src/core/reporter/junit.ts +27 -12
  181. package/src/core/reporter/ndjson.ts +37 -0
  182. package/src/core/reporter/types.ts +3 -0
  183. package/src/core/runner/assertions.ts +58 -1
  184. package/src/core/runner/async-pool.ts +108 -0
  185. package/src/core/runner/auth-path.ts +8 -0
  186. package/src/core/runner/ci-context.ts +72 -0
  187. package/src/core/runner/executor.ts +264 -20
  188. package/src/core/runner/form-encode.ts +51 -0
  189. package/src/core/runner/http-client.ts +75 -2
  190. package/src/core/runner/learn-drift.ts +293 -0
  191. package/src/core/runner/preflight-vars.ts +149 -0
  192. package/src/core/runner/progress-tracker.ts +73 -0
  193. package/src/core/runner/rate-limiter.ts +89 -17
  194. package/src/core/runner/run-kind.ts +39 -0
  195. package/src/core/runner/schema-validator.ts +312 -0
  196. package/src/core/runner/send-request.ts +153 -20
  197. package/src/core/runner/types.ts +38 -0
  198. package/src/core/secrets/registry.ts +164 -0
  199. package/src/core/secrets/secrets-file.ts +115 -0
  200. package/src/core/selectors/operation-filter.ts +144 -0
  201. package/src/core/setup-api.ts +415 -16
  202. package/src/core/severity/category.ts +94 -0
  203. package/src/core/severity/index.ts +121 -0
  204. package/src/core/spec/layers.ts +154 -0
  205. package/src/core/util/format-eta.ts +21 -0
  206. package/src/core/utils.ts +5 -1
  207. package/src/core/workspace/config.ts +129 -0
  208. package/src/core/workspace/manifest.ts +283 -0
  209. package/src/core/workspace/output-rotation.ts +62 -0
  210. package/src/core/workspace/triage-path.ts +87 -0
  211. package/src/db/lint-runs.ts +47 -0
  212. package/src/db/migrate.ts +126 -0
  213. package/src/db/migrations/0001_run_kind.sql +25 -0
  214. package/src/db/migrations/sql.d.ts +4 -0
  215. package/src/db/queries/collections.ts +133 -0
  216. package/src/db/queries/coverage.ts +9 -0
  217. package/src/db/queries/dashboard.ts +59 -0
  218. package/src/db/queries/results.ts +128 -0
  219. package/src/db/queries/runs.ts +235 -0
  220. package/src/db/queries/sessions.ts +42 -0
  221. package/src/db/queries/settings.ts +28 -0
  222. package/src/db/queries/types.ts +172 -0
  223. package/src/db/queries.ts +72 -802
  224. package/src/db/schema.ts +178 -50
  225. package/src/cli/commands/export.ts +0 -144
  226. package/src/cli/commands/guide.ts +0 -127
  227. package/src/cli/commands/init/templates/skills/scenarios.md +0 -97
  228. package/src/cli/commands/probe-methods.ts +0 -108
  229. package/src/cli/commands/probe-validation.ts +0 -124
  230. package/src/cli/commands/serve.ts +0 -114
  231. package/src/cli/commands/sync.ts +0 -268
  232. package/src/cli/commands/update.ts +0 -189
  233. package/src/cli/commands/validate.ts +0 -34
  234. package/src/core/diagnostics/render-md.ts +0 -112
  235. package/src/core/exporter/postman.ts +0 -963
  236. package/src/core/generator/guide-builder.ts +0 -253
  237. package/src/core/meta/types.ts +0 -19
  238. package/src/core/parser/index.ts +0 -21
  239. package/src/core/runner/execute-run.ts +0 -132
  240. package/src/core/runner/index.ts +0 -12
  241. package/src/core/sync/spec-differ.ts +0 -38
  242. package/src/web/data/collection-state.ts +0 -362
  243. package/src/web/routes/api.ts +0 -314
  244. package/src/web/routes/dashboard.ts +0 -350
  245. package/src/web/routes/runs.ts +0 -64
  246. package/src/web/schemas.ts +0 -121
  247. package/src/web/server.ts +0 -134
  248. package/src/web/static/htmx.min.cjs +0 -1
  249. package/src/web/static/style.css +0 -1148
  250. package/src/web/views/endpoints-tab.ts +0 -174
  251. package/src/web/views/explorer-tab.ts +0 -402
  252. package/src/web/views/health-strip.ts +0 -92
  253. package/src/web/views/layout.ts +0 -48
  254. package/src/web/views/results.ts +0 -210
  255. package/src/web/views/runs-tab.ts +0 -126
  256. package/src/web/views/suites-tab.ts +0 -181
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Public entry point for the `core/checks` framework.
3
+ *
4
+ * Importing this module also triggers `checks/index.ts` which registers
5
+ * the built-in checks exactly once.
6
+ */
7
+ export type {
8
+ Check,
9
+ CheckCase,
10
+ CheckContext,
11
+ CheckFinding,
12
+ CheckOutcome,
13
+ CheckResponse,
14
+ CheckRunData,
15
+ CheckRunSummary,
16
+ Phase,
17
+ Severity,
18
+ } from "./types.ts";
19
+ export { emptySummary } from "./types.ts";
20
+ export {
21
+ __resetRegistryForTests,
22
+ getCheck,
23
+ listChecks,
24
+ registerCheck,
25
+ selectChecks,
26
+ type SelectOptions,
27
+ type SelectionResult,
28
+ } from "./registry.ts";
29
+ export { runChecks, type RunChecksOptions, type RunChecksResult } from "./runner.ts";
30
+ export { registerBuiltinChecks } from "./checks/index.ts";
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `--mode positive|negative|all` filter (m-15 ARV-7).
3
+ *
4
+ * Splits the registered check catalog along the positive-vs-negative
5
+ * axis so an agent can run *just* contract verification (positive) or
6
+ * *just* malicious-input probes (negative) on the same spec without
7
+ * juggling a long `--check` list.
8
+ *
9
+ * Centralized table — both response-phase `Check`s and stateful
10
+ * security checks declare their mode here. Tests snapshot the table so
11
+ * adding a new check forces an explicit decision rather than letting it
12
+ * silently land in the "all" bucket and surface in both modes.
13
+ */
14
+ import type { Check } from "./types.ts";
15
+ import type { StatefulCheck } from "./stateful.ts";
16
+
17
+ export type Mode = "positive" | "negative" | "all";
18
+
19
+ export const MODE_BY_CHECK: Record<string, Mode> = {
20
+ // Conformance checks fire on every response — both modes.
21
+ not_a_server_error: "all",
22
+ status_code_conformance: "all",
23
+ content_type_conformance: "all",
24
+ response_headers_conformance: "all",
25
+ response_schema_conformance: "all",
26
+ // Probes that *only* make sense as malicious input.
27
+ missing_required_header: "negative",
28
+ unsupported_method: "negative",
29
+ negative_data_rejection: "negative",
30
+ // The "happy-path didn't break" sanity probe.
31
+ positive_data_acceptance: "positive",
32
+ // Security checks (stateful) — they verify a *bad* outcome (auth
33
+ // bypass, dangling reads). Not part of the positive contract.
34
+ ignored_auth: "negative",
35
+ use_after_free: "negative",
36
+ // Availability is positive-flavored — it asserts the server *can*
37
+ // serve the listed resource. Useful in `--mode positive` runs.
38
+ ensure_resource_availability: "all",
39
+ // ARV-169 (m-20): cross-call drift is a contract-verification check
40
+ // (does GET reflect POST?), not a malicious-input probe. Positive.
41
+ cross_call_references: "positive",
42
+ // ARV-170 (m-20): idempotency replay verifies a *contract* the server
43
+ // advertises (Idempotency-Key honored). Positive.
44
+ idempotency_replay: "positive",
45
+ // ARV-171 (m-20): pagination invariants verify the cursor contract.
46
+ pagination_invariants: "positive",
47
+ // ARV-172 (m-20): lifecycle verifies the declared state machine.
48
+ lifecycle_transitions: "positive",
49
+ // ARV-256 (m-21): open-CORS check sends an attacker Origin and
50
+ // inspects response — categorically negative-mode probe.
51
+ open_cors_on_sensitive: "negative",
52
+ // ARV-256 (m-21): rate-limit headers check inspects 2xx responses
53
+ // for advertised rate-limit metadata — runs on the positive path.
54
+ rate_limit_headers_absent: "positive",
55
+ };
56
+
57
+ export function modeFor(checkId: string): Mode {
58
+ return MODE_BY_CHECK[checkId] ?? "all";
59
+ }
60
+
61
+ export function filterChecksByMode<T extends Check | StatefulCheck>(
62
+ checks: T[],
63
+ mode: Mode,
64
+ ): T[] {
65
+ if (mode === "all") return checks;
66
+ return checks.filter((c) => {
67
+ const cm = modeFor(c.id);
68
+ if (cm === "all") return true;
69
+ return cm === mode;
70
+ });
71
+ }
72
+
73
+ /** True if a built case (with `mode: "positive" | "negative"`) should be
74
+ * sent in the requested run-mode. The runner uses this to short-circuit
75
+ * request emission for cases the active mode doesn't care about. */
76
+ export function caseMatchesMode(caseMode: "positive" | "negative", runMode: Mode): boolean {
77
+ if (runMode === "all") return true;
78
+ return runMode === caseMode;
79
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * ARV-11 (m-15): per-check `recommended_action` table.
3
+ *
4
+ * Each `CheckFinding` carries one closed-enum action so an agent can
5
+ * route on it without parsing the free-form `message`. The mapping is
6
+ * deterministic — given the check id and (for a couple of fan-out cases)
7
+ * the response status, exactly one action comes out.
8
+ *
9
+ * Keep this file thin and table-driven. The shared enum lives in
10
+ * `core/diagnostics/failure-hints.ts` so `db diagnose` (TASK-294) and
11
+ * `zond checks run` (ARV-11) can never drift.
12
+ */
13
+ import type { RecommendedAction } from "../diagnostics/failure-hints.ts";
14
+ import { classify, type FindingClass } from "../classifier/recommended-action.ts";
15
+
16
+ /** Check IDs the classifier explicitly recognises. Keeping this typed
17
+ * guarantees that adding a new check forces a branch in the classifier
18
+ * switch (tsc errors out at compile time on a stale `STATIC_TABLE` lookup). */
19
+ const CHECK_ID_TO_CLASS: Record<string, FindingClass> = {
20
+ status_code_conformance: "check:status_code_conformance",
21
+ content_type_conformance: "check:content_type_conformance",
22
+ response_headers_conformance: "check:response_headers_conformance",
23
+ response_schema_conformance: "check:response_schema_conformance",
24
+ not_a_server_error: "check:not_a_server_error",
25
+ unsupported_method: "check:unsupported_method",
26
+ positive_data_acceptance: "check:positive_data_acceptance",
27
+ use_after_free: "check:use_after_free",
28
+ ensure_resource_availability: "check:ensure_resource_availability",
29
+ negative_data_rejection: "check:negative_data_rejection",
30
+ missing_required_header: "check:missing_required_header",
31
+ ignored_auth: "check:ignored_auth",
32
+ cross_call_references: "check:cross_call_references",
33
+ idempotency_replay: "check:idempotency_replay",
34
+ pagination_invariants: "check:pagination_invariants",
35
+ lifecycle_transitions: "check:lifecycle_transitions",
36
+ open_cors_on_sensitive: "check:open_cors_on_sensitive",
37
+ rate_limit_headers_absent: "check:rate_limit_headers_absent",
38
+ network_error: "check:network_error",
39
+ };
40
+
41
+ /**
42
+ * ARV-56: thin wrapper that maps a check id + status to the unified
43
+ * classifier. Returns `undefined` when the check id isn't in the table
44
+ * — unknown ids are a bug in this map, not a runtime input to coerce.
45
+ */
46
+ export function recommendForCheck(
47
+ checkId: string,
48
+ status?: number,
49
+ ): RecommendedAction | undefined {
50
+ const findingClass = CHECK_ID_TO_CLASS[checkId];
51
+ if (!findingClass) return undefined;
52
+ return classify({ finding_class: findingClass, status: status ?? null });
53
+ }
54
+
55
+ /** Test-only export — keeps the unit table authoritative without
56
+ * re-listing entries inside the test file. Derived from classifier
57
+ * output so the table never drifts from the classifier's switch. */
58
+ export const RECOMMENDED_ACTION_TABLE: Readonly<Record<string, RecommendedAction>> = Object.freeze(
59
+ Object.fromEntries(
60
+ Object.entries(CHECK_ID_TO_CLASS)
61
+ .filter(([id]) => id !== "network_error") // dynamic — depends on status
62
+ .map(([id, fc]) => [id, classify({ finding_class: fc })!]),
63
+ ),
64
+ );
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Global registry of `zond checks`. Built-in checks register themselves
3
+ * on first import (see `checks/index.ts`); `selectChecks` resolves the
4
+ * `--check` / `--exclude-check` filters from the CLI into the active
5
+ * subset for a single run.
6
+ */
7
+ import type { Check } from "./types.ts";
8
+
9
+ const REGISTRY = new Map<string, Check>();
10
+
11
+ export function registerCheck(check: Check): void {
12
+ if (REGISTRY.has(check.id)) {
13
+ throw new Error(`Check "${check.id}" is already registered`);
14
+ }
15
+ REGISTRY.set(check.id, check);
16
+ }
17
+
18
+ export function getCheck(id: string): Check | undefined {
19
+ return REGISTRY.get(id);
20
+ }
21
+
22
+ export function listChecks(): Check[] {
23
+ return [...REGISTRY.values()].sort((a, b) => a.id.localeCompare(b.id));
24
+ }
25
+
26
+ export interface SelectOptions {
27
+ include?: string[];
28
+ exclude?: string[];
29
+ }
30
+
31
+ export interface SelectionResult {
32
+ selected: Check[];
33
+ unknown: string[];
34
+ }
35
+
36
+ /**
37
+ * Resolve include/exclude id lists into the active set of checks.
38
+ * Unknown ids are returned separately so the caller can surface them as
39
+ * envelope warnings (and decide whether to fail or continue).
40
+ */
41
+ export function selectChecks(opts: SelectOptions = {}): SelectionResult {
42
+ const all = listChecks();
43
+ const knownIds = new Set(all.map(c => c.id));
44
+ const unknown: string[] = [];
45
+
46
+ for (const id of [...(opts.include ?? []), ...(opts.exclude ?? [])]) {
47
+ if (!knownIds.has(id)) unknown.push(id);
48
+ }
49
+
50
+ let pool = all;
51
+ if (opts.include && opts.include.length > 0) {
52
+ const includeSet = new Set(opts.include);
53
+ pool = pool.filter(c => includeSet.has(c.id));
54
+ }
55
+ if (opts.exclude && opts.exclude.length > 0) {
56
+ const excludeSet = new Set(opts.exclude);
57
+ pool = pool.filter(c => !excludeSet.has(c.id));
58
+ }
59
+ return { selected: pool, unknown };
60
+ }
61
+
62
+ /** Test-only escape hatch — wipes the registry between unit tests. */
63
+ export function __resetRegistryForTests(): void {
64
+ REGISTRY.clear();
65
+ }
66
+
67
+ /** Test-only snapshot/restore. Use when a test wants a clean slate but the
68
+ * rest of the suite still needs the side-effect-registered built-ins
69
+ * (otherwise wiping the registry leaks across files because the modules
70
+ * that registered them are already cached and won't re-fire on re-import).
71
+ */
72
+ export function __snapshotRegistryForTests(): () => void {
73
+ const saved = new Map(REGISTRY);
74
+ return () => {
75
+ REGISTRY.clear();
76
+ for (const [k, v] of saved) REGISTRY.set(k, v);
77
+ };
78
+ }