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 @@
1
+ {"version":3,"file":"ai-fields.d.ts","sourceRoot":"","sources":["../../src/api-test/ai-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE/C,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CA0BnF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAS1F;AAED,8CAA8C;AAC9C,eAAO,MAAM,oBAAoB;;CAEhC,CAAC"}
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * AI-inferred fields persistence (L-T-10).
4
+ *
5
+ * `enhance-prepare` reads any previously-written AI fields so a re-run keeps
6
+ * the AI's prior inference unless the human regenerates it. `enhance-write`
7
+ * (L-T-20) writes this file after the AI infers fields.
8
+ *
9
+ * Storage: `.cgraphx/api-test/ai-fields/<endpoint>.json` where `<endpoint>`
10
+ * is the .md filename stem (`<svc_id>_<method>`). One file per endpoint so
11
+ * partial reruns only touch their own file.
12
+ *
13
+ * Independent subsystem — only depends on `node:fs`/`node:path` and the
14
+ * `AiFields` / `Field` types.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.__INTERNAL_AI_FIELDS = exports.AI_FIELDS_SUBDIR = void 0;
51
+ exports.aiFieldsFilePath = aiFieldsFilePath;
52
+ exports.readAiFields = readAiFields;
53
+ exports.writeAiFields = writeAiFields;
54
+ const fs = __importStar(require("node:fs"));
55
+ const path = __importStar(require("node:path"));
56
+ /** Subdir under `.cgraphx/api-test/` holding the per-endpoint JSON files. */
57
+ exports.AI_FIELDS_SUBDIR = 'ai-fields';
58
+ /**
59
+ * Resolve the JSON path for an endpoint: `<projectRoot>/.cgraphx/api-test/
60
+ * ai-fields/<endpoint>.json`.
61
+ */
62
+ function aiFieldsFilePath(projectRoot, endpoint) {
63
+ return path.join(projectRoot, '.cgraphx', 'api-test', exports.AI_FIELDS_SUBDIR, `${endpoint}.json`);
64
+ }
65
+ /**
66
+ * Read previously-persisted AI fields for `endpoint`. Returns `null` when the
67
+ * file doesn't exist or can't be parsed (treat as "no prior inference").
68
+ */
69
+ function readAiFields(projectRoot, endpoint) {
70
+ const file = aiFieldsFilePath(projectRoot, endpoint);
71
+ let content;
72
+ try {
73
+ content = fs.readFileSync(file, 'utf8');
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ try {
79
+ const parsed = JSON.parse(content);
80
+ if (typeof parsed !== 'object' ||
81
+ parsed === null ||
82
+ typeof parsed.endpoint !== 'string' ||
83
+ !Array.isArray(parsed.fields)) {
84
+ return null;
85
+ }
86
+ return {
87
+ endpoint: parsed.endpoint,
88
+ fields: parsed.fields,
89
+ updatedAt: typeof parsed.updatedAt === 'string' ? parsed.updatedAt : '',
90
+ };
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ /**
97
+ * Persist AI-inferred fields for `endpoint`. Creates the parent directory if
98
+ * needed. `updatedAt` is stamped with the current ISO timestamp.
99
+ */
100
+ function writeAiFields(projectRoot, endpoint, fields) {
101
+ const file = aiFieldsFilePath(projectRoot, endpoint);
102
+ fs.mkdirSync(path.dirname(file), { recursive: true });
103
+ const payload = {
104
+ endpoint,
105
+ fields,
106
+ updatedAt: new Date().toISOString(),
107
+ };
108
+ fs.writeFileSync(file, JSON.stringify(payload, null, 2) + '\n', 'utf8');
109
+ }
110
+ /** Internal helpers re-exported for tests. */
111
+ exports.__INTERNAL_AI_FIELDS = {
112
+ AI_FIELDS_SUBDIR: exports.AI_FIELDS_SUBDIR,
113
+ };
114
+ //# sourceMappingURL=ai-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-fields.js","sourceRoot":"","sources":["../../src/api-test/ai-fields.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcH,4CAEC;AAMD,oCA0BC;AAMD,sCASC;AA7DD,4CAA8B;AAC9B,gDAAkC;AAIlC,6EAA6E;AAChE,QAAA,gBAAgB,GAAG,WAAW,CAAC;AAE5C;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,WAAmB,EAAE,QAAgB;IACpE,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,wBAAgB,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;AAC9F,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,WAAmB,EAAE,QAAgB;IAChE,MAAM,IAAI,GAAG,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAC;QACxD,IACE,OAAO,MAAM,KAAK,QAAQ;YAC1B,MAAM,KAAK,IAAI;YACf,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YACnC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAC7B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,MAAM,CAAC,MAAiB;YAChC,SAAS,EAAE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;SACxE,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,WAAmB,EAAE,QAAgB,EAAE,MAAe;IAClF,MAAM,IAAI,GAAG,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACrD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,OAAO,GAAa;QACxB,QAAQ;QACR,MAAM;QACN,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1E,CAAC;AAED,8CAA8C;AACjC,QAAA,oBAAoB,GAAG;IAClC,gBAAgB,EAAhB,wBAAgB;CACjB,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Assemble phase — source real parameter values for each endpoint and write
3
+ * them into the `.bru` `body:json` block.
4
+ *
5
+ * Spec contract (T-40): three-tier fallback per endpoint:
6
+ * 1. fixture hit → use `fixtures[<svc_id>_<methodName>]`.params verbatim.
7
+ * 2. db-query → for each Field with `sourceTable`, run
8
+ * `cgraphx db query --sql "SELECT ... LIMIT 1" --format json`
9
+ * via the dist binary (subprocess). `literal` fields are
10
+ * filled directly; `fixtureOnly` fields require a fixture
11
+ * (else the endpoint is skip).
12
+ * 3. skip → db-query CLI failure → `db_query_failed`;
13
+ * any field missing source/literal/fixture → `assemble_failed`.
14
+ *
15
+ * Independent subsystem — does NOT import from `src/dbquery/`. The db-query
16
+ * CLI is invoked as a subprocess (subsystem isolation per spec §12). The
17
+ * subprocess wrapper lives in `./db-query-cli.ts` so tests can `vi.spyOn`
18
+ * the export and intercept calls from this module.
19
+ *
20
+ * Public surface:
21
+ * - `assembleEndpoint(endpoint, config, fixtures, projectRoot)`
22
+ * → `{ body, skipped, reason }`
23
+ * - `writeBruBody(bruPath, body)` — patches a `.bru` file's `body:json` block.
24
+ * - `replaceBodyJsonBlock(bruContent, newBody)` — pure-string transform.
25
+ * - re-exports `runDbQueryCli` / `DbQueryResult` from `./db-query-cli` so
26
+ * callers can import everything from one place.
27
+ */
28
+ import type { ApiTestConfig, Endpoint, Field, Fixtures } from './types';
29
+ import { runDbQueryCli, type DbQueryResult } from './db-query-cli';
30
+ export { runDbQueryCli, type DbQueryResult };
31
+ /** Outcome of assembling one endpoint. */
32
+ export interface AssembleResult {
33
+ /** JSON string ready to drop into a `.bru` `body:json` block. Empty when `skipped`. */
34
+ body: string;
35
+ skipped: boolean;
36
+ reason: string | null;
37
+ }
38
+ /**
39
+ * Assemble the request body for one endpoint following the three-tier
40
+ * fallback: fixture > db-query > skip.
41
+ *
42
+ * Fixture key: `<svc_id>_<methodName>` (matches the `.bru` filename stem).
43
+ * Fixture `params` is used verbatim as the body array (positional per
44
+ * method-declaration order).
45
+ *
46
+ * For db-query sourcing, fields are grouped by `paramIndex` and one object
47
+ * per param is emitted (in paramIndex order). The body is always a JSON
48
+ * array — `[{...param0}, {...param1}, ...]`.
49
+ */
50
+ export declare function assembleEndpoint(endpoint: Endpoint, _config: ApiTestConfig, fixtures: Fixtures, projectRoot: string): Promise<AssembleResult>;
51
+ /**
52
+ * Build the `SELECT ... FROM ... WHERE ... LIMIT 1` SQL for a db-query-sourced
53
+ * field. `sourceFilter` is optional; `sourceColumn` and `sourceTable` are
54
+ * required (caller guards).
55
+ */
56
+ declare function buildSourceSql(field: Field): string;
57
+ /**
58
+ * Patch a `.bru` file's `body:json` block content in place. Reads the file,
59
+ * replaces whatever is between `body:json {` and the matching closing `}`,
60
+ * and writes it back. Preserves the rest of the file verbatim.
61
+ *
62
+ * Returns the new content (also written to disk). Caller is responsible for
63
+ * the file existing.
64
+ */
65
+ export declare function writeBruBody(bruPath: string, body: string): Promise<string>;
66
+ /**
67
+ * Pure-string transform: replace the inner content of the `body:json { ... }`
68
+ * block in a `.bru` file. If the block can't be located, returns the input
69
+ * unchanged (defensive — scan phase always emits one).
70
+ */
71
+ export declare function replaceBodyJsonBlock(bruContent: string, newBody: string): string;
72
+ /** Internal helpers re-exported for tests. */
73
+ export declare const __INTERNAL_ASSEMBLE: {
74
+ buildSourceSql: typeof buildSourceSql;
75
+ };
76
+ //# sourceMappingURL=assemble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/api-test/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EACV,aAAa,EACb,QAAQ,EACR,KAAK,EACL,QAAQ,EACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,CAAC;AAE7C,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC,CAyEzB;AAED;;;;GAIG;AACH,iBAAS,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAG5C;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKjF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIhF;AAED,8CAA8C;AAC9C,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * Assemble phase — source real parameter values for each endpoint and write
4
+ * them into the `.bru` `body:json` block.
5
+ *
6
+ * Spec contract (T-40): three-tier fallback per endpoint:
7
+ * 1. fixture hit → use `fixtures[<svc_id>_<methodName>]`.params verbatim.
8
+ * 2. db-query → for each Field with `sourceTable`, run
9
+ * `cgraphx db query --sql "SELECT ... LIMIT 1" --format json`
10
+ * via the dist binary (subprocess). `literal` fields are
11
+ * filled directly; `fixtureOnly` fields require a fixture
12
+ * (else the endpoint is skip).
13
+ * 3. skip → db-query CLI failure → `db_query_failed`;
14
+ * any field missing source/literal/fixture → `assemble_failed`.
15
+ *
16
+ * Independent subsystem — does NOT import from `src/dbquery/`. The db-query
17
+ * CLI is invoked as a subprocess (subsystem isolation per spec §12). The
18
+ * subprocess wrapper lives in `./db-query-cli.ts` so tests can `vi.spyOn`
19
+ * the export and intercept calls from this module.
20
+ *
21
+ * Public surface:
22
+ * - `assembleEndpoint(endpoint, config, fixtures, projectRoot)`
23
+ * → `{ body, skipped, reason }`
24
+ * - `writeBruBody(bruPath, body)` — patches a `.bru` file's `body:json` block.
25
+ * - `replaceBodyJsonBlock(bruContent, newBody)` — pure-string transform.
26
+ * - re-exports `runDbQueryCli` / `DbQueryResult` from `./db-query-cli` so
27
+ * callers can import everything from one place.
28
+ */
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
41
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
42
+ }) : function(o, v) {
43
+ o["default"] = v;
44
+ });
45
+ var __importStar = (this && this.__importStar) || (function () {
46
+ var ownKeys = function(o) {
47
+ ownKeys = Object.getOwnPropertyNames || function (o) {
48
+ var ar = [];
49
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
50
+ return ar;
51
+ };
52
+ return ownKeys(o);
53
+ };
54
+ return function (mod) {
55
+ if (mod && mod.__esModule) return mod;
56
+ var result = {};
57
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
58
+ __setModuleDefault(result, mod);
59
+ return result;
60
+ };
61
+ })();
62
+ Object.defineProperty(exports, "__esModule", { value: true });
63
+ exports.__INTERNAL_ASSEMBLE = exports.runDbQueryCli = void 0;
64
+ exports.assembleEndpoint = assembleEndpoint;
65
+ exports.writeBruBody = writeBruBody;
66
+ exports.replaceBodyJsonBlock = replaceBodyJsonBlock;
67
+ const fs = __importStar(require("node:fs"));
68
+ const db_query_cli_1 = require("./db-query-cli");
69
+ Object.defineProperty(exports, "runDbQueryCli", { enumerable: true, get: function () { return db_query_cli_1.runDbQueryCli; } });
70
+ /**
71
+ * Assemble the request body for one endpoint following the three-tier
72
+ * fallback: fixture > db-query > skip.
73
+ *
74
+ * Fixture key: `<svc_id>_<methodName>` (matches the `.bru` filename stem).
75
+ * Fixture `params` is used verbatim as the body array (positional per
76
+ * method-declaration order).
77
+ *
78
+ * For db-query sourcing, fields are grouped by `paramIndex` and one object
79
+ * per param is emitted (in paramIndex order). The body is always a JSON
80
+ * array — `[{...param0}, {...param1}, ...]`.
81
+ */
82
+ async function assembleEndpoint(endpoint, _config, fixtures, projectRoot) {
83
+ const fixtureKey = `${endpoint.svcId}_${endpoint.method}`;
84
+ const fixture = fixtures[fixtureKey];
85
+ // ─── Priority 1: fixture hit ────────────────────────────────────────────
86
+ if (fixture) {
87
+ const body = JSON.stringify(fixture.params);
88
+ return { body, skipped: false, reason: null };
89
+ }
90
+ // ─── Priority 2: db-query ───────────────────────────────────────────────
91
+ // No fields → can't assemble (the analyze phase either skipped or produced
92
+ // nothing). Mark `assemble_failed` so the .md reflects it.
93
+ if (endpoint.fields.length === 0) {
94
+ return { body: '', skipped: true, reason: 'assemble_failed' };
95
+ }
96
+ // fixtureOnly fields require a fixture (which we don't have here).
97
+ for (const f of endpoint.fields) {
98
+ if (f.fixtureOnly) {
99
+ return { body: '', skipped: true, reason: 'assemble_failed' };
100
+ }
101
+ }
102
+ // Group fields by paramIndex (one body object per method parameter).
103
+ const paramGroups = new Map();
104
+ for (const f of endpoint.fields) {
105
+ const arr = paramGroups.get(f.paramIndex) ?? [];
106
+ arr.push(f);
107
+ paramGroups.set(f.paramIndex, arr);
108
+ }
109
+ const paramIndices = [...paramGroups.keys()].sort((a, b) => a - b);
110
+ const bodyArray = [];
111
+ for (const idx of paramIndices) {
112
+ const fields = paramGroups.get(idx);
113
+ const paramObj = {};
114
+ for (const f of fields) {
115
+ // literal — fill directly.
116
+ if (f.literal !== null) {
117
+ paramObj[f.name] = f.literal;
118
+ continue;
119
+ }
120
+ // db-query sourced field.
121
+ if (f.sourceTable && f.sourceColumn) {
122
+ const sql = buildSourceSql(f);
123
+ const result = await (0, db_query_cli_1.runDbQueryCli)(sql, projectRoot);
124
+ if (!result.ok) {
125
+ return { body: '', skipped: true, reason: 'db_query_failed' };
126
+ }
127
+ const row = result.rows[0];
128
+ if (!row) {
129
+ // Query succeeded but returned no rows — field value can't be sourced.
130
+ return { body: '', skipped: true, reason: 'assemble_failed' };
131
+ }
132
+ const value = row[f.sourceColumn];
133
+ if (value === undefined || value === null) {
134
+ return { body: '', skipped: true, reason: 'assemble_failed' };
135
+ }
136
+ paramObj[f.name] = value;
137
+ }
138
+ else {
139
+ // No source, no literal, no fixture → can't source this field.
140
+ return { body: '', skipped: true, reason: 'assemble_failed' };
141
+ }
142
+ }
143
+ bodyArray.push(paramObj);
144
+ }
145
+ return { body: JSON.stringify(bodyArray), skipped: false, reason: null };
146
+ }
147
+ /**
148
+ * Build the `SELECT ... FROM ... WHERE ... LIMIT 1` SQL for a db-query-sourced
149
+ * field. `sourceFilter` is optional; `sourceColumn` and `sourceTable` are
150
+ * required (caller guards).
151
+ */
152
+ function buildSourceSql(field) {
153
+ const filter = field.sourceFilter ? ` WHERE ${field.sourceFilter}` : '';
154
+ return `SELECT ${field.sourceColumn} FROM ${field.sourceTable}${filter} LIMIT 1`;
155
+ }
156
+ /**
157
+ * Patch a `.bru` file's `body:json` block content in place. Reads the file,
158
+ * replaces whatever is between `body:json {` and the matching closing `}`,
159
+ * and writes it back. Preserves the rest of the file verbatim.
160
+ *
161
+ * Returns the new content (also written to disk). Caller is responsible for
162
+ * the file existing.
163
+ */
164
+ async function writeBruBody(bruPath, body) {
165
+ const content = await fs.promises.readFile(bruPath, 'utf8');
166
+ const updated = replaceBodyJsonBlock(content, body);
167
+ await fs.promises.writeFile(bruPath, updated, 'utf8');
168
+ return updated;
169
+ }
170
+ /**
171
+ * Pure-string transform: replace the inner content of the `body:json { ... }`
172
+ * block in a `.bru` file. If the block can't be located, returns the input
173
+ * unchanged (defensive — scan phase always emits one).
174
+ */
175
+ function replaceBodyJsonBlock(bruContent, newBody) {
176
+ const re = /(body:json\s*\{\n)([\s\S]*?)(\n\})/;
177
+ if (!re.test(bruContent))
178
+ return bruContent;
179
+ return bruContent.replace(re, `$1 ${newBody}$3`);
180
+ }
181
+ /** Internal helpers re-exported for tests. */
182
+ exports.__INTERNAL_ASSEMBLE = {
183
+ buildSourceSql,
184
+ };
185
+ //# sourceMappingURL=assemble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/api-test/assemble.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Bruno CLI subprocess invoker — isolated in its own module so tests can
3
+ * `vi.spyOn` the `invokeBrunoCli` export and intercept calls from
4
+ * `bru-runner.ts`.
5
+ *
6
+ * Why a separate module: same reason as `db-query-cli.ts` — when the
7
+ * subprocess call lives in the same module as its caller, vitest's
8
+ * `vi.spyOn(module, 'fn')` cannot intercept the internal bare-local-binding
9
+ * call. Cross-module imports route through the namespace, which the spy CAN
10
+ * replace.
11
+ *
12
+ * Independent subsystem — does NOT import from `src/extraction/` /
13
+ * `src/resolution/` / `src/db/`. The Bruno CLI is invoked as a subprocess
14
+ * (spec §12 isolation).
15
+ */
16
+ /** Result of one Bruno CLI invocation. */
17
+ export interface BrunoCliInvocation {
18
+ /** Combined stdout from the subprocess. */
19
+ stdout: string;
20
+ /** Combined stderr from the subprocess. */
21
+ stderr: string;
22
+ /** Process exit code (null if the process was killed by signal). */
23
+ exitCode: number | null;
24
+ /** True when the subprocess was killed by the timeout. */
25
+ timedOut: boolean;
26
+ }
27
+ export interface InvokeBrunoCliOptions {
28
+ /** Working directory for the subprocess. */
29
+ cwd: string;
30
+ /** Hard kill timeout in milliseconds. */
31
+ timeoutMs: number;
32
+ /** Max stdout/stderr buffer size in bytes (default 10 MB). */
33
+ maxBuffer?: number;
34
+ }
35
+ /**
36
+ * Invoke `npx @usebruno/cli` with the given args. Returns the captured
37
+ * stdout/stderr/exitCode. The caller is responsible for arg construction
38
+ * (see `bru-runner.ts`).
39
+ *
40
+ * Exported so tests can `vi.spyOn(bruCliInvoker, 'invokeBrunoCli')` instead
41
+ * of spawning a real subprocess / real Bruno CLI.
42
+ */
43
+ export declare function invokeBrunoCli(args: string[], options: InvokeBrunoCliOptions): BrunoCliInvocation;
44
+ /**
45
+ * Probe whether the Bruno CLI is available. Tries, in order:
46
+ * 1. `npx @usebruno/cli --version` (preferred — always works if npm cache
47
+ * has the package, even without global install).
48
+ * 2. Global `bru` command (`which bru`).
49
+ * 3. Global `bruno` command (`which bruno`).
50
+ *
51
+ * Returns the resolved command + base args for subsequent `invokeBrunoCli`
52
+ * calls, or `null` when no Bruno CLI is reachable.
53
+ *
54
+ * Exported so tests can spy on it to simulate "Bruno CLI unavailable".
55
+ */
56
+ export declare function detectBrunoCli(): {
57
+ command: string;
58
+ baseArgs: string[];
59
+ } | null;
60
+ /**
61
+ * Resolve the collection directory. If `filter.service` is set, narrow to
62
+ * `<collectionDir>/<service>/` when it exists; otherwise fall back to the
63
+ * whole collection dir (Bruno CLI will just find no matching requests).
64
+ */
65
+ export declare function resolveCollectionDir(collectionDir: string, filter?: {
66
+ service?: string;
67
+ }): string;
68
+ /** Internal helper re-exported for tests. */
69
+ export declare const __INTERNAL_BRU_CLI_INVOKER: {
70
+ resolveCollectionDir: typeof resolveCollectionDir;
71
+ };
72
+ //# sourceMappingURL=bru-cli-invoker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-cli-invoker.d.ts","sourceRoot":"","sources":["../../src/api-test/bru-cli-invoker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,qBAAqB,GAC7B,kBAAkB,CA2BpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CA8C/E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,MAAM,CAQR;AAED,6CAA6C;AAC7C,eAAO,MAAM,0BAA0B;;CAEtC,CAAC"}
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ /**
3
+ * Bruno CLI subprocess invoker — isolated in its own module so tests can
4
+ * `vi.spyOn` the `invokeBrunoCli` export and intercept calls from
5
+ * `bru-runner.ts`.
6
+ *
7
+ * Why a separate module: same reason as `db-query-cli.ts` — when the
8
+ * subprocess call lives in the same module as its caller, vitest's
9
+ * `vi.spyOn(module, 'fn')` cannot intercept the internal bare-local-binding
10
+ * call. Cross-module imports route through the namespace, which the spy CAN
11
+ * replace.
12
+ *
13
+ * Independent subsystem — does NOT import from `src/extraction/` /
14
+ * `src/resolution/` / `src/db/`. The Bruno CLI is invoked as a subprocess
15
+ * (spec §12 isolation).
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.__INTERNAL_BRU_CLI_INVOKER = void 0;
52
+ exports.invokeBrunoCli = invokeBrunoCli;
53
+ exports.detectBrunoCli = detectBrunoCli;
54
+ exports.resolveCollectionDir = resolveCollectionDir;
55
+ const node_child_process_1 = require("node:child_process");
56
+ const fs = __importStar(require("node:fs"));
57
+ const path = __importStar(require("node:path"));
58
+ /**
59
+ * Invoke `npx @usebruno/cli` with the given args. Returns the captured
60
+ * stdout/stderr/exitCode. The caller is responsible for arg construction
61
+ * (see `bru-runner.ts`).
62
+ *
63
+ * Exported so tests can `vi.spyOn(bruCliInvoker, 'invokeBrunoCli')` instead
64
+ * of spawning a real subprocess / real Bruno CLI.
65
+ */
66
+ function invokeBrunoCli(args, options) {
67
+ const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;
68
+ try {
69
+ const result = (0, node_child_process_1.spawnSync)(process.execPath, args, {
70
+ cwd: options.cwd,
71
+ encoding: 'utf8',
72
+ timeout: options.timeoutMs,
73
+ maxBuffer,
74
+ stdio: ['ignore', 'pipe', 'pipe'],
75
+ });
76
+ return {
77
+ stdout: typeof result.stdout === 'string' ? result.stdout : '',
78
+ stderr: typeof result.stderr === 'string' ? result.stderr : '',
79
+ exitCode: result.status,
80
+ timedOut: result.signal === 'SIGTERM' || (result.signal != null && result.signal !== null),
81
+ };
82
+ }
83
+ catch (err) {
84
+ // spawnSync throws on ENOENT (node binary not found — shouldn't happen)
85
+ // or other spawn-level failures. Normalize to a non-zero exit.
86
+ const msg = err instanceof Error ? err.message : String(err);
87
+ return {
88
+ stdout: '',
89
+ stderr: `spawn failed: ${msg}`,
90
+ exitCode: 1,
91
+ timedOut: false,
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Probe whether the Bruno CLI is available. Tries, in order:
97
+ * 1. `npx @usebruno/cli --version` (preferred — always works if npm cache
98
+ * has the package, even without global install).
99
+ * 2. Global `bru` command (`which bru`).
100
+ * 3. Global `bruno` command (`which bruno`).
101
+ *
102
+ * Returns the resolved command + base args for subsequent `invokeBrunoCli`
103
+ * calls, or `null` when no Bruno CLI is reachable.
104
+ *
105
+ * Exported so tests can spy on it to simulate "Bruno CLI unavailable".
106
+ */
107
+ function detectBrunoCli() {
108
+ // 1. npx @usebruno/cli --version (short timeout — npx may need to fetch).
109
+ const probe = (0, node_child_process_1.spawnSync)(process.execPath, ['@usebruno/cli', '--version'], {
110
+ encoding: 'utf8',
111
+ timeout: 30_000,
112
+ stdio: ['ignore', 'pipe', 'pipe'],
113
+ });
114
+ if (probe.status === 0 && probe.stdout.trim().length > 0) {
115
+ return { command: process.execPath, baseArgs: ['@usebruno/cli'] };
116
+ }
117
+ // npx wraps @usebruno/cli; the direct `node @usebruno/cli` form above only
118
+ // resolves when the package is installed locally. Use `npx` explicitly so
119
+ // the package is fetched on demand.
120
+ const npxProbe = (0, node_child_process_1.spawnSync)('npx', ['@usebruno/cli', '--version'], {
121
+ encoding: 'utf8',
122
+ timeout: 30_000,
123
+ stdio: ['ignore', 'pipe', 'pipe'],
124
+ shell: process.platform === 'win32',
125
+ });
126
+ if (npxProbe.status === 0 && npxProbe.stdout.trim().length > 0) {
127
+ return { command: 'npx', baseArgs: ['@usebruno/cli'] };
128
+ }
129
+ // 2. Global `bru` command.
130
+ const bruProbe = (0, node_child_process_1.spawnSync)('bru', ['--version'], {
131
+ encoding: 'utf8',
132
+ timeout: 5_000,
133
+ stdio: ['ignore', 'pipe', 'pipe'],
134
+ shell: process.platform === 'win32',
135
+ });
136
+ if (bruProbe.status === 0 && bruProbe.stdout.trim().length > 0) {
137
+ return { command: 'bru', baseArgs: [] };
138
+ }
139
+ // 3. Global `bruno` command.
140
+ const brunoProbe = (0, node_child_process_1.spawnSync)('bruno', ['--version'], {
141
+ encoding: 'utf8',
142
+ timeout: 5_000,
143
+ stdio: ['ignore', 'pipe', 'pipe'],
144
+ shell: process.platform === 'win32',
145
+ });
146
+ if (brunoProbe.status === 0 && brunoProbe.stdout.trim().length > 0) {
147
+ return { command: 'bruno', baseArgs: [] };
148
+ }
149
+ return null;
150
+ }
151
+ /**
152
+ * Resolve the collection directory. If `filter.service` is set, narrow to
153
+ * `<collectionDir>/<service>/` when it exists; otherwise fall back to the
154
+ * whole collection dir (Bruno CLI will just find no matching requests).
155
+ */
156
+ function resolveCollectionDir(collectionDir, filter) {
157
+ if (filter?.service) {
158
+ const sub = path.join(collectionDir, filter.service);
159
+ if (fs.existsSync(sub) && fs.statSync(sub).isDirectory()) {
160
+ return sub;
161
+ }
162
+ }
163
+ return collectionDir;
164
+ }
165
+ /** Internal helper re-exported for tests. */
166
+ exports.__INTERNAL_BRU_CLI_INVOKER = {
167
+ resolveCollectionDir,
168
+ };
169
+ //# sourceMappingURL=bru-cli-invoker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bru-cli-invoker.js","sourceRoot":"","sources":["../../src/api-test/bru-cli-invoker.ts"],"names":[],"mappings":"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"}