@kirrosh/zond 0.26.1 → 0.27.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 (261) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +22 -21
  3. package/bin/zond.mjs +23 -0
  4. package/package.json +13 -16
  5. package/scripts/npm/postinstall.mjs +76 -0
  6. package/src/CLAUDE.md +0 -112
  7. package/src/bun-types.d.ts +0 -5
  8. package/src/cli/argv.ts +0 -122
  9. package/src/cli/commands/add-api.ts +0 -146
  10. package/src/cli/commands/api/annotate/idempotency.ts +0 -59
  11. package/src/cli/commands/api/annotate/index.ts +0 -880
  12. package/src/cli/commands/api/annotate/lifecycle.ts +0 -74
  13. package/src/cli/commands/api/annotate/overlay.ts +0 -206
  14. package/src/cli/commands/api/annotate/pagination.ts +0 -64
  15. package/src/cli/commands/api/annotate/prompts.ts +0 -220
  16. package/src/cli/commands/api/annotate/readback.ts +0 -58
  17. package/src/cli/commands/api/annotate/resources.ts +0 -91
  18. package/src/cli/commands/api/annotate/seed-bodies.ts +0 -61
  19. package/src/cli/commands/audit.ts +0 -786
  20. package/src/cli/commands/catalog.ts +0 -97
  21. package/src/cli/commands/check.ts +0 -361
  22. package/src/cli/commands/checks.ts +0 -1072
  23. package/src/cli/commands/ci-init.ts +0 -223
  24. package/src/cli/commands/clean.ts +0 -212
  25. package/src/cli/commands/cleanup.ts +0 -236
  26. package/src/cli/commands/completions.ts +0 -192
  27. package/src/cli/commands/coverage.ts +0 -872
  28. package/src/cli/commands/db.ts +0 -611
  29. package/src/cli/commands/describe.ts +0 -120
  30. package/src/cli/commands/discover.ts +0 -1356
  31. package/src/cli/commands/doctor.ts +0 -661
  32. package/src/cli/commands/fixtures.ts +0 -402
  33. package/src/cli/commands/generate.ts +0 -577
  34. package/src/cli/commands/init/agents-md.ts +0 -61
  35. package/src/cli/commands/init/bootstrap.ts +0 -111
  36. package/src/cli/commands/init/index.ts +0 -244
  37. package/src/cli/commands/init/skills.ts +0 -141
  38. package/src/cli/commands/init/templates/agents.md +0 -86
  39. package/src/cli/commands/init/templates/markdown.d.ts +0 -4
  40. package/src/cli/commands/init/templates/skills/warm-up-target.md +0 -122
  41. package/src/cli/commands/init/templates/skills/zond-checks.md +0 -621
  42. package/src/cli/commands/init/templates/skills/zond-seed.md +0 -114
  43. package/src/cli/commands/init/templates/skills/zond-triage.md +0 -272
  44. package/src/cli/commands/init/templates/skills/zond.md +0 -861
  45. package/src/cli/commands/init/templates/zond-config.yml +0 -14
  46. package/src/cli/commands/prepare-fixtures.ts +0 -97
  47. package/src/cli/commands/probe/_seed-bodies.ts +0 -52
  48. package/src/cli/commands/probe/mass-assignment.ts +0 -594
  49. package/src/cli/commands/probe/security.ts +0 -537
  50. package/src/cli/commands/probe/static.ts +0 -255
  51. package/src/cli/commands/probe/webhooks.ts +0 -163
  52. package/src/cli/commands/probe.ts +0 -535
  53. package/src/cli/commands/reference.ts +0 -87
  54. package/src/cli/commands/refresh-api.ts +0 -227
  55. package/src/cli/commands/remove-api.ts +0 -150
  56. package/src/cli/commands/report-bundle.ts +0 -310
  57. package/src/cli/commands/report.ts +0 -241
  58. package/src/cli/commands/request.ts +0 -548
  59. package/src/cli/commands/run.ts +0 -1162
  60. package/src/cli/commands/schema-from-runs.ts +0 -128
  61. package/src/cli/commands/secrets.ts +0 -133
  62. package/src/cli/commands/session.ts +0 -244
  63. package/src/cli/commands/use.ts +0 -74
  64. package/src/cli/index.ts +0 -55
  65. package/src/cli/json-envelope.ts +0 -108
  66. package/src/cli/json-schemas.ts +0 -314
  67. package/src/cli/output.ts +0 -40
  68. package/src/cli/program.ts +0 -219
  69. package/src/cli/resolve.ts +0 -105
  70. package/src/cli/runtime.ts +0 -7
  71. package/src/cli/safe-live.ts +0 -24
  72. package/src/cli/status-filter.ts +0 -114
  73. package/src/cli/util/api-context.ts +0 -85
  74. package/src/cli/version.ts +0 -8
  75. package/src/core/audit/persist.ts +0 -183
  76. package/src/core/checks/budget.ts +0 -59
  77. package/src/core/checks/checks/_crud-helpers.ts +0 -133
  78. package/src/core/checks/checks/_negative_mutator.ts +0 -133
  79. package/src/core/checks/checks/_readback-helpers.ts +0 -133
  80. package/src/core/checks/checks/content_type_conformance.ts +0 -39
  81. package/src/core/checks/checks/cross_call_references.ts +0 -147
  82. package/src/core/checks/checks/cursor_boundary_fuzzing.ts +0 -219
  83. package/src/core/checks/checks/ensure_resource_availability.ts +0 -62
  84. package/src/core/checks/checks/idempotency_replay.ts +0 -242
  85. package/src/core/checks/checks/ignored_auth.ts +0 -254
  86. package/src/core/checks/checks/index.ts +0 -68
  87. package/src/core/checks/checks/lifecycle_transitions.ts +0 -416
  88. package/src/core/checks/checks/missing_required_header.ts +0 -40
  89. package/src/core/checks/checks/negative_data_rejection.ts +0 -148
  90. package/src/core/checks/checks/not_a_server_error.ts +0 -35
  91. package/src/core/checks/checks/open_cors_on_sensitive.ts +0 -160
  92. package/src/core/checks/checks/pagination_invariants.ts +0 -419
  93. package/src/core/checks/checks/positive_data_acceptance.ts +0 -33
  94. package/src/core/checks/checks/rate_limit_headers_absent.ts +0 -77
  95. package/src/core/checks/checks/response_headers_conformance.ts +0 -74
  96. package/src/core/checks/checks/response_schema_conformance.ts +0 -30
  97. package/src/core/checks/checks/status_code_conformance.ts +0 -132
  98. package/src/core/checks/checks/unsupported_method.ts +0 -63
  99. package/src/core/checks/checks/use_after_free.ts +0 -78
  100. package/src/core/checks/index.ts +0 -30
  101. package/src/core/checks/mode.ts +0 -82
  102. package/src/core/checks/recommended-action.ts +0 -68
  103. package/src/core/checks/registry.ts +0 -78
  104. package/src/core/checks/runner.ts +0 -1461
  105. package/src/core/checks/sarif.ts +0 -230
  106. package/src/core/checks/spec-findings.ts +0 -308
  107. package/src/core/checks/stateful.ts +0 -121
  108. package/src/core/checks/types.ts +0 -305
  109. package/src/core/checks/zond-extensions.ts +0 -73
  110. package/src/core/classifier/recommended-action.ts +0 -251
  111. package/src/core/context/current.ts +0 -51
  112. package/src/core/context/session.ts +0 -78
  113. package/src/core/coverage/loader.ts +0 -216
  114. package/src/core/coverage/reasons.ts +0 -300
  115. package/src/core/diagnostics/db-analysis.ts +0 -666
  116. package/src/core/diagnostics/failure-class.ts +0 -140
  117. package/src/core/diagnostics/failure-hints.ts +0 -112
  118. package/src/core/diagnostics/spec-pointer.ts +0 -99
  119. package/src/core/diagnostics/suggested-fixes.ts +0 -155
  120. package/src/core/exporter/case-study/index.ts +0 -270
  121. package/src/core/exporter/curl.ts +0 -40
  122. package/src/core/exporter/exporter.ts +0 -48
  123. package/src/core/exporter/html-report/escape.ts +0 -24
  124. package/src/core/exporter/html-report/index.ts +0 -479
  125. package/src/core/exporter/html-report/script.ts +0 -100
  126. package/src/core/exporter/html-report/styles.ts +0 -408
  127. package/src/core/generator/catalog-builder.ts +0 -179
  128. package/src/core/generator/chunker.ts +0 -78
  129. package/src/core/generator/coverage-phase.ts +0 -0
  130. package/src/core/generator/coverage-scanner.ts +0 -87
  131. package/src/core/generator/data-factory.ts +0 -759
  132. package/src/core/generator/describe.ts +0 -302
  133. package/src/core/generator/endpoint-warnings.ts +0 -52
  134. package/src/core/generator/fixtures-builder.ts +0 -332
  135. package/src/core/generator/index.ts +0 -14
  136. package/src/core/generator/openapi-reader.ts +0 -297
  137. package/src/core/generator/path-param-disambig.ts +0 -140
  138. package/src/core/generator/resources-builder.ts +0 -898
  139. package/src/core/generator/schema-utils.ts +0 -112
  140. package/src/core/generator/serializer.ts +0 -343
  141. package/src/core/generator/suite-generator.ts +0 -1301
  142. package/src/core/generator/types.ts +0 -63
  143. package/src/core/identity/identity-file.ts +0 -0
  144. package/src/core/lint/affects.ts +0 -28
  145. package/src/core/lint/config.ts +0 -96
  146. package/src/core/lint/format.ts +0 -42
  147. package/src/core/lint/index.ts +0 -94
  148. package/src/core/lint/reporter.ts +0 -128
  149. package/src/core/lint/rules/consistency.ts +0 -158
  150. package/src/core/lint/rules/heuristics.ts +0 -97
  151. package/src/core/lint/rules/strictness.ts +0 -109
  152. package/src/core/lint/types.ts +0 -96
  153. package/src/core/lint/walker.ts +0 -248
  154. package/src/core/meta/meta-store.ts +0 -11
  155. package/src/core/output/README.md +0 -73
  156. package/src/core/output/index.ts +0 -13
  157. package/src/core/output/run.ts +0 -91
  158. package/src/core/output/types.ts +0 -122
  159. package/src/core/parser/dynamic-values.ts +0 -160
  160. package/src/core/parser/env-interpolation.ts +0 -104
  161. package/src/core/parser/filter.ts +0 -97
  162. package/src/core/parser/schema.ts +0 -359
  163. package/src/core/parser/types.ts +0 -117
  164. package/src/core/parser/variables.ts +0 -0
  165. package/src/core/parser/yaml-parser.ts +0 -181
  166. package/src/core/probe/bootstrap.ts +0 -34
  167. package/src/core/probe/dry-run-envelope.ts +0 -61
  168. package/src/core/probe/mass-assignment/classify.ts +0 -175
  169. package/src/core/probe/mass-assignment/cleanup.ts +0 -52
  170. package/src/core/probe/mass-assignment/digest.ts +0 -114
  171. package/src/core/probe/mass-assignment/orchestrator.ts +0 -459
  172. package/src/core/probe/mass-assignment/regression.ts +0 -141
  173. package/src/core/probe/mass-assignment/suspects.ts +0 -92
  174. package/src/core/probe/mass-assignment/types.ts +0 -135
  175. package/src/core/probe/mass-assignment-probe-class.ts +0 -198
  176. package/src/core/probe/mass-assignment-probe.ts +0 -27
  177. package/src/core/probe/mass-assignment-template.ts +0 -240
  178. package/src/core/probe/method-probe.ts +0 -164
  179. package/src/core/probe/method-shared.ts +0 -69
  180. package/src/core/probe/negative-probe.ts +0 -691
  181. package/src/core/probe/orphan-tracker.ts +0 -188
  182. package/src/core/probe/path-discovery.ts +0 -439
  183. package/src/core/probe/probe-harness.ts +0 -119
  184. package/src/core/probe/registry.ts +0 -89
  185. package/src/core/probe/runner.ts +0 -136
  186. package/src/core/probe/security/baseline.ts +0 -174
  187. package/src/core/probe/security/classify.ts +0 -341
  188. package/src/core/probe/security/cleanup.ts +0 -125
  189. package/src/core/probe/security/detectors.ts +0 -71
  190. package/src/core/probe/security/digest.ts +0 -104
  191. package/src/core/probe/security/orchestrator.ts +0 -398
  192. package/src/core/probe/security/regression.ts +0 -103
  193. package/src/core/probe/security/types.ts +0 -151
  194. package/src/core/probe/security-probe-class.ts +0 -207
  195. package/src/core/probe/security-probe.ts +0 -32
  196. package/src/core/probe/shared.ts +0 -531
  197. package/src/core/probe/static-probe-class.ts +0 -125
  198. package/src/core/probe/types.ts +0 -165
  199. package/src/core/probe/verdict-aggregator.ts +0 -33
  200. package/src/core/probe/webhooks-probe.ts +0 -282
  201. package/src/core/reporter/console.ts +0 -240
  202. package/src/core/reporter/index.ts +0 -22
  203. package/src/core/reporter/json.ts +0 -22
  204. package/src/core/reporter/junit.ts +0 -93
  205. package/src/core/reporter/ndjson.ts +0 -37
  206. package/src/core/reporter/types.ts +0 -15
  207. package/src/core/runner/assertions.ts +0 -383
  208. package/src/core/runner/async-pool.ts +0 -108
  209. package/src/core/runner/auth-path.ts +0 -8
  210. package/src/core/runner/ci-context.ts +0 -72
  211. package/src/core/runner/executor.ts +0 -652
  212. package/src/core/runner/expr-eval.ts +0 -41
  213. package/src/core/runner/form-encode.ts +0 -41
  214. package/src/core/runner/http-client.ts +0 -224
  215. package/src/core/runner/learn-drift.ts +0 -293
  216. package/src/core/runner/preflight-vars.ts +0 -153
  217. package/src/core/runner/progress-tracker.ts +0 -73
  218. package/src/core/runner/rate-limiter.ts +0 -185
  219. package/src/core/runner/run-kind.ts +0 -45
  220. package/src/core/runner/schema-validator.ts +0 -308
  221. package/src/core/runner/send-request.ts +0 -232
  222. package/src/core/runner/transforms.ts +0 -65
  223. package/src/core/runner/types.ts +0 -94
  224. package/src/core/secrets/registry.ts +0 -164
  225. package/src/core/secrets/secrets-file.ts +0 -115
  226. package/src/core/selectors/operation-filter.ts +0 -144
  227. package/src/core/setup-api.ts +0 -590
  228. package/src/core/severity/category.ts +0 -94
  229. package/src/core/severity/index.ts +0 -58
  230. package/src/core/spec/infer-schema.ts +0 -102
  231. package/src/core/spec/layers.ts +0 -154
  232. package/src/core/spec/merge-specs.ts +0 -156
  233. package/src/core/spec/schema-from-runs.ts +0 -117
  234. package/src/core/spec/schema-overlay.ts +0 -130
  235. package/src/core/util/ajv.ts +0 -13
  236. package/src/core/util/format-eta.ts +0 -21
  237. package/src/core/util/headers.ts +0 -9
  238. package/src/core/util/url.ts +0 -24
  239. package/src/core/utils.ts +0 -13
  240. package/src/core/workspace/config.ts +0 -129
  241. package/src/core/workspace/fixture-gap-report.ts +0 -84
  242. package/src/core/workspace/fixture-gaps.ts +0 -71
  243. package/src/core/workspace/manifest.ts +0 -283
  244. package/src/core/workspace/output-rotation.ts +0 -62
  245. package/src/core/workspace/root.ts +0 -96
  246. package/src/core/workspace/triage-path.ts +0 -87
  247. package/src/db/lint-runs.ts +0 -47
  248. package/src/db/migrate.ts +0 -128
  249. package/src/db/migrations/0001_run_kind.sql +0 -25
  250. package/src/db/migrations/0002_run_kind_request.sql +0 -59
  251. package/src/db/migrations/sql.d.ts +0 -4
  252. package/src/db/queries/collections.ts +0 -133
  253. package/src/db/queries/coverage.ts +0 -9
  254. package/src/db/queries/dashboard.ts +0 -59
  255. package/src/db/queries/results.ts +0 -216
  256. package/src/db/queries/runs.ts +0 -289
  257. package/src/db/queries/sessions.ts +0 -42
  258. package/src/db/queries/settings.ts +0 -28
  259. package/src/db/queries/types.ts +0 -172
  260. package/src/db/queries.ts +0 -75
  261. package/src/db/schema.ts +0 -298
