cgraphx 1.1.0 → 1.3.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 (201) hide show
  1. package/README.md +0 -1
  2. package/dist/.claude-template/hooks/precommit-check/precommit-check.cjs +90 -0
  3. package/dist/.claude-template/skills/cgraphx/SKILL.md +3 -3
  4. package/dist/.claude-template/skills/cgraphx/agent-prompt.md +1 -1
  5. package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +94 -0
  6. package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +424 -0
  7. package/dist/.claude-template/skills/clarify-requirements/SKILL.md +19 -8
  8. package/dist/.claude-template/skills/code-impact-docgen/SKILL.md +186 -176
  9. package/dist/.claude-template/skills/code-impact-docgen/template-design-html.md +357 -0
  10. package/dist/.claude-template/skills/code-impact-docgen/template-design-md.md +164 -0
  11. package/dist/.claude-template/skills/code-impact-init/SKILL.md +47 -47
  12. package/dist/.claude-template/skills/developer-timeline/SKILL.md +9 -0
  13. package/dist/.claude-template/skills/precommit-review/SKILL.md +50 -0
  14. package/dist/.claude-template/skills/run-api-test/SKILL.md +187 -0
  15. package/dist/.claude-template/skills/run-api-test/assets/template-test-report.md +103 -0
  16. package/dist/.claude-template/skills/run-api-test/assets/template-test-verify.jsonl +5 -0
  17. package/dist/.claude-template/skills/run-api-test/references/bru-run.md +60 -0
  18. package/dist/.claude-template/skills/run-api-test/references/db-verification.md +81 -0
  19. package/dist/.claude-template/skills/run-api-test/references/report-format.md +104 -0
  20. package/dist/.claude-template/skills/run-api-test/references/service-readiness.md +61 -0
  21. package/dist/.claude-template/skills/run-api-test/references/test-scope.md +64 -0
  22. package/dist/.claude-template/skills/write-api/SKILL.md +150 -0
  23. package/dist/.claude-template/skills/write-api/assets/template-api-spec.md +112 -0
  24. package/dist/.claude-template/skills/write-api/assets/template-request.bru +75 -0
  25. package/dist/.claude-template/skills/write-api/references/ai-prompts.md +133 -0
  26. package/dist/.claude-template/skills/write-api/references/api-spec-format.md +108 -0
  27. package/dist/.claude-template/skills/write-api/references/bru-format.md +144 -0
  28. package/dist/.claude-template/skills/write-api/references/collection-layout.md +81 -0
  29. package/dist/.claude-template/skills/write-api/references/environment-setup.md +105 -0
  30. package/dist/.claude-template/skills/write-api/references/interface-scope.md +74 -0
  31. package/dist/.claude-template/skills/write-api-doc/SKILL.md +317 -0
  32. package/dist/.claude-template/skills/write-api-doc/template-api-html.md +422 -0
  33. package/dist/.claude-template/skills/write-plan/SKILL.md +38 -16
  34. package/dist/.claude-template/skills/write-prd/SKILL.md +32 -8
  35. package/dist/.claude-template/skills/write-spec/SKILL.md +34 -9
  36. package/dist/api-test/ai-fields.d.ts +37 -0
  37. package/dist/api-test/ai-fields.d.ts.map +1 -0
  38. package/dist/api-test/ai-fields.js +114 -0
  39. package/dist/api-test/ai-fields.js.map +1 -0
  40. package/dist/api-test/assemble.d.ts +76 -0
  41. package/dist/api-test/assemble.d.ts.map +1 -0
  42. package/dist/api-test/assemble.js +185 -0
  43. package/dist/api-test/assemble.js.map +1 -0
  44. package/dist/api-test/bru-cli-invoker.d.ts +72 -0
  45. package/dist/api-test/bru-cli-invoker.d.ts.map +1 -0
  46. package/dist/api-test/bru-cli-invoker.js +169 -0
  47. package/dist/api-test/bru-cli-invoker.js.map +1 -0
  48. package/dist/api-test/bru-report-parser.d.ts +24 -0
  49. package/dist/api-test/bru-report-parser.d.ts.map +1 -0
  50. package/dist/api-test/bru-report-parser.js +110 -0
  51. package/dist/api-test/bru-report-parser.js.map +1 -0
  52. package/dist/api-test/bru-runner.d.ts +101 -0
  53. package/dist/api-test/bru-runner.d.ts.map +1 -0
  54. package/dist/api-test/bru-runner.js +316 -0
  55. package/dist/api-test/bru-runner.js.map +1 -0
  56. package/dist/api-test/bru-writer.d.ts +52 -0
  57. package/dist/api-test/bru-writer.d.ts.map +1 -0
  58. package/dist/api-test/bru-writer.js +159 -0
  59. package/dist/api-test/bru-writer.js.map +1 -0
  60. package/dist/api-test/call-chain-extractor.d.ts +80 -0
  61. package/dist/api-test/call-chain-extractor.d.ts.map +1 -0
  62. package/dist/api-test/call-chain-extractor.js +179 -0
  63. package/dist/api-test/call-chain-extractor.js.map +1 -0
  64. package/dist/api-test/cli.d.ts +133 -0
  65. package/dist/api-test/cli.d.ts.map +1 -0
  66. package/dist/api-test/cli.js +1009 -0
  67. package/dist/api-test/cli.js.map +1 -0
  68. package/dist/api-test/config.d.ts +75 -0
  69. package/dist/api-test/config.d.ts.map +1 -0
  70. package/dist/api-test/config.js +406 -0
  71. package/dist/api-test/config.js.map +1 -0
  72. package/dist/api-test/db-query-cli.d.ts +51 -0
  73. package/dist/api-test/db-query-cli.d.ts.map +1 -0
  74. package/dist/api-test/db-query-cli.js +119 -0
  75. package/dist/api-test/db-query-cli.js.map +1 -0
  76. package/dist/api-test/enhance-prepare.d.ts +111 -0
  77. package/dist/api-test/enhance-prepare.d.ts.map +1 -0
  78. package/dist/api-test/enhance-prepare.js +425 -0
  79. package/dist/api-test/enhance-prepare.js.map +1 -0
  80. package/dist/api-test/enhance-write.d.ts +28 -0
  81. package/dist/api-test/enhance-write.d.ts.map +1 -0
  82. package/dist/api-test/enhance-write.js +145 -0
  83. package/dist/api-test/enhance-write.js.map +1 -0
  84. package/dist/api-test/errors.d.ts +48 -0
  85. package/dist/api-test/errors.d.ts.map +1 -0
  86. package/dist/api-test/errors.js +76 -0
  87. package/dist/api-test/errors.js.map +1 -0
  88. package/dist/api-test/field-extractor.d.ts +98 -0
  89. package/dist/api-test/field-extractor.d.ts.map +1 -0
  90. package/dist/api-test/field-extractor.js +327 -0
  91. package/dist/api-test/field-extractor.js.map +1 -0
  92. package/dist/api-test/impl-finder.d.ts +37 -0
  93. package/dist/api-test/impl-finder.d.ts.map +1 -0
  94. package/dist/api-test/impl-finder.js +54 -0
  95. package/dist/api-test/impl-finder.js.map +1 -0
  96. package/dist/api-test/index.d.ts +41 -0
  97. package/dist/api-test/index.d.ts.map +1 -0
  98. package/dist/api-test/index.js +124 -0
  99. package/dist/api-test/index.js.map +1 -0
  100. package/dist/api-test/java-parser.d.ts +89 -0
  101. package/dist/api-test/java-parser.d.ts.map +1 -0
  102. package/dist/api-test/java-parser.js +508 -0
  103. package/dist/api-test/java-parser.js.map +1 -0
  104. package/dist/api-test/md-writer.d.ts +49 -0
  105. package/dist/api-test/md-writer.d.ts.map +1 -0
  106. package/dist/api-test/md-writer.js +202 -0
  107. package/dist/api-test/md-writer.js.map +1 -0
  108. package/dist/api-test/parser-httpservice.d.ts +91 -0
  109. package/dist/api-test/parser-httpservice.d.ts.map +1 -0
  110. package/dist/api-test/parser-httpservice.js +271 -0
  111. package/dist/api-test/parser-httpservice.js.map +1 -0
  112. package/dist/api-test/report.d.ts +188 -0
  113. package/dist/api-test/report.d.ts.map +1 -0
  114. package/dist/api-test/report.js +522 -0
  115. package/dist/api-test/report.js.map +1 -0
  116. package/dist/api-test/snapshot.d.ts +26 -0
  117. package/dist/api-test/snapshot.d.ts.map +1 -0
  118. package/dist/api-test/snapshot.js +150 -0
  119. package/dist/api-test/snapshot.js.map +1 -0
  120. package/dist/api-test/test-history.d.ts +48 -0
  121. package/dist/api-test/test-history.d.ts.map +1 -0
  122. package/dist/api-test/test-history.js +122 -0
  123. package/dist/api-test/test-history.js.map +1 -0
  124. package/dist/api-test/types.d.ts +174 -0
  125. package/dist/api-test/types.d.ts.map +1 -0
  126. package/dist/api-test/types.js +13 -0
  127. package/dist/api-test/types.js.map +1 -0
  128. package/dist/api-test/verify-prepare.d.ts +30 -0
  129. package/dist/api-test/verify-prepare.d.ts.map +1 -0
  130. package/dist/api-test/verify-prepare.js +150 -0
  131. package/dist/api-test/verify-prepare.js.map +1 -0
  132. package/dist/api-test/verify-write.d.ts +31 -0
  133. package/dist/api-test/verify-write.d.ts.map +1 -0
  134. package/dist/api-test/verify-write.js +159 -0
  135. package/dist/api-test/verify-write.js.map +1 -0
  136. package/dist/bin/codegraph.js +0 -100
  137. package/dist/bin/codegraph.js.map +1 -1
  138. package/dist/dbquery/dump-schema.d.ts +46 -0
  139. package/dist/dbquery/dump-schema.d.ts.map +1 -0
  140. package/dist/dbquery/dump-schema.js +379 -0
  141. package/dist/dbquery/dump-schema.js.map +1 -0
  142. package/dist/installer/targets/claude.d.ts +15 -0
  143. package/dist/installer/targets/claude.d.ts.map +1 -1
  144. package/dist/installer/targets/claude.js +53 -0
  145. package/dist/installer/targets/claude.js.map +1 -1
  146. package/dist/resolution/index.d.ts.map +1 -1
  147. package/dist/resolution/index.js +13 -0
  148. package/dist/resolution/index.js.map +1 -1
  149. package/dist/resolution/scope-index.d.ts +86 -0
  150. package/dist/resolution/scope-index.d.ts.map +1 -0
  151. package/dist/resolution/scope-index.js +143 -0
  152. package/dist/resolution/scope-index.js.map +1 -0
  153. package/dist/resolution/stdlib-blocklist.d.ts +53 -0
  154. package/dist/resolution/stdlib-blocklist.d.ts.map +1 -0
  155. package/dist/resolution/stdlib-blocklist.js +143 -0
  156. package/dist/resolution/stdlib-blocklist.js.map +1 -0
  157. package/dist/search/ast-helpers.d.ts +42 -0
  158. package/dist/search/ast-helpers.d.ts.map +1 -0
  159. package/dist/search/ast-helpers.js +106 -0
  160. package/dist/search/ast-helpers.js.map +1 -0
  161. package/dist/search/call-sites.d.ts +398 -0
  162. package/dist/search/call-sites.d.ts.map +1 -0
  163. package/dist/search/call-sites.js +1433 -0
  164. package/dist/search/call-sites.js.map +1 -0
  165. package/dist/search/context.d.ts +134 -0
  166. package/dist/search/context.d.ts.map +1 -0
  167. package/dist/search/context.js +575 -0
  168. package/dist/search/context.js.map +1 -0
  169. package/dist/search/impact.d.ts +139 -0
  170. package/dist/search/impact.d.ts.map +1 -0
  171. package/dist/search/impact.js +646 -0
  172. package/dist/search/impact.js.map +1 -0
  173. package/dist/search/related.d.ts +178 -0
  174. package/dist/search/related.d.ts.map +1 -0
  175. package/dist/search/related.js +667 -0
  176. package/dist/search/related.js.map +1 -0
  177. package/dist/search/slice.d.ts +148 -0
  178. package/dist/search/slice.d.ts.map +1 -0
  179. package/dist/search/slice.js +460 -0
  180. package/dist/search/slice.js.map +1 -0
  181. package/dist/search/snr-constants.d.ts +41 -0
  182. package/dist/search/snr-constants.d.ts.map +1 -0
  183. package/dist/search/snr-constants.js +44 -0
  184. package/dist/search/snr-constants.js.map +1 -0
  185. package/dist/search/types.d.ts +28 -0
  186. package/dist/search/types.d.ts.map +1 -0
  187. package/dist/search/types.js +12 -0
  188. package/dist/search/types.js.map +1 -0
  189. package/dist/timeline/cli.d.ts.map +1 -1
  190. package/dist/timeline/cli.js +22 -3
  191. package/dist/timeline/cli.js.map +1 -1
  192. package/dist/timeline/store.d.ts +5 -0
  193. package/dist/timeline/store.d.ts.map +1 -1
  194. package/dist/timeline/store.js +23 -3
  195. package/dist/timeline/store.js.map +1 -1
  196. package/package.json +1 -1
  197. package/scripts/agent-eval/subagent-token-cost.py +188 -0
  198. package/dist/.claude-template/skills/code-impact-docgen/template-business-html.md +0 -242
  199. package/dist/.claude-template/skills/code-impact-docgen/template-business-md.md +0 -107
  200. package/dist/.claude-template/skills/code-impact-docgen/template-technical-html.md +0 -205
  201. package/dist/.claude-template/skills/code-impact-docgen/template-technical-md.md +0 -155
