api-tests-coverage 1.0.5 → 1.0.6

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 (57) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-CeCjvJWr.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-DQMeOG5y.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-BDIdECDJ.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-D2t8Py1B.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-7B2h6RPh.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-BJS73w81.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-B5FEo6x8.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-rMDV_g4i.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-H2T0HHMf.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-BIav900j.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-CL1ZxZeg.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-BH1h7N7Y.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-Dzk5VCZ3.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-Z3uL3Vu6.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-Bk8ko3-5.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-M4p4DdHs.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-M4p4DdHs.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-DISkn1y_.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-BbDNIhrI.js +1 -0
  20. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-Niwmlwr0.js +4 -0
  21. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-D6YSgDDJ.js +24 -0
  22. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BIV3o3EV.js +43 -0
  23. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-B_V6T3Do.js +24 -0
  24. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-Dv8XSfJj.js +60 -0
  25. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-DHVxWNFx.js +162 -0
  26. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-C0zKIcDh.js +267 -0
  27. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-DbNQAtGz.js +65 -0
  28. package/dist/dashboard/dist/assets/graph-BUXAK8S4.js +1 -0
  29. package/dist/dashboard/dist/assets/index-HrRX8fCW.css +1 -0
  30. package/dist/dashboard/dist/assets/index-k7QMCdxo.js +522 -0
  31. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-CbvfFEOs.js +2 -0
  32. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-CHncp1wO.js +139 -0
  33. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-Ct5tvLkE.js +89 -0
  34. package/dist/dashboard/dist/assets/layout-DEw3EHVd.js +1 -0
  35. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-CM92aa9b.js +68 -0
  36. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Dow8SBXC.js +30 -0
  37. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-CHDOoeNa.js +7 -0
  38. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-BdQwEiam.js +64 -0
  39. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-C2tgBc3h.js +10 -0
  40. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-BJapitlJ.js +145 -0
  41. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-CAMqhH5J.js +1 -0
  42. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-DMcei2iB.js +1 -0
  43. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-CgZuzj68.js +61 -0
  44. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-DISZoPlK.js +162 -0
  45. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-WhBuJ91k.js +7 -0
  46. package/dist/dashboard/dist/index.html +2 -2
  47. package/dist/src/index.js +248 -11
  48. package/dist/src/inference/businessRuleInference.d.ts +8 -0
  49. package/dist/src/inference/businessRuleInference.d.ts.map +1 -1
  50. package/dist/src/inference/businessRuleInference.js +52 -0
  51. package/dist/src/inference/routeInference.d.ts +50 -0
  52. package/dist/src/inference/routeInference.d.ts.map +1 -0
  53. package/dist/src/inference/routeInference.js +220 -0
  54. package/dist/src/inference/scanManifest.d.ts +35 -0
  55. package/dist/src/inference/scanManifest.d.ts.map +1 -0
  56. package/dist/src/inference/scanManifest.js +58 -0
  57. package/package.json +1 -1
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ /**
3
+ * Route Inference Engine
4
+ *
5
+ * Discovers HTTP routes from Express/Koa/Hapi/Fastify source files when no
6
+ * OpenAPI spec is available, enabling endpoint coverage analysis without
7
+ * a pre-authored specification.
8
+ *
9
+ * Patterns supported:
10
+ * - Express/Koa: router.get('/path', ...) / app.post('/path', ...)
11
+ * - Multi-line: router.get(\n '/path', ...) — path on next line
12
+ * - Chained: router.route('/path').get(...).post(...)
13
+ * - JSDoc route annotations: @route {GET} /path (used only when no code pattern found)
14
+ *
15
+ * Deduplication:
16
+ * - Routes are deduplicated by method:path within each file.
17
+ * - Actual code patterns take priority over JSDoc annotations.
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.HTTP_METHODS = void 0;
54
+ exports.inferRoutesFromFile = inferRoutesFromFile;
55
+ exports.inferRoutes = inferRoutes;
56
+ exports.writeInferredRoutes = writeInferredRoutes;
57
+ const fs = __importStar(require("fs"));
58
+ const path = __importStar(require("path"));
59
+ // ─── Public types ─────────────────────────────────────────────────────────────
60
+ exports.HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
61
+ // ─── Patterns ─────────────────────────────────────────────────────────────────
62
+ /** Matches: router.get('/path', ...) or app.post('/path', ...) — on a single line */
63
+ const ROUTER_METHOD_INLINE = /(?:router|app|server)\s*\.\s*(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/i;
64
+ /**
65
+ * Matches the beginning of a router.method( call that spans multiple lines.
66
+ * Group 1 = method name (get|post|...).
67
+ * The route path is expected on the same or next line.
68
+ */
69
+ const ROUTER_METHOD_MULTILINE_START = /(?:router|app|server)\s*\.\s*(get|post|put|patch|delete|head|options)\s*\(\s*$/i;
70
+ /** Matches a standalone route path string (first arg in multi-line route) */
71
+ const ROUTE_PATH_LINE = /^\s*['"`]([^'"`]+)['"`]\s*,?\s*$/;
72
+ /** Matches: router.route('/path').get(...) — chained route definition */
73
+ const CHAINED_ROUTE_PATTERN = /(?:router|app)\s*\.\s*route\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/i;
74
+ const CHAINED_METHOD_PATTERN = /\.\s*(get|post|put|patch|delete|head|options)\s*\(/gi;
75
+ /** Matches JSDoc @route {GET} /path */
76
+ const JSDOC_ROUTE_PATTERN = /@route\s+\{(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\}\s+(\S+)/i;
77
+ /**
78
+ * Matches the primary async service function call within a route handler body.
79
+ * e.g. `await deleteComment(...)` or `const result = await getArticles(...)`.
80
+ * Group 1 = function name.
81
+ *
82
+ * Note: This only detects `await`-based async calls. Synchronous handlers
83
+ * (e.g. `res.json(getUsers())`) are not matched by this pattern.
84
+ */
85
+ const HANDLER_FUNCTION_PATTERN = /(?:await|const\s+\w+\s*=\s*await)\s+([a-zA-Z][a-zA-Z0-9]*)\s*\(/;
86
+ /**
87
+ * Number of source lines scanned ahead to find the primary handler function
88
+ * when the route is defined on a single line (router.method('/path', ...)).
89
+ * The inline form starts the handler function close to the route definition.
90
+ */
91
+ const HANDLER_SCAN_WINDOW_INLINE = 20;
92
+ /**
93
+ * Number of source lines scanned ahead to find the primary handler function
94
+ * when the route is defined with the path on the next line. The multi-line
95
+ * form has more preamble (middleware, auth, etc.) before the handler function.
96
+ */
97
+ const HANDLER_SCAN_WINDOW_MULTILINE = 25;
98
+ // ─── Core inference function ──────────────────────────────────────────────────
99
+ /**
100
+ * Infer routes from a single source file.
101
+ * Routes are deduplicated by method:path within the file.
102
+ * Code-based patterns take priority over JSDoc annotations for the same method+path.
103
+ */
104
+ function inferRoutesFromFile(filePath) {
105
+ let content;
106
+ try {
107
+ content = fs.readFileSync(filePath, 'utf-8');
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ const lines = content.split('\n');
113
+ // key = "method:path" → route (code-found routes win over jsdoc)
114
+ const byKey = new Map();
115
+ const addRoute = (method, routePath, lineNumber, discoveredVia, handlerFunction) => {
116
+ const key = `${method}:${routePath}`;
117
+ const existing = byKey.get(key);
118
+ // Prefer code-discovered over jsdoc; if same source, first occurrence wins
119
+ if (!existing || (discoveredVia === 'code' && existing.discoveredVia === 'jsdoc')) {
120
+ byKey.set(key, { method, path: routePath, handlerFunction, sourceFile: filePath, lineNumber, discoveredVia });
121
+ }
122
+ };
123
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
124
+ const line = lines[lineIdx];
125
+ // 1a. router.method('/path', ...) — all on one line
126
+ const inlineMatch = line.match(ROUTER_METHOD_INLINE);
127
+ if (inlineMatch) {
128
+ const method = inlineMatch[1].toLowerCase();
129
+ const routePath = inlineMatch[2];
130
+ // Scan up to HANDLER_SCAN_WINDOW_INLINE following lines for the primary await function call
131
+ const handlerFn = findHandlerFunction(lines, lineIdx, HANDLER_SCAN_WINDOW_INLINE);
132
+ addRoute(method, routePath, lineIdx + 1, 'code', handlerFn);
133
+ continue;
134
+ }
135
+ // 1b. router.method(\n '/path', ...) — method and path on separate lines
136
+ const multilineStart = line.match(ROUTER_METHOD_MULTILINE_START);
137
+ if (multilineStart && lineIdx + 1 < lines.length) {
138
+ const nextLine = lines[lineIdx + 1];
139
+ const pathMatch = nextLine.match(ROUTE_PATH_LINE);
140
+ if (pathMatch) {
141
+ const method = multilineStart[1].toLowerCase();
142
+ const routePath = pathMatch[1];
143
+ const handlerFn = findHandlerFunction(lines, lineIdx, HANDLER_SCAN_WINDOW_MULTILINE);
144
+ addRoute(method, routePath, lineIdx + 1, 'code', handlerFn);
145
+ lineIdx++; // skip the path line
146
+ continue;
147
+ }
148
+ }
149
+ // 2. JSDoc @route annotation (only added if no code pattern found for same route)
150
+ const jsDocMatch = line.match(JSDOC_ROUTE_PATTERN);
151
+ if (jsDocMatch) {
152
+ const method = jsDocMatch[1].toLowerCase();
153
+ const routePath = jsDocMatch[2];
154
+ addRoute(method, routePath, lineIdx + 1, 'jsdoc');
155
+ continue;
156
+ }
157
+ // 3. Chained: .route('/path').get(...).post(...)
158
+ const chainedPathMatch = line.match(CHAINED_ROUTE_PATTERN);
159
+ if (chainedPathMatch) {
160
+ const routePath = chainedPathMatch[1];
161
+ const windowEnd = Math.min(lineIdx + 5, lines.length);
162
+ const windowText = lines.slice(lineIdx, windowEnd).join(' ');
163
+ let methodMatch;
164
+ CHAINED_METHOD_PATTERN.lastIndex = 0;
165
+ while ((methodMatch = CHAINED_METHOD_PATTERN.exec(windowText)) !== null) {
166
+ const method = methodMatch[1].toLowerCase();
167
+ addRoute(method, routePath, lineIdx + 1, 'code');
168
+ }
169
+ }
170
+ }
171
+ return [...byKey.values()];
172
+ }
173
+ /**
174
+ * Scan ahead in the source lines from `startLine` to find the first awaited
175
+ * service function call in the route handler body.
176
+ */
177
+ function findHandlerFunction(lines, startLine, maxLines) {
178
+ const end = Math.min(startLine + maxLines, lines.length);
179
+ for (let i = startLine; i < end; i++) {
180
+ const m = lines[i].match(HANDLER_FUNCTION_PATTERN);
181
+ if (m)
182
+ return m[1];
183
+ }
184
+ return undefined;
185
+ }
186
+ // ─── Public API ───────────────────────────────────────────────────────────────
187
+ /**
188
+ * Infer routes from a set of service source files.
189
+ */
190
+ function inferRoutes(serviceFiles) {
191
+ const allRoutes = [];
192
+ const warnings = [];
193
+ let filesAnalyzed = 0;
194
+ for (const fp of serviceFiles) {
195
+ const fileRoutes = inferRoutesFromFile(fp);
196
+ allRoutes.push(...fileRoutes);
197
+ filesAnalyzed++;
198
+ }
199
+ if (allRoutes.length === 0) {
200
+ warnings.push('No HTTP routes detected in service files.');
201
+ }
202
+ return { routes: allRoutes, filesAnalyzed, warnings };
203
+ }
204
+ /**
205
+ * Write inferred routes to the reports directory.
206
+ * Returns the path of the written file.
207
+ */
208
+ function writeInferredRoutes(result, reportsDir) {
209
+ fs.mkdirSync(reportsDir, { recursive: true });
210
+ const outputPath = path.join(reportsDir, 'inferred-routes.json');
211
+ const output = {
212
+ generated_at: new Date().toISOString(),
213
+ route_source: 'inferred',
214
+ files_analyzed: result.filesAnalyzed,
215
+ route_count: result.routes.length,
216
+ routes: result.routes,
217
+ };
218
+ fs.writeFileSync(outputPath, JSON.stringify(output, null, 2), 'utf-8');
219
+ return outputPath;
220
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Scan Manifest
3
+ *
4
+ * Records what was scanned during a zero-config `analyze` run —
5
+ * files analyzed, languages detected, frameworks detected, and
6
+ * per-metric coverage outcomes with their source (explicit / inferred / skipped).
7
+ *
8
+ * Written to: reports/scan-manifest.json
9
+ */
10
+ export interface ScanTypeEntry {
11
+ type: string;
12
+ source: 'explicit' | 'inferred' | 'skipped';
13
+ reason?: string;
14
+ itemsFound: number;
15
+ itemsCovered: number;
16
+ coveragePercent: number;
17
+ }
18
+ export interface ScanManifest {
19
+ projectRoot: string;
20
+ analyzedAt: string;
21
+ discoveredFiles: {
22
+ serviceFiles: string[];
23
+ testFiles: string[];
24
+ specFiles: string[];
25
+ };
26
+ languages: string[];
27
+ frameworks: string[];
28
+ scanTypes: ScanTypeEntry[];
29
+ }
30
+ /**
31
+ * Write a scan manifest to the reports directory.
32
+ * Returns the path of the written file.
33
+ */
34
+ export declare function writeScanManifest(manifest: ScanManifest, reportsDir: string): string;
35
+ //# sourceMappingURL=scanManifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanManifest.d.ts","sourceRoot":"","sources":["../../../src/inference/scanManifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE;QACf,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAKpF"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * Scan Manifest
4
+ *
5
+ * Records what was scanned during a zero-config `analyze` run —
6
+ * files analyzed, languages detected, frameworks detected, and
7
+ * per-metric coverage outcomes with their source (explicit / inferred / skipped).
8
+ *
9
+ * Written to: reports/scan-manifest.json
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.writeScanManifest = writeScanManifest;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ // ─── Writer ───────────────────────────────────────────────────────────────────
49
+ /**
50
+ * Write a scan manifest to the reports directory.
51
+ * Returns the path of the written file.
52
+ */
53
+ function writeScanManifest(manifest, reportsDir) {
54
+ fs.mkdirSync(reportsDir, { recursive: true });
55
+ const outputPath = path.join(reportsDir, 'scan-manifest.json');
56
+ fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2), 'utf-8');
57
+ return outputPath;
58
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-tests-coverage",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "CLI and library to measure how thoroughly your test suite exercises your API surface area",
5
5
  "main": "dist/src/lib/index.js",
6
6
  "types": "dist/src/lib/index.d.ts",