cgraphx 1.2.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/dist/.claude-template/hooks/precommit-check/precommit-check.cjs +90 -0
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +99 -78
- 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/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/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/package.json +1 -1
- package/scripts/agent-eval/block-cgraphx-and-gitnexus-cli-hook.sh +0 -43
- package/scripts/agent-eval/block-cgraphx-cli-hook.sh +0 -32
- package/scripts/agent-eval/block-cgraphx-cli-settings.json +0 -16
- package/scripts/agent-eval/cli-vs-mcp-3arm.sh +0 -121
- package/scripts/agent-eval/multi-tool-eval.sh +0 -171
- package/scripts/agent-eval/parse-cli-vs-mcp.mjs +0 -232
- package/scripts/agent-eval/parse-multi-tool.mjs +0 -242
|
@@ -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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bruno `.bru` file writer.
|
|
4
|
+
*
|
|
5
|
+
* Spec §5.2 template — emits one `.bru` per endpoint. The HTTP method block
|
|
6
|
+
* keyword (`post {` / `get {` / `put {` / `delete {` / `patch {`) reflects
|
|
7
|
+
* `endpoint.httpMethod` (lowercased). Default body is `[{}]` — the assemble
|
|
8
|
+
* phase (T-40) fills it in; scan phase writes the placeholder.
|
|
9
|
+
*
|
|
10
|
+
* Header values from `config.defaultHeaders` are written verbatim, INCLUDING
|
|
11
|
+
* any `{{var}}` placeholders — Bruno resolves those at request time from the
|
|
12
|
+
* collection environment. Per spec §9, scan/analyze/assemble do NOT load the
|
|
13
|
+
* profile (only run does) — Bruno's `{{var}}` syntax does the substitution
|
|
14
|
+
* natively at request time.
|
|
15
|
+
*
|
|
16
|
+
* Public surface:
|
|
17
|
+
* - `renderBru(endpoint, config)` — string
|
|
18
|
+
* - `writeBru(endpoint, config, outputDir)` — Promise<string> (written path)
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.__INTERNAL_BRU = void 0;
|
|
55
|
+
exports.bruFileName = bruFileName;
|
|
56
|
+
exports.serviceDirName = serviceDirName;
|
|
57
|
+
exports.renderBru = renderBru;
|
|
58
|
+
exports.writeBru = writeBru;
|
|
59
|
+
const fs = __importStar(require("node:fs"));
|
|
60
|
+
const path = __importStar(require("node:path"));
|
|
61
|
+
/** Filename for an endpoint's `.bru` pair: `<svc_id>_<methodName>.bru`. */
|
|
62
|
+
function bruFileName(endpoint) {
|
|
63
|
+
return `${endpoint.svcId}_${endpoint.method}.bru`;
|
|
64
|
+
}
|
|
65
|
+
/** Subdirectory under `outputDir` for an endpoint's service: `<service>/`. */
|
|
66
|
+
function serviceDirName(endpoint) {
|
|
67
|
+
return endpoint.service;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Render the `.bru` body for one endpoint, following spec §5.2.
|
|
71
|
+
*
|
|
72
|
+
* The HTTP method keyword is derived from `endpoint.httpMethod`
|
|
73
|
+
* (lowercased). If the method is unknown (not get/post/put/delete/patch),
|
|
74
|
+
* fall back to `post` (the hx default) and emit a comment.
|
|
75
|
+
*/
|
|
76
|
+
function renderBru(endpoint, config) {
|
|
77
|
+
const methodKey = normalizeHttpMethodKey(endpoint.httpMethod);
|
|
78
|
+
const metaName = `${endpoint.svcId}_${endpoint.method}`;
|
|
79
|
+
const url = endpoint.urlPattern;
|
|
80
|
+
const headerLines = Object.entries(config.defaultHeaders).map(([k, v]) => ` ${k}: ${v}`);
|
|
81
|
+
const headersBlock = headerLines.length > 0
|
|
82
|
+
? `headers {\n${headerLines.join('\n')}\n}`
|
|
83
|
+
: 'headers {}';
|
|
84
|
+
const bodyTemplate = config.bodyTemplate || '[{}]';
|
|
85
|
+
const descLine = endpoint.javadoc
|
|
86
|
+
? firstLine(endpoint.javadoc)
|
|
87
|
+
: '(scan 阶段未提取到 Javadoc,请补充业务说明)';
|
|
88
|
+
// The HTTP method block keyword — Bruno's grammar uses the lowercase method
|
|
89
|
+
// name as the block key. We emit the matched key; if the configured method
|
|
90
|
+
// is non-standard, fall back to `post` and add a `docs` note.
|
|
91
|
+
const methodBlockKey = methodKey ?? 'post';
|
|
92
|
+
const methodFallbackNote = methodKey === null
|
|
93
|
+
? `\n # NOTE: endpoint.httpMethod="${endpoint.httpMethod}" is non-standard; defaulting to post.`
|
|
94
|
+
: '';
|
|
95
|
+
return `meta {
|
|
96
|
+
name: ${metaName}
|
|
97
|
+
type: http
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
${methodBlockKey} {${methodFallbackNote}
|
|
101
|
+
url: ${url}
|
|
102
|
+
body: json
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
${headersBlock}
|
|
106
|
+
|
|
107
|
+
body:json {
|
|
108
|
+
${bodyTemplate}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
docs {
|
|
112
|
+
业务说明: ${descLine}
|
|
113
|
+
完整分析见同名 .md 文件
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
tests {
|
|
117
|
+
test("HTTP 200", function() {
|
|
118
|
+
expect(res.getStatus()).to.equal(200);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Write an endpoint's `.bru` to `<outputDir>/<service>/<svc_id>_<methodName>.bru`.
|
|
125
|
+
* Creates the service directory if needed. Returns the absolute written path.
|
|
126
|
+
*
|
|
127
|
+
* Also stamps the written path back onto `endpoint.bruPath` so the report
|
|
128
|
+
* phase can reference it.
|
|
129
|
+
*/
|
|
130
|
+
async function writeBru(endpoint, config, outputDir) {
|
|
131
|
+
const serviceDir = path.join(outputDir, serviceDirName(endpoint));
|
|
132
|
+
await fs.promises.mkdir(serviceDir, { recursive: true });
|
|
133
|
+
const filePath = path.join(serviceDir, bruFileName(endpoint));
|
|
134
|
+
const content = renderBru(endpoint, config);
|
|
135
|
+
await fs.promises.writeFile(filePath, content, 'utf8');
|
|
136
|
+
endpoint.bruPath = filePath;
|
|
137
|
+
return filePath;
|
|
138
|
+
}
|
|
139
|
+
/** Map an HTTP method string to Bruno's lowercase block key. `null` = unknown. */
|
|
140
|
+
function normalizeHttpMethodKey(method) {
|
|
141
|
+
const m = method.trim().toLowerCase();
|
|
142
|
+
if (m === 'get' || m === 'post' || m === 'put' || m === 'delete' || m === 'patch') {
|
|
143
|
+
return m;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
/** First non-empty line of a javadoc string, trimmed. */
|
|
148
|
+
function firstLine(javadoc) {
|
|
149
|
+
const line = javadoc.split(/\r?\n/).map((s) => s.trim()).find((s) => s.length > 0);
|
|
150
|
+
return line ?? '';
|
|
151
|
+
}
|
|
152
|
+
/** Internal helpers re-exported for tests. */
|
|
153
|
+
exports.__INTERNAL_BRU = {
|
|
154
|
+
bruFileName,
|
|
155
|
+
serviceDirName,
|
|
156
|
+
normalizeHttpMethodKey,
|
|
157
|
+
firstLine,
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=bru-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bru-writer.js","sourceRoot":"","sources":["../../src/api-test/bru-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyze phase — extract the call chain, dependency tables, and auth flag
|
|
3
|
+
* from an impl method body.
|
|
4
|
+
*
|
|
5
|
+
* Spec contract (T-30):
|
|
6
|
+
* - Call chain: every `someService.method()` invocation in the body
|
|
7
|
+
* (first-level callees only — no recursion into callees of callees).
|
|
8
|
+
* At least 1 level is expected (impl → first callee).
|
|
9
|
+
* - Dep tables: inferred from Mapper calls (`xxxMapper.selectXxx` etc.)
|
|
10
|
+
* by stripping the `Mapper` suffix from the receiver's declared field
|
|
11
|
+
* type (or receiver name) and lowercasing.
|
|
12
|
+
* - Auth required: true when the impl calls `IAuthInfoService` (field type
|
|
13
|
+
* or receiver name starting with `iAuth` / `IAuthInfoService`) or reads
|
|
14
|
+
* the staffcode header (`ServerHeadersHolder.getStaffcode()` etc.).
|
|
15
|
+
*
|
|
16
|
+
* Implementation note: extraction uses `method.bodyText` (regex) rather than
|
|
17
|
+
* the AST `body` node. The T-11 parser deletes the tree after extraction to
|
|
18
|
+
* release WASM heap, which would invalidate any retained AST node reference;
|
|
19
|
+
* `bodyText` is captured before the delete and stays valid. The task's
|
|
20
|
+
* "允许局部适配" clause permits this regex-based approach.
|
|
21
|
+
*
|
|
22
|
+
* Self-contained: depends only on `./java-parser` (ClassInfo, MethodSignature).
|
|
23
|
+
* No imports from `src/extraction/` / `src/resolution/` / `src/db/`.
|
|
24
|
+
*
|
|
25
|
+
* Public surface:
|
|
26
|
+
* - `extractCallChain(implInfo, method)` —
|
|
27
|
+
* Promise<{ callChain: string[]; depTables: string[]; authRequired: boolean }>
|
|
28
|
+
*/
|
|
29
|
+
import type { ClassInfo, MethodSignature } from './java-parser';
|
|
30
|
+
/**
|
|
31
|
+
* Extract `receiver.methodName` strings from the body text. Dedupes by
|
|
32
|
+
* the full `receiver.method` pair, preserving document order.
|
|
33
|
+
*/
|
|
34
|
+
declare function extractCallChainFromBody(bodyText: string): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Infer dependency table names from Mapper-typed calls in the body.
|
|
37
|
+
*
|
|
38
|
+
* Heuristic:
|
|
39
|
+
* 1. For each `xxxMapper.method()` call in the body, look up the field
|
|
40
|
+
* `xxxMapper` on the impl class. If its type ends with `Mapper`,
|
|
41
|
+
* strip the suffix and lowercase → table name.
|
|
42
|
+
* 2. Fallback: if the receiver name itself ends with `Mapper` (e.g.
|
|
43
|
+
* `orderMapper`), strip + lowercase.
|
|
44
|
+
*
|
|
45
|
+
* Returns deduped table names in document order.
|
|
46
|
+
*/
|
|
47
|
+
declare function inferDepTables(bodyText: string, implInfo: ClassInfo): string[];
|
|
48
|
+
/**
|
|
49
|
+
* Detect whether the impl method touches the auth surface.
|
|
50
|
+
*
|
|
51
|
+
* Signals (any one → true):
|
|
52
|
+
* - The impl class has a field whose type simple-name is `IAuthInfoService`
|
|
53
|
+
* (or whose name starts with `iAuth`).
|
|
54
|
+
* - The body text references `IAuthInfoService`, `getStaffcode`, or
|
|
55
|
+
* `ServerHeadersHolder`.
|
|
56
|
+
*
|
|
57
|
+
* The field-type check catches `@Resource IAuthInfoService iAuthService;`
|
|
58
|
+
* even when the body only calls `iAuthService.check()` (receiver name
|
|
59
|
+
* doesn't carry the type). The body-text check catches direct
|
|
60
|
+
* `ServerHeadersHolder.getStaffcode()` calls without a field.
|
|
61
|
+
*/
|
|
62
|
+
declare function detectAuthRequired(bodyText: string, implInfo: ClassInfo): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Extract the call chain, dependency tables, and auth-required flag for
|
|
65
|
+
* one impl method. Returns empty arrays + `authRequired: false` when the
|
|
66
|
+
* method has no body (interface/abstract method — shouldn't happen for an
|
|
67
|
+
* impl, but defensive per T-11's empty-bodyText caveat).
|
|
68
|
+
*/
|
|
69
|
+
export declare function extractCallChain(implInfo: ClassInfo, method: MethodSignature): Promise<{
|
|
70
|
+
callChain: string[];
|
|
71
|
+
depTables: string[];
|
|
72
|
+
authRequired: boolean;
|
|
73
|
+
}>;
|
|
74
|
+
export declare const __INTERNAL_CALL_CHAIN: {
|
|
75
|
+
extractCallChainFromBody: typeof extractCallChainFromBody;
|
|
76
|
+
inferDepTables: typeof inferDepTables;
|
|
77
|
+
detectAuthRequired: typeof detectAuthRequired;
|
|
78
|
+
};
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=call-chain-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-chain-extractor.d.ts","sourceRoot":"","sources":["../../src/api-test/call-chain-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAoBhE;;;GAGG;AACH,iBAAS,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB5D;AAMD;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CACrB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,SAAS,GAClB,MAAM,EAAE,CA8BV;AAMD;;;;;;;;;;;;;GAaG;AACH,iBAAS,kBAAkB,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,SAAS,GAClB,OAAO,CAkBT;AAMD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC,CAU9E;AAGD,eAAO,MAAM,qBAAqB;;;;CAIjC,CAAC"}
|