@kirrosh/zond 0.26.0 → 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 +59 -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 -1142
  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,91 +0,0 @@
1
- /**
2
- * ARV-116 (m-19): runner that turns an `OutputSpec` + CLI flags into
3
- * a `ResolvedOutput`, then renders and writes the payload.
4
- *
5
- * Resolution order:
6
- * 1. `--json` and `--report` are mutually exclusive — error.
7
- * 2. If `--report` is set, look it up in `aliases` first, then in
8
- * `formats`. Unknown → error (consistent with ARV-97; never
9
- * silently swallow a typo).
10
- * 3. If `--json` is set, pick the first envelope-wrapping format
11
- * from the spec. Specs without one fall back to the
12
- * `defaultFormat` (acceptable for commands like `run` whose
13
- * `--json` is special-cased — see ARV-117).
14
- * 4. Otherwise use `defaultFormat`.
15
- * 5. Channel: `--output` forces file. Without it, take the format's
16
- * `defaultChannel`. File channel without an explicit `--output`
17
- * uses `defaultFilename` (relative paths resolved against cwd).
18
- */
19
- import { resolve as resolvePath } from "path";
20
- import {
21
- OutputSpecError,
22
- type OutputOptions,
23
- type OutputSpec,
24
- type ResolvedOutput,
25
- } from "./types.ts";
26
-
27
- export function resolveOutput<P>(
28
- spec: OutputSpec<P>,
29
- opts: OutputOptions,
30
- ): ResolvedOutput {
31
- if (opts.json && typeof opts.report === "string" && opts.report.length > 0) {
32
- throw new OutputSpecError(
33
- "--json and --report are mutually exclusive — pick one output channel",
34
- );
35
- }
36
-
37
- // Step 1: resolve the format.
38
- let format: string;
39
- if (typeof opts.report === "string" && opts.report.length > 0) {
40
- const alias = spec.aliases?.[opts.report];
41
- format = alias ?? opts.report;
42
- } else if (opts.json) {
43
- format = pickEnvelopeFormat(spec) ?? spec.defaultFormat;
44
- } else {
45
- format = spec.defaultFormat;
46
- }
47
-
48
- const policy = spec.formats[format];
49
- if (!policy) {
50
- const known = Object.keys(spec.formats).sort().join(", ");
51
- throw new OutputSpecError(
52
- `Unknown --report format: "${format}". Available for ${spec.command}: ${known}`,
53
- );
54
- }
55
-
56
- // Step 2: resolve channel + path.
57
- const explicitOutput = typeof opts.output === "string" && opts.output.length > 0
58
- ? opts.output
59
- : undefined;
60
- let channel: "stdout" | "file";
61
- let path: string | undefined;
62
- if (explicitOutput) {
63
- channel = "file";
64
- path = resolvePath(explicitOutput);
65
- } else if (policy.defaultChannel === "file") {
66
- channel = "file";
67
- path = policy.defaultFilename ? resolvePath(policy.defaultFilename) : undefined;
68
- if (!path) {
69
- throw new OutputSpecError(
70
- `Format "${format}" defaults to file but has no defaultFilename and --output was not set`,
71
- );
72
- }
73
- } else {
74
- channel = "stdout";
75
- }
76
-
77
- return {
78
- format,
79
- channel,
80
- path,
81
- envelopeWrap: policy.envelopeWrap === true,
82
- };
83
- }
84
-
85
- function pickEnvelopeFormat<P>(spec: OutputSpec<P>): string | undefined {
86
- for (const [name, policy] of Object.entries(spec.formats)) {
87
- if (policy.envelopeWrap) return name;
88
- }
89
- return undefined;
90
- }
91
-
@@ -1,122 +0,0 @@
1
- /**
2
- * ARV-116 (m-19): typed declaration of every `--report` / `--output` /
3
- * `--json` combination a command supports.
4
- *
5
- * Background. Lesson §E of strategy/lessons.md collects seven separate
6
- * bug reports about output-flag plumbing:
7
- * - ARV-97 — `--report ndjson --output <path>` silently dropped events;
8
- * - ARV-50 — probe `--dry-run` JSON shape diverged from `--report json`;
9
- * - ARV-82 — `db runs --json` envelope `data` field shape disagreed
10
- * with sibling commands;
11
- * - …and four more along the same lines.
12
- *
13
- * Root cause: each command has its own ad-hoc flag parser. Some treat
14
- * "ndjson" as an alias for `--ndjson`, some don't. Some honour
15
- * `--output` for SARIF only, some for every format. Some wrap in a JSON
16
- * envelope, some emit a raw stream. There is no single place to read
17
- * "what does `command X` produce when I pass `--report Y --output Z`".
18
- *
19
- * This module gives that single place. A command declares an
20
- * `OutputSpec<Payload>` once — listing the formats it supports, each
21
- * format's default channel (stdout vs file), each format's default
22
- * filename, and whether the format wraps in the standard `--json`
23
- * envelope. A runner helper (`runCommandWithOutput`) consumes the spec
24
- * plus the CLI flags and resolves them to a single `ResolvedOutput`
25
- * decision, with consistent mutual-exclusion enforcement.
26
- *
27
- * The spec is the source-of-truth for ARV-120's build-time check:
28
- * every `--json` command must declare an `OutputSpec` with an
29
- * `envelopeWrap: true` entry, so the published schema and the runtime
30
- * output cannot drift.
31
- *
32
- * This task only ships the infrastructure. Per-command migration
33
- * (`run`, `checks`, `probe*`) lands in ARV-117/118/119.
34
- */
35
-
36
- /** Where a rendered payload lands. */
37
- export type OutputChannel = "stdout" | "file";
38
-
39
- /** A format name — `sarif` / `ndjson` / `json` / `junit` etc. The set is
40
- * open: each command declares its own. */
41
- export type OutputFormat = string;
42
-
43
- /** Per-format policy: where the format writes by default, what filename
44
- * to use when it writes to a file, and whether the payload is wrapped
45
- * in the standard `{ ok, command, data, ... }` envelope. */
46
- export interface FormatPolicy {
47
- /** Default destination when neither `--output` nor channel-overriding
48
- * flags are present. SARIF defaults to file (`zond-checks.sarif`);
49
- * NDJSON defaults to stdout (one event per line for piping); JSON
50
- * envelopes default to stdout. */
51
- defaultChannel: OutputChannel;
52
- /** Default filename when `defaultChannel === "file"`. Relative paths
53
- * are resolved against cwd by the runner. Ignored when
54
- * `defaultChannel === "stdout"` — the user has to opt in to a file
55
- * via `--output`. */
56
- defaultFilename?: string;
57
- /** True for formats that should be wrapped in the shared envelope
58
- * (`jsonOk` / `jsonError` from `cli/json-envelope.ts`). Streaming
59
- * formats (NDJSON) and bespoke serialisations (SARIF, JUnit) are
60
- * not envelope-wrapped — they have their own contracts. */
61
- envelopeWrap?: boolean;
62
- /** ARV-120: for `envelopeWrap` formats — basename of the JSON schema
63
- * under `docs/json-schema/` that describes the envelope's `data`
64
- * field. The build-time coverage test asserts the file exists, so
65
- * a renamed/deleted schema fails CI together with the spec. */
66
- envelopeSchemaFile?: string;
67
- /** Optional human description, surfaced by `--help` generators and
68
- * the README table. */
69
- description?: string;
70
- }
71
-
72
- export interface OutputSpec<Payload = unknown> {
73
- /** Command name — propagated into the JSON envelope's `command` field
74
- * and used by error messages. */
75
- command: string;
76
- /** Supported formats keyed by name. Unknown formats fall through to
77
- * an error (no silent acceptance, see ARV-97). */
78
- formats: Record<OutputFormat, FormatPolicy>;
79
- /** Default format when neither `--report` nor `--json` is set. */
80
- defaultFormat: OutputFormat;
81
- /** Optional alias map: `{ ndjson: 'ndjson' }` lets `--report ndjson`
82
- * fold into the `--ndjson` flag (ARV-63 alias, retained because
83
- * skill prompts ship it). Keys are flag values seen on the CLI;
84
- * values are the resolved format name. */
85
- aliases?: Record<string, OutputFormat>;
86
- }
87
-
88
- /** Decision the runner makes after applying the spec to CLI flags. */
89
- export interface ResolvedOutput {
90
- /** Resolved format name (always one of `spec.formats`). */
91
- format: OutputFormat;
92
- /** Resolved destination. */
93
- channel: OutputChannel;
94
- /** Filesystem path when `channel === "file"`. Absolute path expected
95
- * by callers — the runner resolves it against cwd. */
96
- path?: string;
97
- /** Whether the runner should wrap the payload in the standard
98
- * envelope before writing. Mirrors `FormatPolicy.envelopeWrap`. */
99
- envelopeWrap: boolean;
100
- }
101
-
102
- /** Inputs the runner reads from the CLI layer. Names mirror commander
103
- * options so a command can pass `cmd.opts<OutputOptions>()` directly. */
104
- export interface OutputOptions {
105
- /** `--report <format>`. */
106
- report?: string;
107
- /** `--output <path>`. */
108
- output?: string;
109
- /** `--json`. */
110
- json?: boolean;
111
- }
112
-
113
- /** Thrown by `resolveOutput` when the user-supplied flags violate the
114
- * spec's policy (unknown format, mutually exclusive flags, …). The
115
- * CLI layer catches this and emits the standard `jsonError` or
116
- * human `printError`. */
117
- export class OutputSpecError extends Error {
118
- constructor(message: string) {
119
- super(message);
120
- this.name = "OutputSpecError";
121
- }
122
- }
@@ -1,160 +0,0 @@
1
- /**
2
- * `#(funcName)` / `#(funcName(args))` dynamic-value substitution for
3
- * loaded YAML values (ARV-190, m-21).
4
- *
5
- * Lets a workspace avoid stale hardcoded data — UUIDs that get reused
6
- * by the API and pin existing rows, dates that expire days after
7
- * checkout, idempotency keys that race against past runs. Functions
8
- * are evaluated at LOAD time (per-run, not per-request), so a single
9
- * `#(uuid)` reference resolves to the same value across every step
10
- * inside one run — the idempotency-replay scenarios only work if the
11
- * key stays stable for the run's lifetime.
12
- *
13
- * Supported functions:
14
- * #(uuid) — fresh UUID v4 (stable within one run via cache)
15
- * #(uuidStable(seed)) — deterministic UUID derived from seed (sha-256 → v4 shape)
16
- * #(today) — YYYY-MM-DD now (UTC)
17
- * #(todayPlus(N)) — today + N days (N may be negative)
18
- * #(now) — ISO 8601 timestamp
19
- * #(unix) — seconds since epoch
20
- * #(alphanumeric(N)) — N random a-z0-9 chars
21
- * #(env:VAR) — process.env.VAR (alias for ${VAR})
22
- *
23
- * Resolution order: env-interpolation (${VAR}) runs first because its
24
- * default-value syntax can produce strings the dynamic resolver should
25
- * see literally. Dynamic values run BEFORE @secret/@identity reference
26
- * resolution so the function tokens never leak into a secret-typed
27
- * value (and so secret-stored values that happen to look like `#(...)`
28
- * stay opaque). Strings that have no `#(` substring short-circuit.
29
- *
30
- * Nested forms: a single value may carry multiple references mixed
31
- * with literal text — `"req-#(uuid)-#(today)"` yields `req-<uuid>-2026-05-16`.
32
- */
33
-
34
- import { createHash } from "node:crypto";
35
-
36
- export interface DynamicValueContext {
37
- /** Per-run cache keyed by raw expression (`#(uuid)`, `#(today)`). */
38
- cache: Map<string, string>;
39
- /** Source of env vars; defaults to process.env. Override in tests. */
40
- env?: Record<string, string | undefined>;
41
- /** File path the value came from — surfaced in error messages. */
42
- filePath?: string;
43
- }
44
-
45
- /** Build a fresh cache for one resolution pass. Exported so the caller
46
- * can share a cache across multiple `resolveDynamicValuesDeep` calls
47
- * that belong to the same run (e.g. workspace + per-API env files). */
48
- export function newDynamicCache(): Map<string, string> {
49
- return new Map();
50
- }
51
-
52
- // Captures `#(funcName)` or `#(funcName(args))`. `args` may contain any
53
- // chars except an unescaped closing paren — keep it simple, the funcs we
54
- // support take string or integer args without nested parens.
55
- const FUNC_RE = /#\(([A-Za-z_][A-Za-z0-9_]*)(?::([^)]*))?(?:\(([^)]*)\))?\)/g;
56
-
57
- export function resolveDynamicValues(
58
- text: string,
59
- ctx: DynamicValueContext,
60
- ): string {
61
- if (typeof text !== "string" || text.indexOf("#(") === -1) return text;
62
- return text.replace(FUNC_RE, (full, name: string, colonArg: string | undefined, parenArg: string | undefined) => {
63
- const cached = ctx.cache.get(full);
64
- if (cached !== undefined) return cached;
65
- const value = evaluate(name, colonArg ?? parenArg, full, ctx);
66
- ctx.cache.set(full, value);
67
- return value;
68
- });
69
- }
70
-
71
- export function resolveDynamicValuesDeep(
72
- obj: Record<string, unknown>,
73
- ctx: DynamicValueContext,
74
- ): Record<string, string> {
75
- const out: Record<string, string> = {};
76
- for (const [k, v] of Object.entries(obj)) {
77
- if (typeof v === "string") {
78
- out[k] = resolveDynamicValues(v, ctx);
79
- } else {
80
- out[k] = String(v);
81
- }
82
- }
83
- return out;
84
- }
85
-
86
- function evaluate(
87
- name: string,
88
- arg: string | undefined,
89
- fullExpr: string,
90
- ctx: DynamicValueContext,
91
- ): string {
92
- switch (name) {
93
- case "uuid":
94
- return crypto.randomUUID();
95
- case "uuidStable": {
96
- if (!arg) throw makeError(`#(uuidStable(seed)) requires a seed argument`, fullExpr, ctx);
97
- return seedToUuid(arg);
98
- }
99
- case "today":
100
- return new Date().toISOString().slice(0, 10);
101
- case "todayPlus": {
102
- if (!arg) throw makeError(`#(todayPlus(N)) requires an integer N`, fullExpr, ctx);
103
- const n = Number.parseInt(arg, 10);
104
- if (!Number.isFinite(n)) throw makeError(`#(todayPlus(${arg})) — N must be an integer`, fullExpr, ctx);
105
- const d = new Date();
106
- d.setUTCDate(d.getUTCDate() + n);
107
- return d.toISOString().slice(0, 10);
108
- }
109
- case "now":
110
- return new Date().toISOString();
111
- case "unix":
112
- return String(Math.floor(Date.now() / 1000));
113
- case "alphanumeric": {
114
- const n = arg ? Number.parseInt(arg, 10) : 8;
115
- if (!Number.isFinite(n) || n <= 0 || n > 1024) {
116
- throw makeError(`#(alphanumeric(${arg ?? ""})) — length must be 1..1024`, fullExpr, ctx);
117
- }
118
- return randomAlphanumeric(n);
119
- }
120
- case "env": {
121
- if (!arg) throw makeError(`#(env:VAR) requires a variable name`, fullExpr, ctx);
122
- const env = ctx.env ?? (process.env as Record<string, string | undefined>);
123
- const v = env[arg];
124
- if (v === undefined || v === "") {
125
- throw makeError(`#(env:${arg}) is not set — define it in your shell or CI secret`, fullExpr, ctx);
126
- }
127
- return v;
128
- }
129
- default:
130
- throw makeError(`unknown dynamic function "${name}" in ${fullExpr} — supported: uuid, uuidStable, today, todayPlus, now, unix, alphanumeric, env`, fullExpr, ctx);
131
- }
132
- }
133
-
134
- function seedToUuid(seed: string): string {
135
- // SHA-256 the seed, take first 16 bytes, format as UUID v4 shape so
136
- // any consumer-side `format: uuid` validator accepts it. Variant +
137
- // version bits are forced to match v4 — the value is still
138
- // deterministic because the source bytes are stable per seed.
139
- const hash = createHash("sha256").update(seed).digest();
140
- const bytes = Buffer.from(hash.subarray(0, 16));
141
- bytes[6] = (bytes[6]! & 0x0f) | 0x40; // version 4
142
- bytes[8] = (bytes[8]! & 0x3f) | 0x80; // RFC 4122 variant
143
- const hex = bytes.toString("hex");
144
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
145
- }
146
-
147
- const ALPHANUM = "abcdefghijklmnopqrstuvwxyz0123456789";
148
-
149
- function randomAlphanumeric(n: number): string {
150
- let out = "";
151
- for (let i = 0; i < n; i++) {
152
- out += ALPHANUM[Math.floor(Math.random() * ALPHANUM.length)];
153
- }
154
- return out;
155
- }
156
-
157
- function makeError(msg: string, expr: string, ctx: DynamicValueContext): Error {
158
- const where = ctx.filePath ? ` (referenced from ${ctx.filePath})` : "";
159
- return new Error(`Dynamic value ${expr}: ${msg}${where}`);
160
- }
@@ -1,104 +0,0 @@
1
- /**
2
- * `${VAR}` / `${VAR:-default}` substitution for `.env.yaml` (TASK-169, m-10).
3
- *
4
- * Lets a workspace commit `.env.yaml` without bare secrets:
5
- *
6
- * auth_token: "${MYAPI_AUTH_TOKEN}"
7
- * base_url: "${MYAPI_BASE_URL:-https://api.example.com}"
8
- *
9
- * Rules:
10
- * - `${VAR}` → process.env.VAR (throws if missing).
11
- * - `${VAR:-default}` → process.env.VAR ?? default. The default may
12
- * contain `:` (everything after `:-` up to the closing `}` is the
13
- * default).
14
- * - `\${LITERAL}` → literal `${LITERAL}` (the backslash is
15
- * stripped). Matches the `dotenv-expand` / docker-compose convention.
16
- * - One level of resolution only — values pulled from env are NOT
17
- * re-scanned for further `${...}` (cycle-risk).
18
- * - Variable names matching /TOKEN|SECRET|PASSWORD|KEY|DSN/i are NOT
19
- * auto-registered with the redaction registry. Auto-registration is
20
- * opt-in via `@secret:` (TASK-170). We do print a one-line warning
21
- * suggesting the user mark them as a secret, but only the first time.
22
- */
23
-
24
- const ENV_REF_RE = /(\\?)\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g;
25
- const SUSPICIOUS_NAME_RE = /TOKEN|SECRET|PASSWORD|API_KEY|^KEY$|_KEY$|DSN/i;
26
-
27
- export interface EnvInterpolationContext {
28
- /** Absolute path of the file we're resolving — used for error messages. */
29
- filePath: string;
30
- /** YAML key whose value contains the reference — used for error messages. */
31
- key: string;
32
- /** Source of variables; defaults to `process.env`. Override in tests. */
33
- env?: Record<string, string | undefined>;
34
- /** Sink for human-facing warnings. Default: stderr write. */
35
- warn?: (msg: string) => void;
36
- }
37
-
38
- /** Module-level set of variable names we've already warned about, so the
39
- * same `${MYAPI_AUTH_TOKEN}` reference doesn't yell once per env file. */
40
- const warned = new Set<string>();
41
-
42
- export function _resetEnvInterpolationWarnings(): void {
43
- warned.clear();
44
- }
45
-
46
- /**
47
- * Substitute every `${VAR}` / `${VAR:-default}` in `text`. Returns the
48
- * fully-resolved string. Throws `Error` when an unresolved reference has
49
- * no default.
50
- */
51
- export function interpolateEnvRefs(text: string, ctx: EnvInterpolationContext): string {
52
- if (typeof text !== "string" || text.length === 0) return text;
53
- if (text.indexOf("${") === -1 && text.indexOf("\\$") === -1) return text;
54
- const env = ctx.env ?? (process.env as Record<string, string | undefined>);
55
- const warn = ctx.warn ?? ((m: string) => process.stderr.write(m + "\n"));
56
-
57
- return text.replace(ENV_REF_RE, (full, escape: string, name: string, def: string | undefined) => {
58
- if (escape === "\\") {
59
- // Escaped reference → strip the backslash, keep the literal.
60
- return full.slice(1);
61
- }
62
- const value = env[name];
63
- if (value === undefined || value === "") {
64
- if (def !== undefined) {
65
- return def;
66
- }
67
- throw new Error(
68
- `Environment variable \${${name}} is not set (referenced from "${ctx.filePath}", key "${ctx.key}"). ` +
69
- `Provide it via your shell, CI secret, or use the \${${name}:-<default>} form to give it a fallback.`,
70
- );
71
- }
72
- if (SUSPICIOUS_NAME_RE.test(name) && !warned.has(name)) {
73
- warned.add(name);
74
- warn(
75
- `[zond] ${ctx.filePath}: variable \${${name}} looks like a secret. ` +
76
- `Consider mapping it through @secret:${ctx.key} (TASK-170) so it is redacted in artifacts.`,
77
- );
78
- }
79
- return value;
80
- });
81
- }
82
-
83
- /**
84
- * Apply interpolation to every string value in a flat env object. Other
85
- * value types (numbers, booleans, nulls coming back from YAML) are
86
- * stringified untouched, matching the existing loader behaviour.
87
- */
88
- export function interpolateEnvObject(
89
- obj: Record<string, unknown>,
90
- filePath: string,
91
- env?: Record<string, string | undefined>,
92
- ): Record<string, string> {
93
- const out: Record<string, string> = {};
94
- for (const [k, v] of Object.entries(obj)) {
95
- if (typeof v === "string") {
96
- out[k] = interpolateEnvRefs(v, { filePath, key: k, env });
97
- } else {
98
- out[k] = String(v);
99
- }
100
- }
101
- return out;
102
- }
103
-
104
- const SUSPICIOUS_ENV_NAME_RE = SUSPICIOUS_NAME_RE;
@@ -1,97 +0,0 @@
1
- import type { TestSuite } from "./types.ts";
2
- import { compileOperationFilter } from "../selectors/operation-filter.ts";
3
- import type { EndpointInfo } from "../generator/types.ts";
4
-
5
- /**
6
- * Filter suites by tags (OR logic, case-insensitive).
7
- * Suites without tags are excluded when filtering is active.
8
- */
9
- export function filterSuitesByTags(suites: TestSuite[], tags: string[]): TestSuite[] {
10
- if (tags.length === 0) return suites;
11
- const normalizedTags = tags.map(t => t.toLowerCase());
12
- return suites.filter(suite => {
13
- if (!suite.tags || suite.tags.length === 0) return false;
14
- return suite.tags.some(t => normalizedTags.includes(t.toLowerCase()));
15
- });
16
- }
17
-
18
- /**
19
- * Exclude suites whose tags intersect with the exclusion set (OR logic, case-insensitive).
20
- * Suites without tags are kept.
21
- */
22
- export function excludeSuitesByTags(suites: TestSuite[], excludeTags: string[]): TestSuite[] {
23
- if (excludeTags.length === 0) return suites;
24
- const normalizedTags = excludeTags.map(t => t.toLowerCase());
25
- return suites.filter(suite => {
26
- if (!suite.tags || suite.tags.length === 0) return true;
27
- return !suite.tags.some(t => normalizedTags.includes(t.toLowerCase()));
28
- });
29
- }
30
-
31
- /**
32
- * Filter test steps within suites by HTTP method (case-insensitive).
33
- * Suites with no remaining tests are removed.
34
- */
35
- export function filterSuitesByMethod(suites: TestSuite[], method: string): TestSuite[] {
36
- const upperMethod = method.toUpperCase();
37
- const filtered = suites.map(suite => ({
38
- ...suite,
39
- tests: suite.tests.filter(t => (t.method ?? "GET").toUpperCase() === upperMethod),
40
- }));
41
- return filtered.filter(s => s.tests.length > 0);
42
- }
43
-
44
- export interface SuiteFilterResult {
45
- suites: TestSuite[];
46
- errors: string[];
47
- }
48
-
49
- /**
50
- * ARV-25: parity with `zond generate`/`zond checks run` — apply the unified
51
- * `--include`/`--exclude` selector grammar (path/method/tag/operation-id)
52
- * to a list of test suites. Step-level selectors (path, method, operation-id)
53
- * filter steps within a suite; suite-level selectors (tag) borrow each
54
- * step's parent suite tags. A suite drops out once it has no steps left.
55
- *
56
- * Reuses `compileOperationFilter` so semantics match generate/checks 1:1
57
- * (multiple --include combine with OR; --exclude evaluated after includes).
58
- *
59
- * `operation-id` matches against `step.source?.endpoint` ("METHOD /path"),
60
- * which is what the generator records; tests authored manually without
61
- * `source.endpoint` simply never match operation-id selectors.
62
- */
63
- export function filterSuitesByOperationFilter(
64
- suites: TestSuite[],
65
- includes: string[],
66
- excludes: string[],
67
- ): SuiteFilterResult {
68
- if (includes.length === 0 && excludes.length === 0) {
69
- return { suites, errors: [] };
70
- }
71
- const compiled = compileOperationFilter({ includes, excludes });
72
- if (compiled.errors.length > 0) {
73
- return { suites: [], errors: compiled.errors };
74
- }
75
- const filtered = suites.map(suite => ({
76
- ...suite,
77
- tests: suite.tests.filter(step => compiled.filter(stepToEndpoint(suite, step))),
78
- }));
79
- return { suites: filtered.filter(s => s.tests.length > 0), errors: [] };
80
- }
81
-
82
- function stepToEndpoint(suite: TestSuite, step: TestSuite["tests"][number]): EndpointInfo {
83
- const sourceEndpoint = typeof step.source?.endpoint === "string" ? step.source.endpoint : undefined;
84
- return {
85
- path: step.path,
86
- method: step.method,
87
- operationId: sourceEndpoint,
88
- summary: undefined,
89
- tags: suite.tags ?? [],
90
- parameters: [],
91
- requestBodySchema: undefined,
92
- requestBodyContentType: undefined,
93
- responseContentTypes: [],
94
- responses: [],
95
- security: [],
96
- };
97
- }