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.
- package/README.md +0 -1
- package/dist/.claude-template/hooks/precommit-check/precommit-check.cjs +90 -0
- package/dist/.claude-template/skills/cgraphx/SKILL.md +3 -3
- package/dist/.claude-template/skills/cgraphx/agent-prompt.md +1 -1
- package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +94 -0
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +424 -0
- package/dist/.claude-template/skills/clarify-requirements/SKILL.md +19 -8
- package/dist/.claude-template/skills/code-impact-docgen/SKILL.md +186 -176
- package/dist/.claude-template/skills/code-impact-docgen/template-design-html.md +357 -0
- package/dist/.claude-template/skills/code-impact-docgen/template-design-md.md +164 -0
- package/dist/.claude-template/skills/code-impact-init/SKILL.md +47 -47
- package/dist/.claude-template/skills/developer-timeline/SKILL.md +9 -0
- package/dist/.claude-template/skills/precommit-review/SKILL.md +50 -0
- package/dist/.claude-template/skills/run-api-test/SKILL.md +187 -0
- package/dist/.claude-template/skills/run-api-test/assets/template-test-report.md +103 -0
- package/dist/.claude-template/skills/run-api-test/assets/template-test-verify.jsonl +5 -0
- package/dist/.claude-template/skills/run-api-test/references/bru-run.md +60 -0
- package/dist/.claude-template/skills/run-api-test/references/db-verification.md +81 -0
- package/dist/.claude-template/skills/run-api-test/references/report-format.md +104 -0
- package/dist/.claude-template/skills/run-api-test/references/service-readiness.md +61 -0
- package/dist/.claude-template/skills/run-api-test/references/test-scope.md +64 -0
- package/dist/.claude-template/skills/write-api/SKILL.md +150 -0
- package/dist/.claude-template/skills/write-api/assets/template-api-spec.md +112 -0
- package/dist/.claude-template/skills/write-api/assets/template-request.bru +75 -0
- package/dist/.claude-template/skills/write-api/references/ai-prompts.md +133 -0
- package/dist/.claude-template/skills/write-api/references/api-spec-format.md +108 -0
- package/dist/.claude-template/skills/write-api/references/bru-format.md +144 -0
- package/dist/.claude-template/skills/write-api/references/collection-layout.md +81 -0
- package/dist/.claude-template/skills/write-api/references/environment-setup.md +105 -0
- package/dist/.claude-template/skills/write-api/references/interface-scope.md +74 -0
- package/dist/.claude-template/skills/write-api-doc/SKILL.md +317 -0
- package/dist/.claude-template/skills/write-api-doc/template-api-html.md +422 -0
- package/dist/.claude-template/skills/write-plan/SKILL.md +38 -16
- package/dist/.claude-template/skills/write-prd/SKILL.md +32 -8
- package/dist/.claude-template/skills/write-spec/SKILL.md +34 -9
- package/dist/api-test/ai-fields.d.ts +37 -0
- package/dist/api-test/ai-fields.d.ts.map +1 -0
- package/dist/api-test/ai-fields.js +114 -0
- package/dist/api-test/ai-fields.js.map +1 -0
- package/dist/api-test/assemble.d.ts +76 -0
- package/dist/api-test/assemble.d.ts.map +1 -0
- package/dist/api-test/assemble.js +185 -0
- package/dist/api-test/assemble.js.map +1 -0
- package/dist/api-test/bru-cli-invoker.d.ts +72 -0
- package/dist/api-test/bru-cli-invoker.d.ts.map +1 -0
- package/dist/api-test/bru-cli-invoker.js +169 -0
- package/dist/api-test/bru-cli-invoker.js.map +1 -0
- package/dist/api-test/bru-report-parser.d.ts +24 -0
- package/dist/api-test/bru-report-parser.d.ts.map +1 -0
- package/dist/api-test/bru-report-parser.js +110 -0
- package/dist/api-test/bru-report-parser.js.map +1 -0
- package/dist/api-test/bru-runner.d.ts +101 -0
- package/dist/api-test/bru-runner.d.ts.map +1 -0
- package/dist/api-test/bru-runner.js +316 -0
- package/dist/api-test/bru-runner.js.map +1 -0
- package/dist/api-test/bru-writer.d.ts +52 -0
- package/dist/api-test/bru-writer.d.ts.map +1 -0
- package/dist/api-test/bru-writer.js +159 -0
- package/dist/api-test/bru-writer.js.map +1 -0
- package/dist/api-test/call-chain-extractor.d.ts +80 -0
- package/dist/api-test/call-chain-extractor.d.ts.map +1 -0
- package/dist/api-test/call-chain-extractor.js +179 -0
- package/dist/api-test/call-chain-extractor.js.map +1 -0
- package/dist/api-test/cli.d.ts +133 -0
- package/dist/api-test/cli.d.ts.map +1 -0
- package/dist/api-test/cli.js +1009 -0
- package/dist/api-test/cli.js.map +1 -0
- package/dist/api-test/config.d.ts +75 -0
- package/dist/api-test/config.d.ts.map +1 -0
- package/dist/api-test/config.js +406 -0
- package/dist/api-test/config.js.map +1 -0
- package/dist/api-test/db-query-cli.d.ts +51 -0
- package/dist/api-test/db-query-cli.d.ts.map +1 -0
- package/dist/api-test/db-query-cli.js +119 -0
- package/dist/api-test/db-query-cli.js.map +1 -0
- package/dist/api-test/enhance-prepare.d.ts +111 -0
- package/dist/api-test/enhance-prepare.d.ts.map +1 -0
- package/dist/api-test/enhance-prepare.js +425 -0
- package/dist/api-test/enhance-prepare.js.map +1 -0
- package/dist/api-test/enhance-write.d.ts +28 -0
- package/dist/api-test/enhance-write.d.ts.map +1 -0
- package/dist/api-test/enhance-write.js +145 -0
- package/dist/api-test/enhance-write.js.map +1 -0
- package/dist/api-test/errors.d.ts +48 -0
- package/dist/api-test/errors.d.ts.map +1 -0
- package/dist/api-test/errors.js +76 -0
- package/dist/api-test/errors.js.map +1 -0
- package/dist/api-test/field-extractor.d.ts +98 -0
- package/dist/api-test/field-extractor.d.ts.map +1 -0
- package/dist/api-test/field-extractor.js +327 -0
- package/dist/api-test/field-extractor.js.map +1 -0
- package/dist/api-test/impl-finder.d.ts +37 -0
- package/dist/api-test/impl-finder.d.ts.map +1 -0
- package/dist/api-test/impl-finder.js +54 -0
- package/dist/api-test/impl-finder.js.map +1 -0
- package/dist/api-test/index.d.ts +41 -0
- package/dist/api-test/index.d.ts.map +1 -0
- package/dist/api-test/index.js +124 -0
- package/dist/api-test/index.js.map +1 -0
- package/dist/api-test/java-parser.d.ts +89 -0
- package/dist/api-test/java-parser.d.ts.map +1 -0
- package/dist/api-test/java-parser.js +508 -0
- package/dist/api-test/java-parser.js.map +1 -0
- package/dist/api-test/md-writer.d.ts +49 -0
- package/dist/api-test/md-writer.d.ts.map +1 -0
- package/dist/api-test/md-writer.js +202 -0
- package/dist/api-test/md-writer.js.map +1 -0
- package/dist/api-test/parser-httpservice.d.ts +91 -0
- package/dist/api-test/parser-httpservice.d.ts.map +1 -0
- package/dist/api-test/parser-httpservice.js +271 -0
- package/dist/api-test/parser-httpservice.js.map +1 -0
- package/dist/api-test/report.d.ts +188 -0
- package/dist/api-test/report.d.ts.map +1 -0
- package/dist/api-test/report.js +522 -0
- package/dist/api-test/report.js.map +1 -0
- package/dist/api-test/snapshot.d.ts +26 -0
- package/dist/api-test/snapshot.d.ts.map +1 -0
- package/dist/api-test/snapshot.js +150 -0
- package/dist/api-test/snapshot.js.map +1 -0
- package/dist/api-test/test-history.d.ts +48 -0
- package/dist/api-test/test-history.d.ts.map +1 -0
- package/dist/api-test/test-history.js +122 -0
- package/dist/api-test/test-history.js.map +1 -0
- package/dist/api-test/types.d.ts +174 -0
- package/dist/api-test/types.d.ts.map +1 -0
- package/dist/api-test/types.js +13 -0
- package/dist/api-test/types.js.map +1 -0
- package/dist/api-test/verify-prepare.d.ts +30 -0
- package/dist/api-test/verify-prepare.d.ts.map +1 -0
- package/dist/api-test/verify-prepare.js +150 -0
- package/dist/api-test/verify-prepare.js.map +1 -0
- package/dist/api-test/verify-write.d.ts +31 -0
- package/dist/api-test/verify-write.d.ts.map +1 -0
- package/dist/api-test/verify-write.js +159 -0
- package/dist/api-test/verify-write.js.map +1 -0
- package/dist/bin/codegraph.js +0 -100
- package/dist/bin/codegraph.js.map +1 -1
- package/dist/dbquery/dump-schema.d.ts +46 -0
- package/dist/dbquery/dump-schema.d.ts.map +1 -0
- package/dist/dbquery/dump-schema.js +379 -0
- package/dist/dbquery/dump-schema.js.map +1 -0
- package/dist/installer/targets/claude.d.ts +15 -0
- package/dist/installer/targets/claude.d.ts.map +1 -1
- package/dist/installer/targets/claude.js +53 -0
- package/dist/installer/targets/claude.js.map +1 -1
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +13 -0
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/scope-index.d.ts +86 -0
- package/dist/resolution/scope-index.d.ts.map +1 -0
- package/dist/resolution/scope-index.js +143 -0
- package/dist/resolution/scope-index.js.map +1 -0
- package/dist/resolution/stdlib-blocklist.d.ts +53 -0
- package/dist/resolution/stdlib-blocklist.d.ts.map +1 -0
- package/dist/resolution/stdlib-blocklist.js +143 -0
- package/dist/resolution/stdlib-blocklist.js.map +1 -0
- package/dist/search/ast-helpers.d.ts +42 -0
- package/dist/search/ast-helpers.d.ts.map +1 -0
- package/dist/search/ast-helpers.js +106 -0
- package/dist/search/ast-helpers.js.map +1 -0
- package/dist/search/call-sites.d.ts +398 -0
- package/dist/search/call-sites.d.ts.map +1 -0
- package/dist/search/call-sites.js +1433 -0
- package/dist/search/call-sites.js.map +1 -0
- package/dist/search/context.d.ts +134 -0
- package/dist/search/context.d.ts.map +1 -0
- package/dist/search/context.js +575 -0
- package/dist/search/context.js.map +1 -0
- package/dist/search/impact.d.ts +139 -0
- package/dist/search/impact.d.ts.map +1 -0
- package/dist/search/impact.js +646 -0
- package/dist/search/impact.js.map +1 -0
- package/dist/search/related.d.ts +178 -0
- package/dist/search/related.d.ts.map +1 -0
- package/dist/search/related.js +667 -0
- package/dist/search/related.js.map +1 -0
- package/dist/search/slice.d.ts +148 -0
- package/dist/search/slice.d.ts.map +1 -0
- package/dist/search/slice.js +460 -0
- package/dist/search/slice.js.map +1 -0
- package/dist/search/snr-constants.d.ts +41 -0
- package/dist/search/snr-constants.d.ts.map +1 -0
- package/dist/search/snr-constants.js +44 -0
- package/dist/search/snr-constants.js.map +1 -0
- package/dist/search/types.d.ts +28 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/search/types.js +12 -0
- package/dist/search/types.js.map +1 -0
- package/dist/timeline/cli.d.ts.map +1 -1
- package/dist/timeline/cli.js +22 -3
- package/dist/timeline/cli.js.map +1 -1
- package/dist/timeline/store.d.ts +5 -0
- package/dist/timeline/store.d.ts.map +1 -1
- package/dist/timeline/store.js +23 -3
- package/dist/timeline/store.js.map +1 -1
- package/package.json +1 -1
- package/scripts/agent-eval/subagent-token-cost.py +188 -0
- package/dist/.claude-template/skills/code-impact-docgen/template-business-html.md +0 -242
- package/dist/.claude-template/skills/code-impact-docgen/template-business-md.md +0 -107
- package/dist/.claude-template/skills/code-impact-docgen/template-technical-html.md +0 -205
- 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"}
|