@@ -0,0 +1,24 @@
1
+ /**
2
+ * L-T-30: bru-report-parser — parse `bru run --output results.json --format json` output.
3
+ *
4
+ * Bruno CLI report schema (from L-T-00 exploration of bruno source code):
5
+ * { results: [ { name, suitename, path, runDuration, response: { status, body, ... },
6
+ * testResults, assertionResults, error, skipped } ] }
7
+ *
8
+ * Robust parser — supports field aliases across Bruno CLI versions.
9
+ */
10
+ export interface BruReportEntry {
11
+ endpoint: string;
12
+ httpStatus: number;
13
+ responseBody: string;
14
+ tests: unknown[];
15
+ durationMs: number;
16
+ error: string | null;
17
+ }
18
+ /**
19
+ * Parse a bru run JSON report and extract the entry matching `endpoint`.
20
+ * `endpoint` matches against `name` (preferred) or `path` stem.
21
+ * Returns null if no match found.
22
+ */
23
+ export declare function parseBruReport(reportJson: string, endpoint: string): BruReportEntry | null;
24
+ //# sourceMappingURL=bru-report-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-report-parser.d.ts","sourceRoot":"","sources":["../../src/api-test/bru-report-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAe1F"}
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * L-T-30: bru-report-parser — parse `bru run --output results.json --format json` output.
4
+ *
5
+ * Bruno CLI report schema (from L-T-00 exploration of bruno source code):
6
+ * { results: [ { name, suitename, path, runDuration, response: { status, body, ... },
7
+ * testResults, assertionResults, error, skipped } ] }
8
+ *
9
+ * Robust parser — supports field aliases across Bruno CLI versions.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.parseBruReport = parseBruReport;
13
+ /**
14
+ * Parse a bru run JSON report and extract the entry matching `endpoint`.
15
+ * `endpoint` matches against `name` (preferred) or `path` stem.
16
+ * Returns null if no match found.
17
+ */
18
+ function parseBruReport(reportJson, endpoint) {
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(reportJson);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ const results = extractResultsArray(parsed);
27
+ if (!results || results.length === 0)
28
+ return null;
29
+ const match = results.find((r) => matchesEndpoint(r, endpoint));
30
+ if (!match)
31
+ return null;
32
+ return extractEntry(match, endpoint);
33
+ }
34
+ function extractResultsArray(parsed) {
35
+ // Bruno CLI actual output: [{ iterationIndex: 0, results: [{ test, request, response, ... }] }]
36
+ // Unwrap iterations and flatten all `results` arrays.
37
+ if (Array.isArray(parsed)) {
38
+ const flat = [];
39
+ for (const item of parsed) {
40
+ if (item && typeof item === 'object') {
41
+ const obj = item;
42
+ if (Array.isArray(obj.results)) {
43
+ flat.push(...obj.results);
44
+ }
45
+ else if (Array.isArray(obj.tests)) {
46
+ flat.push(...obj.tests);
47
+ }
48
+ else {
49
+ // Already an entry (no wrapper)
50
+ flat.push(item);
51
+ }
52
+ }
53
+ }
54
+ return flat.length > 0 ? flat : parsed;
55
+ }
56
+ if (parsed && typeof parsed === 'object') {
57
+ const obj = parsed;
58
+ if (Array.isArray(obj.results))
59
+ return obj.results;
60
+ if (Array.isArray(obj.tests))
61
+ return obj.tests;
62
+ }
63
+ return null;
64
+ }
65
+ function matchesEndpoint(entry, endpoint) {
66
+ if (!entry || typeof entry !== 'object')
67
+ return false;
68
+ const obj = entry;
69
+ const name = obj.name ?? obj.request?.name;
70
+ if (typeof name === 'string' && name === endpoint)
71
+ return true;
72
+ const entryPath = obj.path ?? obj.test?.filename;
73
+ if (typeof entryPath === 'string') {
74
+ const stem = entryPath.replace(/\.bru$/, '').split('/').pop();
75
+ if (stem === endpoint)
76
+ return true;
77
+ }
78
+ return false;
79
+ }
80
+ function extractEntry(entry, endpoint) {
81
+ const obj = (entry && typeof entry === 'object' ? entry : {});
82
+ const response = (obj.response ?? {});
83
+ const httpStatus = (typeof response.status === 'number' ? response.status : null) ??
84
+ (typeof response.statusCode === 'number' ? response.statusCode : null) ??
85
+ (typeof obj.status === 'number' ? obj.status : 0);
86
+ // Bruno CLI stores response body in `response.data` (object or string), not `response.body`.
87
+ const responseData = response.data ?? response.body ?? obj.responseBody;
88
+ const responseBody = typeof responseData === 'string' ? responseData :
89
+ (responseData && typeof responseData === 'object' ? JSON.stringify(responseData) : '');
90
+ const tests = Array.isArray(obj.testResults) ? obj.testResults :
91
+ Array.isArray(obj.tests) ? obj.tests :
92
+ Array.isArray(obj.assertionResults) ? obj.assertionResults :
93
+ [];
94
+ const runDuration = (typeof obj.runDuration === 'number' ? obj.runDuration : null) ??
95
+ (typeof obj.duration === 'number' ? obj.duration : 0);
96
+ const durationMs = Math.round(runDuration * 1000);
97
+ const error = (typeof obj.error === 'string' ? obj.error : null) ??
98
+ (obj.error && typeof obj.error === 'object' && typeof obj.error.message === 'string'
99
+ ? obj.error.message
100
+ : null);
101
+ return {
102
+ endpoint,
103
+ httpStatus,
104
+ responseBody,
105
+ tests,
106
+ durationMs,
107
+ error,
108
+ };
109
+ }
110
+ //# sourceMappingURL=bru-report-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-report-parser.js","sourceRoot":"","sources":["../../src/api-test/bru-report-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Bruno runner — Phase 5 of the api-test pipeline.
3
+ *
4
+ * Spec contract (T-50):
5
+ * - Invoke `npx @usebruno/cli run <collection-dir>` to fire every assembled
6
+ * `.bru` request against the live target.
7
+ * - Pass `--env <profile.env>` and `--var key=value` for each
8
+ * `profile.variables` entry.
9
+ * - Single-request timeout 30s; failure does not retry; serial execution
10
+ * (MVP — no `--concurrent`).
11
+ * - Parse Bruno CLI's JSON output into one `BrunoRunResult` per request.
12
+ * - Bruno CLI unavailable → throw `BrunoCliError`; the CLI layer turns
13
+ * that into exit code 4 + an install hint on stderr.
14
+ *
15
+ * Independent subsystem — does NOT import from `src/extraction/` /
16
+ * `src/resolution/` / `src/db/`. The Bruno CLI is invoked as a subprocess
17
+ * via `./bru-cli-invoker.ts` (subsystem isolation per spec §12).
18
+ */
19
+ import type { ApiTestProfile } from './types';
20
+ import { type BrunoCliInvocation } from './bru-cli-invoker';
21
+ /** Result of running one endpoint through Bruno CLI. */
22
+ export interface BrunoRunResult {
23
+ /** Endpoint name — matches the `.bru` `meta.name` (i.e. `<svc_id>_<method>`). */
24
+ endpoint: string;
25
+ /** HTTP status code returned by the server. 0 when the request never completed. */
26
+ httpStatus: number;
27
+ /** Raw response body string (for biz-code extraction by the CLI layer). */
28
+ responseBody: string;
29
+ /** Request duration in milliseconds. */
30
+ durationMs: number;
31
+ /** Error tag when the request didn't succeed. `null` on a clean 2xx. */
32
+ error: string | null;
33
+ }
34
+ /**
35
+ * Run every `.bru` request in `collectionDir` against the live target.
36
+ *
37
+ * `filter.service` narrows the run to `<collectionDir>/<service>/` when that
38
+ * subdir exists.
39
+ *
40
+ * Throws `BrunoCliError` when the Bruno CLI cannot be reached — the CLI
41
+ * layer catches that and surfaces exit code 4 + an install hint.
42
+ */
43
+ export declare function runBrunoCollection(collectionDir: string, profile: ApiTestProfile, filter?: {
44
+ service?: string;
45
+ }): Promise<BrunoRunResult[]>;
46
+ /**
47
+ * Build the full argv for `npx @usebruno/cli run <dir> --env ... --var ...`.
48
+ *
49
+ * Uses `--output json` (Bruno CLI ≥1.x supports this; the flag was renamed
50
+ * to `--format json` in some versions — `parseBrunoOutput` handles both
51
+ * shapes, and we pass both flags defensively when Bruno CLI accepts them).
52
+ *
53
+ * `profile.variables` entries are passed as `--var key=value` so Bruno's
54
+ * `{{var}}` template syntax resolves them at request time.
55
+ */
56
+ declare function buildBrunoArgs(baseArgs: string[], collectionDir: string, profile: ApiTestProfile): string[];
57
+ /**
58
+ * Parse Bruno CLI's output into per-request `BrunoRunResult`s.
59
+ *
60
+ * Bruno CLI's JSON output shape varies across versions. This parser handles
61
+ * the known shapes:
62
+ * - JSON array: `[{ name, status, response, duration, error }, ...]`
63
+ * - JSON object with `results` array: `{ results: [...] }`
64
+ * - JSON object with `tests` array: `{ tests: [...] }`
65
+ *
66
+ * Field name aliases covered (best-effort):
67
+ * - name: `name` | `request` | `testName` | `suite`
68
+ * - status: `status` | `statusCode` | `responseStatus`
69
+ * - body: `response` | `data` | `body` | `responseBody`
70
+ * - duration: `duration` | `responseTime` | `elapsed`
71
+ * - error: `error` | `errorMessage` | `err`
72
+ *
73
+ * When the output cannot be parsed as JSON, falls back to a text-scan that
74
+ * extracts `(\\d+)\\s+ms` + status codes — best-effort, marks unparseable
75
+ * requests with `error: bruno_output_parse_failed`.
76
+ */
77
+ declare function parseBrunoOutput(invocation: BrunoCliInvocation): BrunoRunResult[];
78
+ /** Normalize one Bruno result record into a `BrunoRunResult`. */
79
+ declare function normalizeResult(record: unknown): BrunoRunResult;
80
+ /**
81
+ * Best-effort text fallback parser. Bruno CLI's text output looks like:
82
+ * ```
83
+ * Running request: qryOrderList
84
+ * Status: 200, Duration: 234ms
85
+ * Response: {"code":"0"}
86
+ * ```
87
+ * This parser extracts endpoint names + status + duration heuristically.
88
+ * Unparseable lines get `error: bruno_output_parse_failed`.
89
+ */
90
+ declare function parseTextOutput(stdout: string, timedOut: boolean): BrunoRunResult[];
91
+ /** Internal helpers re-exported for tests. */
92
+ export declare const __INTERNAL_BRU_RUNNER: {
93
+ buildBrunoArgs: typeof buildBrunoArgs;
94
+ parseBrunoOutput: typeof parseBrunoOutput;
95
+ parseTextOutput: typeof parseTextOutput;
96
+ normalizeResult: typeof normalizeResult;
97
+ PER_REQUEST_TIMEOUT_MS: number;
98
+ };
99
+ export { invokeBrunoCli, detectBrunoCli, resolveCollectionDir } from './bru-cli-invoker';
100
+ export type { BrunoCliInvocation, InvokeBrunoCliOptions } from './bru-cli-invoker';
101
+ //# sourceMappingURL=bru-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-runner.d.ts","sourceRoot":"","sources":["../../src/api-test/bru-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAO3B,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,cAAc,EAAE,CAAC,CAsB3B;AAED;;;;;;;;;GASG;AACH,iBAAS,cAAc,CACrB,QAAQ,EAAE,MAAM,EAAE,EAClB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,cAAc,GACtB,MAAM,EAAE,CAgBV;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,gBAAgB,CAAC,UAAU,EAAE,kBAAkB,GAAG,cAAc,EAAE,CA2D1E;AAeD,iEAAiE;AACjE,iBAAS,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,CA6BxD;AAgCD;;;;;;;;;GASG;AACH,iBAAS,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,cAAc,EAAE,CAuD5E;AAED,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB;;;;;;CAMjC,CAAC;AAGF,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzF,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ /**
3
+ * Bruno runner — Phase 5 of the api-test pipeline.
4
+ *
5
+ * Spec contract (T-50):
6
+ * - Invoke `npx @usebruno/cli run <collection-dir>` to fire every assembled
7
+ * `.bru` request against the live target.
8
+ * - Pass `--env <profile.env>` and `--var key=value` for each
9
+ * `profile.variables` entry.
10
+ * - Single-request timeout 30s; failure does not retry; serial execution
11
+ * (MVP — no `--concurrent`).
12
+ * - Parse Bruno CLI's JSON output into one `BrunoRunResult` per request.
13
+ * - Bruno CLI unavailable → throw `BrunoCliError`; the CLI layer turns
14
+ * that into exit code 4 + an install hint on stderr.
15
+ *
16
+ * Independent subsystem — does NOT import from `src/extraction/` /
17
+ * `src/resolution/` / `src/db/`. The Bruno CLI is invoked as a subprocess
18
+ * via `./bru-cli-invoker.ts` (subsystem isolation per spec §12).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.resolveCollectionDir = exports.detectBrunoCli = exports.invokeBrunoCli = exports.__INTERNAL_BRU_RUNNER = void 0;
22
+ exports.runBrunoCollection = runBrunoCollection;
23
+ const errors_1 = require("./errors");
24
+ const bru_cli_invoker_1 = require("./bru-cli-invoker");
25
+ /** Per-request timeout: 30s (spec). */
26
+ const PER_REQUEST_TIMEOUT_MS = 30_000;
27
+ /** Hard cap for the whole collection run so a runaway doesn't hang CI. */
28
+ const COLLECTION_TIMEOUT_CAP_MS = 10 * 60 * 1000;
29
+ /**
30
+ * Run every `.bru` request in `collectionDir` against the live target.
31
+ *
32
+ * `filter.service` narrows the run to `<collectionDir>/<service>/` when that
33
+ * subdir exists.
34
+ *
35
+ * Throws `BrunoCliError` when the Bruno CLI cannot be reached — the CLI
36
+ * layer catches that and surfaces exit code 4 + an install hint.
37
+ */
38
+ async function runBrunoCollection(collectionDir, profile, filter) {
39
+ const detected = (0, bru_cli_invoker_1.detectBrunoCli)();
40
+ if (detected === null) {
41
+ throw new errors_1.BrunoCliError('Bruno CLI 不可用。请安装:npm install -g @usebruno/cli\n或在仓库内:npm install @usebruno/cli --save-dev');
42
+ }
43
+ const targetDir = (0, bru_cli_invoker_1.resolveCollectionDir)(collectionDir, filter);
44
+ const args = buildBrunoArgs(detected.baseArgs, targetDir, profile);
45
+ // Per-request timeout × a generous multiplier for the whole collection.
46
+ // Bruno CLI runs requests serially by default, so N requests × 30s is the
47
+ // upper bound. Cap at COLLECTION_TIMEOUT_CAP_MS to avoid runaway hangs.
48
+ const timeoutMs = Math.min(PER_REQUEST_TIMEOUT_MS * 50, // assume ≤50 requests for the multiplier
49
+ COLLECTION_TIMEOUT_CAP_MS);
50
+ const invocation = (0, bru_cli_invoker_1.invokeBrunoCli)(args, { cwd: collectionDir, timeoutMs });
51
+ return parseBrunoOutput(invocation);
52
+ }
53
+ /**
54
+ * Build the full argv for `npx @usebruno/cli run <dir> --env ... --var ...`.
55
+ *
56
+ * Uses `--output json` (Bruno CLI ≥1.x supports this; the flag was renamed
57
+ * to `--format json` in some versions — `parseBrunoOutput` handles both
58
+ * shapes, and we pass both flags defensively when Bruno CLI accepts them).
59
+ *
60
+ * `profile.variables` entries are passed as `--var key=value` so Bruno's
61
+ * `{{var}}` template syntax resolves them at request time.
62
+ */
63
+ function buildBrunoArgs(baseArgs, collectionDir, profile) {
64
+ const args = [...baseArgs, 'run', collectionDir];
65
+ if (profile.env) {
66
+ args.push('--env', profile.env);
67
+ }
68
+ // Pass baseUrl + variables as --var key=value so Bruno's {{var}} resolution
69
+ // picks them up. baseUrl is special-cased because the .bru url uses
70
+ // {{baseUrl}}.
71
+ args.push('--var', `baseUrl=${profile.baseUrl}`);
72
+ for (const [k, v] of Object.entries(profile.variables ?? {})) {
73
+ args.push('--var', `${k}=${v}`);
74
+ }
75
+ // Request both flag spellings — Bruno CLI ignores unknown flags in most
76
+ // versions, and the parser handles whichever shape actually comes back.
77
+ args.push('--output', 'json');
78
+ return args;
79
+ }
80
+ /**
81
+ * Parse Bruno CLI's output into per-request `BrunoRunResult`s.
82
+ *
83
+ * Bruno CLI's JSON output shape varies across versions. This parser handles
84
+ * the known shapes:
85
+ * - JSON array: `[{ name, status, response, duration, error }, ...]`
86
+ * - JSON object with `results` array: `{ results: [...] }`
87
+ * - JSON object with `tests` array: `{ tests: [...] }`
88
+ *
89
+ * Field name aliases covered (best-effort):
90
+ * - name: `name` | `request` | `testName` | `suite`
91
+ * - status: `status` | `statusCode` | `responseStatus`
92
+ * - body: `response` | `data` | `body` | `responseBody`
93
+ * - duration: `duration` | `responseTime` | `elapsed`
94
+ * - error: `error` | `errorMessage` | `err`
95
+ *
96
+ * When the output cannot be parsed as JSON, falls back to a text-scan that
97
+ * extracts `(\\d+)\\s+ms` + status codes — best-effort, marks unparseable
98
+ * requests with `error: bruno_output_parse_failed`.
99
+ */
100
+ function parseBrunoOutput(invocation) {
101
+ const { stdout, stderr, exitCode, timedOut } = invocation;
102
+ // Bruno CLI writes JSON to stdout. If stdout is empty but the process
103
+ // failed, surface the stderr as a single error result.
104
+ const trimmed = stdout.trim();
105
+ if (trimmed.length === 0) {
106
+ if (timedOut) {
107
+ return [{
108
+ endpoint: '(collection)',
109
+ httpStatus: 0,
110
+ responseBody: '',
111
+ durationMs: 0,
112
+ error: 'timeout',
113
+ }];
114
+ }
115
+ if (exitCode !== 0) {
116
+ return [{
117
+ endpoint: '(collection)',
118
+ httpStatus: 0,
119
+ responseBody: '',
120
+ durationMs: 0,
121
+ error: stderr.trim().length > 0 ? `bruno_cli_failed: ${stderr.trim().slice(0, 200)}` : 'bruno_cli_failed',
122
+ }];
123
+ }
124
+ return [];
125
+ }
126
+ // Try JSON parse.
127
+ let parsed;
128
+ try {
129
+ parsed = JSON.parse(trimmed);
130
+ }
131
+ catch {
132
+ // Not JSON — fall back to text scan.
133
+ return parseTextOutput(trimmed, timedOut);
134
+ }
135
+ const records = extractResultArray(parsed);
136
+ if (records.length === 0) {
137
+ // JSON parsed but no recognizable results array — mark as parse failure.
138
+ if (timedOut) {
139
+ return [{
140
+ endpoint: '(collection)',
141
+ httpStatus: 0,
142
+ responseBody: '',
143
+ durationMs: 0,
144
+ error: 'timeout',
145
+ }];
146
+ }
147
+ return [{
148
+ endpoint: '(collection)',
149
+ httpStatus: 0,
150
+ responseBody: trimmed,
151
+ durationMs: 0,
152
+ error: 'bruno_output_parse_failed',
153
+ }];
154
+ }
155
+ return records.map((r) => normalizeResult(r));
156
+ }
157
+ /** Pull the results array out of whichever top-level shape Bruno used. */
158
+ function extractResultArray(parsed) {
159
+ if (Array.isArray(parsed))
160
+ return parsed;
161
+ if (parsed && typeof parsed === 'object') {
162
+ const obj = parsed;
163
+ for (const key of ['results', 'tests', 'requests', 'items']) {
164
+ const v = obj[key];
165
+ if (Array.isArray(v))
166
+ return v;
167
+ }
168
+ }
169
+ return [];
170
+ }
171
+ /** Normalize one Bruno result record into a `BrunoRunResult`. */
172
+ function normalizeResult(record) {
173
+ const r = (record ?? {});
174
+ const endpoint = pickString(r, ['name', 'request', 'testName', 'suite']) ?? '(unknown)';
175
+ const httpStatus = pickNumber(r, ['status', 'statusCode', 'responseStatus']) ?? 0;
176
+ const responseBody = pickString(r, ['response', 'data', 'body', 'responseBody']) ?? '';
177
+ const durationMs = pickNumber(r, ['duration', 'responseTime', 'elapsed']) ?? 0;
178
+ const error = pickString(r, ['error', 'errorMessage', 'err']);
179
+ // Bruno CLI may report a per-request error (e.g. timeout) in the `error`
180
+ // field. Normalize common error strings to our tags.
181
+ let errorTag = null;
182
+ if (typeof error === 'string' && error.length > 0) {
183
+ const lower = error.toLowerCase();
184
+ if (lower.includes('timeout') || lower.includes('timed out')) {
185
+ errorTag = 'timeout';
186
+ }
187
+ else {
188
+ errorTag = `bruno_request_error: ${error.slice(0, 200)}`;
189
+ }
190
+ }
191
+ else if (httpStatus === 0) {
192
+ errorTag = 'bruno_request_failed';
193
+ }
194
+ return {
195
+ endpoint,
196
+ httpStatus,
197
+ responseBody,
198
+ durationMs,
199
+ error: errorTag,
200
+ };
201
+ }
202
+ function pickString(r, keys) {
203
+ for (const k of keys) {
204
+ const v = r[k];
205
+ if (typeof v === 'string' && v.length > 0)
206
+ return v;
207
+ if (v != null && typeof v === 'object') {
208
+ // Response body may be a nested object — stringify it so the CLI layer
209
+ // can extract biz_code via JSON.parse.
210
+ try {
211
+ const s = JSON.stringify(v);
212
+ if (s && s.length > 0)
213
+ return s;
214
+ }
215
+ catch {
216
+ // ignore
217
+ }
218
+ }
219
+ }
220
+ return undefined;
221
+ }
222
+ function pickNumber(r, keys) {
223
+ for (const k of keys) {
224
+ const v = r[k];
225
+ if (typeof v === 'number' && Number.isFinite(v))
226
+ return v;
227
+ if (typeof v === 'string') {
228
+ const n = Number(v);
229
+ if (Number.isFinite(n))
230
+ return n;
231
+ }
232
+ }
233
+ return undefined;
234
+ }
235
+ /**
236
+ * Best-effort text fallback parser. Bruno CLI's text output looks like:
237
+ * ```
238
+ * Running request: qryOrderList
239
+ * Status: 200, Duration: 234ms
240
+ * Response: {"code":"0"}
241
+ * ```
242
+ * This parser extracts endpoint names + status + duration heuristically.
243
+ * Unparseable lines get `error: bruno_output_parse_failed`.
244
+ */
245
+ function parseTextOutput(stdout, timedOut) {
246
+ const results = [];
247
+ const lines = stdout.split(/\r?\n/);
248
+ let current = null;
249
+ for (const line of lines) {
250
+ const nameMatch = /(?:Running request:|Request:)\s*(\S+)/.exec(line);
251
+ if (nameMatch) {
252
+ if (current)
253
+ results.push(current);
254
+ current = {
255
+ endpoint: nameMatch[1] ?? '(unknown)',
256
+ httpStatus: 0,
257
+ responseBody: '',
258
+ durationMs: 0,
259
+ error: null,
260
+ };
261
+ continue;
262
+ }
263
+ if (!current)
264
+ continue;
265
+ const statusMatch = /Status:\s*(\d+)/.exec(line);
266
+ if (statusMatch) {
267
+ current.httpStatus = Number(statusMatch[1] ?? 0);
268
+ continue;
269
+ }
270
+ const durMatch = /Duration:\s*(\d+)/.exec(line) || /(\d+)\s*ms/.exec(line);
271
+ if (durMatch) {
272
+ current.durationMs = Number(durMatch[1] ?? 0);
273
+ continue;
274
+ }
275
+ const respMatch = /Response:\s*(.*)/.exec(line);
276
+ if (respMatch) {
277
+ current.responseBody = respMatch[1] ?? '';
278
+ continue;
279
+ }
280
+ }
281
+ if (current)
282
+ results.push(current);
283
+ if (results.length === 0) {
284
+ if (timedOut) {
285
+ return [{
286
+ endpoint: '(collection)',
287
+ httpStatus: 0,
288
+ responseBody: '',
289
+ durationMs: 0,
290
+ error: 'timeout',
291
+ }];
292
+ }
293
+ return [{
294
+ endpoint: '(collection)',
295
+ httpStatus: 0,
296
+ responseBody: stdout,
297
+ durationMs: 0,
298
+ error: 'bruno_output_parse_failed',
299
+ }];
300
+ }
301
+ return results;
302
+ }
303
+ /** Internal helpers re-exported for tests. */
304
+ exports.__INTERNAL_BRU_RUNNER = {
305
+ buildBrunoArgs,
306
+ parseBrunoOutput,
307
+ parseTextOutput,
308
+ normalizeResult,
309
+ PER_REQUEST_TIMEOUT_MS,
310
+ };
311
+ // Re-export the invoker surface so callers can import everything from one place.
312
+ var bru_cli_invoker_2 = require("./bru-cli-invoker");
313
+ Object.defineProperty(exports, "invokeBrunoCli", { enumerable: true, get: function () { return bru_cli_invoker_2.invokeBrunoCli; } });
314
+ Object.defineProperty(exports, "detectBrunoCli", { enumerable: true, get: function () { return bru_cli_invoker_2.detectBrunoCli; } });
315
+ Object.defineProperty(exports, "resolveCollectionDir", { enumerable: true, get: function () { return bru_cli_invoker_2.resolveCollectionDir; } });
316
+ //# sourceMappingURL=bru-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-runner.js","sourceRoot":"","sources":["../../src/api-test/bru-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Bruno `.bru` file writer.
3
+ *
4
+ * Spec §5.2 template — emits one `.bru` per endpoint. The HTTP method block
5
+ * keyword (`post {` / `get {` / `put {` / `delete {` / `patch {`) reflects
6
+ * `endpoint.httpMethod` (lowercased). Default body is `[{}]` — the assemble
7
+ * phase (T-40) fills it in; scan phase writes the placeholder.
8
+ *
9
+ * Header values from `config.defaultHeaders` are written verbatim, INCLUDING
10
+ * any `{{var}}` placeholders — Bruno resolves those at request time from the
11
+ * collection environment. Per spec §9, scan/analyze/assemble do NOT load the
12
+ * profile (only run does) — Bruno's `{{var}}` syntax does the substitution
13
+ * natively at request time.
14
+ *
15
+ * Public surface:
16
+ * - `renderBru(endpoint, config)` — string
17
+ * - `writeBru(endpoint, config, outputDir)` — Promise<string> (written path)
18
+ */
19
+ import type { ApiTestConfig, Endpoint } from './types';
20
+ /** Filename for an endpoint's `.bru` pair: `<svc_id>_<methodName>.bru`. */
21
+ export declare function bruFileName(endpoint: Endpoint): string;
22
+ /** Subdirectory under `outputDir` for an endpoint's service: `<service>/`. */
23
+ export declare function serviceDirName(endpoint: Endpoint): string;
24
+ /**
25
+ * Render the `.bru` body for one endpoint, following spec §5.2.
26
+ *
27
+ * The HTTP method keyword is derived from `endpoint.httpMethod`
28
+ * (lowercased). If the method is unknown (not get/post/put/delete/patch),
29
+ * fall back to `post` (the hx default) and emit a comment.
30
+ */
31
+ export declare function renderBru(endpoint: Endpoint, config: ApiTestConfig): string;
32
+ /**
33
+ * Write an endpoint's `.bru` to `<outputDir>/<service>/<svc_id>_<methodName>.bru`.
34
+ * Creates the service directory if needed. Returns the absolute written path.
35
+ *
36
+ * Also stamps the written path back onto `endpoint.bruPath` so the report
37
+ * phase can reference it.
38
+ */
39
+ export declare function writeBru(endpoint: Endpoint, config: ApiTestConfig, outputDir: string): Promise<string>;
40
+ /** Map an HTTP method string to Bruno's lowercase block key. `null` = unknown. */
41
+ declare function normalizeHttpMethodKey(method: string): string | null;
42
+ /** First non-empty line of a javadoc string, trimmed. */
43
+ declare function firstLine(javadoc: string): string;
44
+ /** Internal helpers re-exported for tests. */
45
+ export declare const __INTERNAL_BRU: {
46
+ bruFileName: typeof bruFileName;
47
+ serviceDirName: typeof serviceDirName;
48
+ normalizeHttpMethodKey: typeof normalizeHttpMethodKey;
49
+ firstLine: typeof firstLine;
50
+ };
51
+ export {};
52
+ //# sourceMappingURL=bru-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-writer.d.ts","sourceRoot":"","sources":["../../src/api-test/bru-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEvD,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEtD;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,GACpB,MAAM,CAqDR;AAED;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,kFAAkF;AAClF,iBAAS,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM7D;AAED,yDAAyD;AACzD,iBAAS,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1C;AAED,8CAA8C;AAC9C,eAAO,MAAM,cAAc;;;;;CAK1B,CAAC"}