@@ -1,105 +0,0 @@
1
- /**
2
- * Shared helpers for command-action callbacks: resolving the active API
3
- * collection, the spec argument that spec-consuming commands accept, and
4
- * tiny utilities (`globalJson`, deprecation warning). Extracted from
5
- * program.ts (TASK-190 round 2a) so per-command modules can register
6
- * themselves without re-importing program.ts.
7
- */
8
-
9
- import type { Command } from "commander";
10
- import { getDb } from "../db/schema.ts";
11
- import { findCollectionByNameOrId } from "../db/queries.ts";
12
- import { resolveCollectionSpec } from "../core/setup-api.ts";
13
- import { readCurrentApi } from "../core/context/current.ts";
14
-
15
- /**
16
- * TASK-73: `--json` is a per-command option (not a top-level global) so
17
- * that `run --json` does not collide with `run --report json`.
18
- * Subcommands that support an envelope output add `.option("--json", ...)`
19
- * themselves and we read it from local opts.
20
- */
21
- export function globalJson(cmd: Command): boolean {
22
- return cmd.opts().json === true;
23
- }
24
-
25
- /** Resolve API collection → returns { spec?, testPath?, baseDir? } or { error } when not found. */
26
- export function resolveApiCollection(apiName: string, dbPath: string | undefined):
27
- | { spec: string | null; testPath: string | null; baseDir: string | null }
28
- | { error: string } {
29
- if (typeof apiName !== "string" || apiName.length === 0) {
30
- return { error: "Internal: --api received non-string value" };
31
- }
32
- try {
33
- getDb(dbPath);
34
- const col = findCollectionByNameOrId(apiName);
35
- if (!col) return { error: `API '${apiName}' not found` };
36
- const spec = col.openapi_spec ? resolveCollectionSpec(col.openapi_spec) : null;
37
- return { spec, testPath: col.test_path ?? null, baseDir: col.base_dir ?? null };
38
- } catch (err) {
39
- return { error: `Failed to resolve --api: ${(err as Error).message}` };
40
- }
41
- }
42
-
43
- /**
44
- * Resolve `apis/<name>/.env.yaml` for a registered API. TASK-233: probe
45
- * subcommands required `--env <file>` even when `--api <name>` was given,
46
- * forcing users to repeat the path. When --api is set we derive the env
47
- * file from the collection's base_dir; only error if the file is missing.
48
- *
49
- * Returns the absolute path to the env file when it exists, otherwise an
50
- * error object. Callers may also fall back to other strategies on miss.
51
- */
52
- export function resolveApiEnv(apiName: string, dbPath: string | undefined):
53
- | { env: string }
54
- | { error: string } {
55
- const col = resolveApiCollection(apiName, dbPath);
56
- if ("error" in col) return col;
57
- if (!col.baseDir) {
58
- return { error: `API '${apiName}' has no base_dir registered — pass --env <file> explicitly.` };
59
- }
60
- const envPath = `${col.baseDir.replace(/\/+$/, "")}/.env.yaml`;
61
- return { env: envPath };
62
- }
63
-
64
- /**
65
- * Resolve a `<spec>` argument used by spec-consuming commands —
66
- * catalog, sync, generate, probe-validation, probe-methods,
67
- * probe-mass-assignment, lint-spec, describe, guide.
68
- *
69
- * Resolution order:
70
- * 1. Explicit positional/flag value — used as-is (URL or filesystem path).
71
- * 2. --api <name> — look up the workspace-local snapshot via
72
- * `resolveCollectionSpec`.
73
- * 3. ZOND_API env / .zond/current-api — same lookup using the currently-selected API
74
- * (TASK-290; resolution chain implemented in core/context/current.ts).
75
- *
76
- * Returns `{ spec }` on success, `{ error }` on failure. Centralised here
77
- * so commands stay thin and skill/CI prompts can rely on either form.
78
- */
79
- export function resolveSpecArg(
80
- positional: string | undefined,
81
- apiFlag: string | undefined,
82
- dbPath: string | undefined,
83
- ): { spec: string } | { error: string } {
84
- if (typeof positional === "string" && positional.length > 0) {
85
- return { spec: positional };
86
- }
87
- const apiName = apiFlag ?? readCurrentApi() ?? undefined;
88
- if (!apiName) {
89
- return {
90
- error: "Need a spec — pass it positionally, via --api <name>, or set the current API with `zond use <name>`.",
91
- };
92
- }
93
- const resolved = resolveApiCollection(apiName, dbPath);
94
- if ("error" in resolved) return { error: resolved.error };
95
- if (!resolved.spec) {
96
- return {
97
- error:
98
- `API '${apiName}' is registered without an OpenAPI spec — this command needs one. ` +
99
- `Run \`zond refresh-api ${apiName} --spec <path|url>\` to attach a spec, ` +
100
- `or use \`zond run --api ${apiName} <test.yaml>\` for YAML-based testing.`,
101
- };
102
- }
103
- return { spec: resolved.spec };
104
- }
105
-
@@ -1,7 +0,0 @@
1
- export function isCompiledBinary(): boolean {
2
- return process.argv[0] === "bun" && import.meta.path.includes("~BUN");
3
- }
4
-
5
- export function getRuntimeInfo(): string {
6
- return isCompiledBinary() ? "standalone" : "bun";
7
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * ARV-299: one safe/live vocabulary across `audit`, `checks run`, and the
3
- * mutating `probe` subcommands so a developer has a single button —
4
- * `--safe` (default) means "no destructive traffic", `--live` opts in.
5
- *
6
- * The help strings live here so all three commands read identically
7
- * (ARV-299 AC#3). `audit` predates this module and keeps its own richer
8
- * --live text (it gates several stages); the shorter pair here is for
9
- * checks/probe.
10
- */
11
-
12
- export const SAFE_HELP =
13
- "Safe mode (DEFAULT): no destructive/mutating traffic — no create/update/delete " +
14
- "chains, no live attack payloads. Explicit inverse of --live; safe if neither is given.";
15
-
16
- export const LIVE_HELP =
17
- "Opt into destructive/mutating operations (create/update/delete chains, live " +
18
- "attack payloads) against a real API. Use only against a throwaway/sandbox account.";
19
-
20
- /** Resolve the safe/live pair to a single boolean. Default is safe;
21
- * `--live` wins if both are somehow passed. */
22
- export function resolveLive(opts: { safe?: boolean; live?: boolean }): boolean {
23
- return opts.live === true;
24
- }
@@ -1,114 +0,0 @@
1
- /**
2
- * TASK-140: parse `--status` filter expressions for `zond db run/runs`.
3
- *
4
- * Accepted forms (combinable via comma):
5
- * 502 — exact code
6
- * 5xx / 4xx / 3xx / 2xx / 1xx — class wildcard (`5xx` ≡ 500..599)
7
- * 500-599 — inclusive range
8
- * >=500 / >500 / <=400 / <400 — open-ended comparison
9
- * 500,502,504 — list of exacts
10
- * 5xx,429 — mix of class + exact
11
- *
12
- * The parser yields a `StatusMatcher` which the DB layer converts into a
13
- * single SQL `WHERE` fragment (set + ranges combined with `OR`).
14
- */
15
-
16
- export interface StatusMatcher {
17
- /** Specific codes that should match. */
18
- exacts: number[];
19
- /** Inclusive ranges `[min, max]` — including class wildcards (5xx → 500..599). */
20
- ranges: Array<[number, number]>;
21
- }
22
-
23
- const CLASS_RE = /^([1-5])xx$/i;
24
- const RANGE_RE = /^(\d{3})-(\d{3})$/;
25
- const CMP_RE = /^(>=|<=|>|<)\s*(\d{3})$/;
26
- const CODE_RE = /^\d{3}$/;
27
-
28
- function pushExact(out: StatusMatcher, code: number): void {
29
- if (code < 100 || code > 599) {
30
- throw new Error(`status code out of range: ${code}`);
31
- }
32
- if (!out.exacts.includes(code)) out.exacts.push(code);
33
- }
34
-
35
- function pushRange(out: StatusMatcher, min: number, max: number): void {
36
- if (min > max) throw new Error(`status range start > end: ${min}-${max}`);
37
- out.ranges.push([min, max]);
38
- }
39
-
40
- /**
41
- * Parse a `--status` argument. Throws on invalid syntax — caller wraps the
42
- * thrown message in a CLI error.
43
- */
44
- export function parseStatusFilter(raw: string): StatusMatcher {
45
- const trimmed = raw.trim();
46
- if (trimmed === "") throw new Error("empty --status value");
47
- const parts = trimmed.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
48
- if (parts.length === 0) throw new Error("empty --status value");
49
-
50
- const out: StatusMatcher = { exacts: [], ranges: [] };
51
- for (const part of parts) {
52
- let m: RegExpMatchArray | null;
53
- if ((m = part.match(CODE_RE))) {
54
- pushExact(out, Number(part));
55
- continue;
56
- }
57
- if ((m = part.match(CLASS_RE))) {
58
- const klass = Number(m[1]);
59
- pushRange(out, klass * 100, klass * 100 + 99);
60
- continue;
61
- }
62
- if ((m = part.match(RANGE_RE))) {
63
- const lo = Number(m[1]);
64
- const hi = Number(m[2]);
65
- if (lo < 100 || lo > 599 || hi < 100 || hi > 599) {
66
- throw new Error(`status range out of bounds: ${part} (expected 100..599)`);
67
- }
68
- pushRange(out, lo, hi);
69
- continue;
70
- }
71
- if ((m = part.match(CMP_RE))) {
72
- const op = m[1]!;
73
- const code = Number(m[2]);
74
- if (code < 100 || code > 599) {
75
- throw new Error(`status comparison out of bounds: ${part} (expected 100..599)`);
76
- }
77
- switch (op) {
78
- case ">=": pushRange(out, code, 599); break;
79
- case ">": pushRange(out, code + 1, 599); break;
80
- case "<=": pushRange(out, 100, code); break;
81
- case "<": pushRange(out, 100, code - 1); break;
82
- }
83
- continue;
84
- }
85
- throw new Error(
86
- `invalid --status part: '${part}' (expected one of: 502, 5xx, 500-599, >=500, <400)`,
87
- );
88
- }
89
- return out;
90
- }
91
-
92
- /**
93
- * Compile a `StatusMatcher` to a SQL `WHERE` fragment + bound parameters.
94
- * The fragment is wrapped in parentheses; combine with other conditions via
95
- * `AND`. Returns `null` if the matcher is empty (no constraints).
96
- */
97
- export function compileStatusFilterToSql(
98
- matcher: StatusMatcher,
99
- column: string,
100
- ): { sql: string; params: number[] } | null {
101
- const clauses: string[] = [];
102
- const params: number[] = [];
103
- if (matcher.exacts.length > 0) {
104
- const placeholders = matcher.exacts.map(() => "?").join(",");
105
- clauses.push(`${column} IN (${placeholders})`);
106
- params.push(...matcher.exacts);
107
- }
108
- for (const [lo, hi] of matcher.ranges) {
109
- clauses.push(`${column} BETWEEN ? AND ?`);
110
- params.push(lo, hi);
111
- }
112
- if (clauses.length === 0) return null;
113
- return { sql: `(${clauses.join(" OR ")})`, params };
114
- }
@@ -1,85 +0,0 @@
1
- /**
2
- * ARV-53: single resolver for the active `--api` collection name.
3
- *
4
- * Before this module the chain `localOpts.api → cmd.parent?.opts().api →
5
- * readCurrentApi()` was inlined in nine call-sites (probe / prepare-fixtures
6
- * / audit / checks / coverage / run / request, plus two intermediate helpers
7
- * in probe.ts and resolve.ts). Each repeat was a separate commit
8
- * (TASK-17, TASK-20, ARV-21, ARV-29, ARV-33). Centralising the chain here
9
- * means new commands consume one import and the fallback rules live in
10
- * exactly one place.
11
- *
12
- * Resolution order (matches the chain users see documented for `zond use`):
13
- * 1. Per-command `--api <name>` (the local Commander scope).
14
- * 2. Any ancestor command's `--api`, walking up to the program root
15
- * (covers the global `zond --api X <subcmd>` form whose value is
16
- * otherwise stranded on the parent Command).
17
- * 3. `readCurrentApi()` — which itself folds in
18
- * `ZOND_API_GLOBAL` (mirrored by program.ts preAction) →
19
- * `ZOND_API` (user env) → `.zond/current-api` (persisted by `zond use`).
20
- */
21
-
22
- import { readCurrentApi } from "../../core/context/current.ts";
23
-
24
- /**
25
- * Minimal shape we need from a Commander `Command`. Spelled out as an
26
- * interface (not `import("commander").Command`) so test doubles can pass a
27
- * plain `{ opts, parent }` literal and TS still type-checks.
28
- */
29
- export interface CommandLike {
30
- opts(): Record<string, unknown>;
31
- parent?: CommandLike | null;
32
- }
33
-
34
- export type ApiResolution =
35
- | { ok: true; api: string; source: "local" | "ancestor" | "current" }
36
- | { ok: false };
37
-
38
- /** Pull the explicit --api value out of a command's parsed opts, if any. */
39
- function readApiOpt(cmd: CommandLike): string | undefined {
40
- const v = cmd.opts().api;
41
- return typeof v === "string" && v.trim().length > 0 ? v.trim() : undefined;
42
- }
43
-
44
- /**
45
- * Resolve the active API collection name for `cmd`. Pass `localOpts` when
46
- * the action handler already has the parsed local opts in hand (avoids a
47
- * second `cmd.opts()` parse and lets callers tunnel through pre-coerced
48
- * shapes from tests).
49
- */
50
- export function resolveApi(
51
- cmd: CommandLike | undefined,
52
- localOpts?: Record<string, unknown>,
53
- ): ApiResolution {
54
- const localRaw = localOpts?.api;
55
- const local = typeof localRaw === "string" && localRaw.trim().length > 0
56
- ? localRaw.trim()
57
- : (cmd ? readApiOpt(cmd) : undefined);
58
- if (local) return { ok: true, api: local, source: "local" };
59
-
60
- let parent: CommandLike | null | undefined = cmd?.parent ?? null;
61
- while (parent) {
62
- const fromAncestor = readApiOpt(parent);
63
- if (fromAncestor) return { ok: true, api: fromAncestor, source: "ancestor" };
64
- parent = parent.parent ?? null;
65
- }
66
-
67
- const fromCurrent = readCurrentApi();
68
- if (fromCurrent) return { ok: true, api: fromCurrent, source: "current" };
69
-
70
- return { ok: false };
71
- }
72
-
73
- /**
74
- * Convenience: returns the API name as `string | undefined`. Use this when
75
- * the caller decides for itself how to react to "missing" (e.g. `coverage`
76
- * falls back to `--spec`, `run` falls back to a positional path).
77
- */
78
- export function getApi(cmd: CommandLike | undefined, localOpts?: Record<string, unknown>): string | undefined {
79
- const r = resolveApi(cmd, localOpts);
80
- return r.ok ? r.api : undefined;
81
- }
82
-
83
- /** Default error message for commands that strictly require an API. */
84
- export const MISSING_API_MESSAGE =
85
- "--api is required (or set ZOND_API / `zond use <name>`).";
@@ -1,8 +0,0 @@
1
- import { version } from "../../package.json";
2
-
3
- export const VERSION = version;
4
-
5
- /** Canonical GitHub repo (owner/name). Single source of truth for any code
6
- * that links to releases, install scripts, or generated artefacts. */
7
- export const REPO = "kirrosh/zond";
8
- export const REPO_URL = `https://github.com/${REPO}`;
@@ -1,183 +0,0 @@
1
- /**
2
- * ARV-265: shared helpers for "any HTTP touch by zond should be visible
3
- * to `audit-coverage`". `zond run` was the only producer that wrote into
4
- * `runs` / `results`; `checks run`, `probe`, `request`, and
5
- * `prepare-fixtures --cascade` all emitted ndjson/stdout and left no
6
- * persistent trace. This module gives those producers a uniform path:
7
- *
8
- * 1. `beginAuditRun(opts)` — INSERT into `runs` with the right
9
- * `run_kind` and return the row id.
10
- * 2. accumulate `AuditCaseRecord` entries as the work runs.
11
- * 3. `finalizeAuditRun(runId, suiteName, cases)` — group cases into a
12
- * single synthetic `TestRunResult`, call `saveResults`, then
13
- * `finalizeRun` so the row carries totals.
14
- *
15
- * Suite/test naming is *pseudo* (`checks/response`, `probe/security`,
16
- * `request/ad-hoc`) — these rows aren't meant to drive failure triage,
17
- * just to keep the audit-coverage matrix honest. The coverage engine
18
- * matches results to endpoints via (request_method, request_url) regex,
19
- * so the suite name is purely cosmetic.
20
- *
21
- * Error handling: every write is wrapped in try/catch by the CLI caller
22
- * and degrades to a warning — failing to persist must not break the
23
- * primary command (a `checks run` that exits 0 with no DB row is still
24
- * a successful checks run for the user; audit-coverage just won't pick
25
- * it up).
26
- */
27
- import type { HttpRequest, HttpResponse, TestRunResult, StepResult, StepStatus } from "../runner/types.ts";
28
- import type { RunKind } from "../runner/run-kind.ts";
29
- import { createRun, saveResults, finalizeRun } from "../../db/queries.ts";
30
- import { setHttpAuditRecorder, type AuditRecord } from "../runner/http-client.ts";
31
-
32
- export interface BeginAuditRunOpts {
33
- runKind: RunKind;
34
- collectionId?: number;
35
- sessionId?: string | null;
36
- environment?: string;
37
- trigger?: string;
38
- tags?: string[];
39
- commitSha?: string;
40
- branch?: string;
41
- startedAt?: string;
42
- }
43
-
44
- export interface AuditCaseRecord {
45
- suiteName: string;
46
- testName: string;
47
- status: StepStatus;
48
- request: HttpRequest;
49
- response?: HttpResponse;
50
- durationMs: number;
51
- error?: string;
52
- /** Optional `suite_file` value — surfaces in the diagnose output and
53
- * helps the legacy detectRunKind heuristic understand the row's
54
- * origin even if `run_kind` ever drifts. */
55
- suiteFile?: string;
56
- }
57
-
58
- export function beginAuditRun(opts: BeginAuditRunOpts): number {
59
- return createRun({
60
- started_at: opts.startedAt ?? new Date().toISOString(),
61
- environment: opts.environment,
62
- trigger: opts.trigger ?? "manual",
63
- commit_sha: opts.commitSha,
64
- branch: opts.branch,
65
- collection_id: opts.collectionId,
66
- session_id: opts.sessionId ?? undefined,
67
- tags: opts.tags && opts.tags.length > 0 ? opts.tags : undefined,
68
- run_kind: opts.runKind,
69
- });
70
- }
71
-
72
- /**
73
- * Group flat case records into one `TestRunResult` per `suite_file` (or
74
- * per `suiteName` when no file is set) and write them. Returns the number
75
- * of `results` rows persisted so the caller can sanity-check the totals.
76
- */
77
- export function finalizeAuditRun(
78
- runId: number,
79
- cases: AuditCaseRecord[],
80
- ): { rows: number } {
81
- if (cases.length === 0) {
82
- // Nothing executed — still close the run so coverage queries can
83
- // see an empty audit row instead of an open-ended row dangling
84
- // without finished_at.
85
- finalizeRun(runId, []);
86
- return { rows: 0 };
87
- }
88
- const bySuite = new Map<string, AuditCaseRecord[]>();
89
- for (const c of cases) {
90
- const key = c.suiteFile ?? c.suiteName;
91
- const list = bySuite.get(key) ?? [];
92
- list.push(c);
93
- bySuite.set(key, list);
94
- }
95
- const now = new Date().toISOString();
96
- const results: TestRunResult[] = [];
97
- for (const [, group] of bySuite) {
98
- const first = group[0]!;
99
- const steps: StepResult[] = group.map((c) => ({
100
- name: c.testName,
101
- status: c.status,
102
- duration_ms: c.durationMs,
103
- request: c.request,
104
- response: c.response,
105
- assertions: [],
106
- captures: {},
107
- ...(c.error ? { error: c.error } : {}),
108
- }));
109
- let passed = 0, failed = 0, skipped = 0;
110
- for (const s of steps) {
111
- if (s.status === "pass") passed += 1;
112
- else if (s.status === "skip") skipped += 1;
113
- else failed += 1; // fail | error both bucket as failed for the row totals
114
- }
115
- results.push({
116
- suite_name: first.suiteName,
117
- ...(first.suiteFile ? { suite_file: first.suiteFile } : {}),
118
- started_at: now,
119
- finished_at: now,
120
- total: steps.length,
121
- passed,
122
- failed,
123
- skipped,
124
- steps,
125
- });
126
- }
127
- saveResults(runId, results);
128
- finalizeRun(runId, results);
129
- return { rows: cases.length };
130
- }
131
-
132
- /** ARV-265: ZOND_CHECKS_PERSIST opt-out. Default ON — the task's whole
133
- * point is that `checks run` contributes to audit-coverage out of the
134
- * box. Setting the env var to "0" / "false" disables it (for tests, or
135
- * for users who want the pre-ARV-265 behaviour back). */
136
- export function checksPersistEnabled(): boolean {
137
- const v = (process.env.ZOND_CHECKS_PERSIST ?? "").trim().toLowerCase();
138
- if (v === "0" || v === "false" || v === "off" || v === "no") return false;
139
- return true;
140
- }
141
-
142
- /**
143
- * ARV-265: run `fn` with the HTTP audit recorder set; return both the
144
- * function's value and the collected records. Convenience for the live
145
- * probe commands (probe security / probe mass-assignment) which can't
146
- * easily hook into their own internals but route every request through
147
- * `executeRequest`.
148
- *
149
- * The recorder is always cleared in `finally`, even if `fn` throws.
150
- */
151
- export async function withHttpAudit<T>(
152
- fn: () => Promise<T>,
153
- ): Promise<{ value: T; records: AuditRecord[] }> {
154
- const records: AuditRecord[] = [];
155
- setHttpAuditRecorder((rec) => records.push(rec));
156
- try {
157
- const value = await fn();
158
- return { value, records };
159
- } finally {
160
- setHttpAuditRecorder(null);
161
- }
162
- }
163
-
164
- export function auditRecordToCase(
165
- rec: AuditRecord,
166
- meta: { suiteName: string; suiteFile: string; testName: string },
167
- ): AuditCaseRecord {
168
- const status: StepStatus = rec.error
169
- ? "error"
170
- : rec.response && rec.response.status >= 200 && rec.response.status < 400 ? "pass" : "fail";
171
- return {
172
- suiteName: meta.suiteName,
173
- suiteFile: meta.suiteFile,
174
- testName: meta.testName,
175
- status,
176
- request: rec.request,
177
- ...(rec.response ? { response: rec.response } : {}),
178
- durationMs: rec.durationMs,
179
- ...(rec.error ? { error: rec.error } : {}),
180
- };
181
- }
182
-
183
- export type { AuditRecord } from "../runner/http-client.ts";
@@ -1,59 +0,0 @@
1
- /**
2
- * `--budget quick|standard|full` (ARV-292): adaptive cap for `zond audit` and
3
- * `zond checks run`, designed so small-team CI gets a "60-sec gate" by default
4
- * without having to remember `--max-requests` for huge specs.
5
- *
6
- * Tiers:
7
- * quick — 50 req cap, skip stateful (CRUD) checks. ~60-sec wall-clock.
8
- * standard — 500 req cap, all checks (sampling kept as a Phase-B knob).
9
- * full — uncapped, all checks.
10
- *
11
- * Omitted `--budget` keeps the legacy uncapped behaviour so existing CI
12
- * pipelines don't suddenly trip `max-requests-cap-reached`. `--max-requests`
13
- * always wins over the tier-seeded cap.
14
- */
15
-
16
- export type Budget = "quick" | "standard" | "full";
17
-
18
- export const BUDGETS: readonly Budget[] = ["quick", "standard", "full"];
19
-
20
- export interface ResolvedBudget {
21
- maxRequests?: number;
22
- skipStateful: boolean;
23
- }
24
-
25
- interface TierDefaults {
26
- maxRequests?: number;
27
- skipStateful: boolean;
28
- }
29
-
30
- const TIERS: Record<Budget, TierDefaults> = {
31
- quick: { maxRequests: 50, skipStateful: true },
32
- standard: { maxRequests: 500, skipStateful: false },
33
- full: { skipStateful: false },
34
- };
35
-
36
- export function isBudget(value: unknown): value is Budget {
37
- return typeof value === "string" && (BUDGETS as readonly string[]).includes(value);
38
- }
39
-
40
- /** Resolve final budget given an explicit budget tier and an optional
41
- * `--max-requests` override. When `--max-requests` is set it always wins,
42
- * even if it relaxes a tighter tier cap (e.g. `--budget quick --max-requests
43
- * 200` → cap=200). Stateful-skip is tier-driven only; pass an explicit
44
- * include list via `forceStatefulIfIncluded` to opt back in (e.g. when the
45
- * user typed `--check stateful`). */
46
- export function resolveBudget(
47
- budget: Budget | undefined,
48
- maxRequestsOverride: number | undefined,
49
- opts?: { forceStatefulIfIncluded?: boolean },
50
- ): ResolvedBudget {
51
- const tier = budget ? TIERS[budget] : undefined;
52
- const skipStateful = tier?.skipStateful === true && opts?.forceStatefulIfIncluded !== true;
53
- const cap = typeof maxRequestsOverride === "number" && maxRequestsOverride > 0
54
- ? maxRequestsOverride
55
- : tier?.maxRequests;
56
- const result: ResolvedBudget = { skipStateful };
57
- if (cap !== undefined) result.maxRequests = cap;
58
- return result;
59
- }