api-tests-coverage 1.0.21 → 1.0.22

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 (125) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-BKGHUeDJ.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-CtA-DQF7.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-CbXP3Mc9.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-n7QxasMM.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-MXnGwKRn.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-B3yZ5P9k.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-C7QwX7U8.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-Dw3El5KF.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-T8Jf00IL.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-DAJalKYJ.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-p7o_KuDF.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-B0wUAfQR.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BTFwTEw8.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CNXHnjkC.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BIVywBYp.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dkb-G0UK.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dkb-G0UK.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-BSdXhKmH.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DuALhY5a.js +1 -0
  20. package/dist/dashboard/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
  21. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-w6endfy9.js +4 -0
  22. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-BDnUwPQC.js +24 -0
  23. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-Cbb7ctAo.js +43 -0
  24. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-76ascveh.js +24 -0
  25. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BQQnVF0J.js +60 -0
  26. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-dKukiSxD.js +162 -0
  27. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BqnazZuZ.js +267 -0
  28. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BUFcnXCj.js +65 -0
  29. package/dist/dashboard/dist/assets/graph-CGQIvL3r.js +1 -0
  30. package/dist/dashboard/dist/assets/index-CadOHtae.css +1 -0
  31. package/dist/dashboard/dist/assets/index-DwqfA4mc.js +777 -0
  32. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BdylFPLI.js +2 -0
  33. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BzJTBkhp.js +139 -0
  34. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-StKomgio.js +89 -0
  35. package/dist/dashboard/dist/assets/katex-O9d3_IXG.js +261 -0
  36. package/dist/dashboard/dist/assets/layout-BnsX73QS.js +1 -0
  37. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-HqFRhVFX.js +68 -0
  38. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-2I2tFH0C.js +30 -0
  39. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-PlELkdBW.js +7 -0
  40. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-_9eLQNcZ.js +64 -0
  41. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DHl1Ss7O.js +10 -0
  42. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-B9p0m3Uf.js +145 -0
  43. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC--rpDODjT.js +1 -0
  44. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Ca9uGk46.js +1 -0
  45. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-BCHaGBHB.js +61 -0
  46. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CgiqDY8M.js +162 -0
  47. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-BMvBBbeV.js +7 -0
  48. package/dist/dashboard/dist/index.html +14 -0
  49. package/dist/dashboard/dist/reports/business-coverage.json +201 -0
  50. package/dist/dashboard/dist/reports/coverage-intelligence.json +728 -0
  51. package/dist/dashboard/dist/reports/coverage-summary.json +995 -0
  52. package/dist/dashboard/dist/reports/endpoint-coverage.json +336 -0
  53. package/dist/dashboard/dist/reports/error-coverage.json +367 -0
  54. package/dist/dashboard/dist/reports/missing-tests-recommendations.json +285 -0
  55. package/dist/dashboard/dist/reports/risk-prioritization.json +312 -0
  56. package/dist/dashboard/dist/reports/security-coverage.json +299 -0
  57. package/dist/dashboard/dist/vite.svg +1 -0
  58. package/dist/src/generation/context-builder.d.ts +6 -0
  59. package/dist/src/generation/context-builder.d.ts.map +1 -0
  60. package/dist/src/generation/context-builder.js +202 -0
  61. package/dist/src/generation/engine.d.ts +40 -0
  62. package/dist/src/generation/engine.d.ts.map +1 -0
  63. package/dist/src/generation/engine.js +378 -0
  64. package/dist/src/generation/file-router.d.ts +7 -0
  65. package/dist/src/generation/file-router.d.ts.map +1 -0
  66. package/dist/src/generation/file-router.js +79 -0
  67. package/dist/src/generation/gap-extractor.d.ts +12 -0
  68. package/dist/src/generation/gap-extractor.d.ts.map +1 -0
  69. package/dist/src/generation/gap-extractor.js +281 -0
  70. package/dist/src/generation/template-renderer.d.ts +10 -0
  71. package/dist/src/generation/template-renderer.d.ts.map +1 -0
  72. package/dist/src/generation/template-renderer.js +526 -0
  73. package/dist/src/generation/types.d.ts +73 -0
  74. package/dist/src/generation/types.d.ts.map +1 -0
  75. package/dist/src/generation/types.js +2 -0
  76. package/dist/src/index.js +154 -0
  77. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts +21 -0
  78. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts.map +1 -0
  79. package/dist/src/pipeline/detectors/expressMiddlewareDetector.js +201 -0
  80. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts +23 -0
  81. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts.map +1 -0
  82. package/dist/src/pipeline/detectors/flaskBlueprintDetector.js +263 -0
  83. package/dist/src/pipeline/detectors/springDddDetector.d.ts +23 -0
  84. package/dist/src/pipeline/detectors/springDddDetector.d.ts.map +1 -0
  85. package/dist/src/pipeline/detectors/springDddDetector.js +237 -0
  86. package/dist/src/pipeline/detectors/types.d.ts +97 -0
  87. package/dist/src/pipeline/detectors/types.d.ts.map +1 -0
  88. package/dist/src/pipeline/detectors/types.js +15 -0
  89. package/dist/src/reporting.d.ts.map +1 -1
  90. package/dist/src/reporting.js +2 -1
  91. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts +5 -0
  92. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts.map +1 -0
  93. package/dist/src/streaming/detectors/eventBridgeDetector.js +82 -0
  94. package/dist/src/streaming/detectors/kafkaDetector.d.ts +5 -0
  95. package/dist/src/streaming/detectors/kafkaDetector.d.ts.map +1 -0
  96. package/dist/src/streaming/detectors/kafkaDetector.js +97 -0
  97. package/dist/src/streaming/detectors/natsDetector.d.ts +5 -0
  98. package/dist/src/streaming/detectors/natsDetector.d.ts.map +1 -0
  99. package/dist/src/streaming/detectors/natsDetector.js +96 -0
  100. package/dist/src/streaming/detectors/pubsubDetector.d.ts +5 -0
  101. package/dist/src/streaming/detectors/pubsubDetector.d.ts.map +1 -0
  102. package/dist/src/streaming/detectors/pubsubDetector.js +82 -0
  103. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts +5 -0
  104. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts.map +1 -0
  105. package/dist/src/streaming/detectors/rabbitmqDetector.js +103 -0
  106. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts +5 -0
  107. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts.map +1 -0
  108. package/dist/src/streaming/detectors/redisPubsubDetector.js +81 -0
  109. package/dist/src/streaming/detectors/snsDetector.d.ts +5 -0
  110. package/dist/src/streaming/detectors/snsDetector.d.ts.map +1 -0
  111. package/dist/src/streaming/detectors/snsDetector.js +81 -0
  112. package/dist/src/streaming/detectors/sqsDetector.d.ts +5 -0
  113. package/dist/src/streaming/detectors/sqsDetector.d.ts.map +1 -0
  114. package/dist/src/streaming/detectors/sqsDetector.js +81 -0
  115. package/dist/src/streaming/eventCoverage.d.ts +46 -0
  116. package/dist/src/streaming/eventCoverage.d.ts.map +1 -0
  117. package/dist/src/streaming/eventCoverage.js +270 -0
  118. package/dist/src/streaming/types.d.ts +34 -0
  119. package/dist/src/streaming/types.d.ts.map +1 -0
  120. package/dist/src/streaming/types.js +2 -0
  121. package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
  122. package/dist/src/summary/markdownRenderer.js +1 -0
  123. package/dist/src/summary/summaryTypes.d.ts.map +1 -1
  124. package/dist/src/summary/summaryTypes.js +1 -0
  125. package/package.json +1 -1
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ /**
3
+ * Flask Blueprint detector (Feature 27)
4
+ *
5
+ * Detects from raw Python file content:
6
+ * 1. Blueprint() constructor calls → blueprint-definition nodes
7
+ * 2. @blueprint.route() decorators with HTTP methods → endpoint nodes (marked
8
+ * cross-file-unresolved until url_prefix is resolved by FlaskBlueprintResolver)
9
+ * 3. app.register_blueprint() calls → blueprint-registration nodes
10
+ * 4. @use_kwargs({...}) decorator → parameter nodes with location inference
11
+ * 5. @marshal_with(schema, code=N) → response-schema nodes
12
+ * 6. @jwt_required / @jwt_optional / @jwt_required(optional=True) → security nodes
13
+ *
14
+ * RULE-SA01: never infers role from directory — only from code content
15
+ * RULE-SA02: endpoint nodes are emitted with resolution: 'cross-file-unresolved'
16
+ * when url_prefix is not visible in the same file
17
+ * RULE-SA04: @jwt_optional → security.optional = true — NEVER classified as public
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.FlaskBlueprintDetector = void 0;
21
+ const types_1 = require("./types");
22
+ class FlaskBlueprintDetector {
23
+ constructor() {
24
+ this.name = 'flask-blueprint';
25
+ }
26
+ detect(fileContent, filePath) {
27
+ if (!filePath.endsWith('.py'))
28
+ return [];
29
+ const nodes = [];
30
+ const lines = fileContent.split('\n');
31
+ // ── Pass 1: collect Blueprint() constructor calls ─────────────────────
32
+ // Pattern: varName = Blueprint('name', __name__[, url_prefix='...'])
33
+ const blueprintCtorPattern = /^(\w+)\s*=\s*Blueprint\s*\(\s*['"]([^'"]+)['"]\s*,\s*__name__(?:\s*,\s*url_prefix\s*=\s*['"]([^'"]*)['"]\s*)?\)/;
34
+ for (let i = 0; i < lines.length; i++) {
35
+ const line = lines[i].trim();
36
+ const m = line.match(blueprintCtorPattern);
37
+ if (!m)
38
+ continue;
39
+ const varName = m[1];
40
+ const bpName = m[2];
41
+ const urlPrefix = m[3];
42
+ nodes.push({
43
+ id: (0, types_1.makeNodeId)(filePath, 'blueprint-definition', varName, i + 1),
44
+ kind: 'blueprint-definition',
45
+ name: varName,
46
+ filePath,
47
+ line: i + 1,
48
+ urlPrefix: urlPrefix !== null && urlPrefix !== void 0 ? urlPrefix : undefined,
49
+ resolution: urlPrefix ? 'resolved' : 'cross-file-unresolved',
50
+ diagnosticMessage: urlPrefix
51
+ ? undefined
52
+ : `Blueprint '${bpName}' has no inline url_prefix — awaiting register_blueprint() call`,
53
+ validationConstraints: { blueprintName: bpName },
54
+ });
55
+ }
56
+ // ── Pass 2: collect decorator stacks and route registrations ─────────
57
+ // We walk backwards from each def statement collecting all decorators above it.
58
+ const decoratorLinePattern = /^\s*@(.+)/;
59
+ const defLinePattern = /^\s*def\s+(\w+)\s*\(/;
60
+ // Collect (defLine index → decorators[])
61
+ const decoratorsByDef = new Map();
62
+ let pendingDecorators = [];
63
+ for (let i = 0; i < lines.length; i++) {
64
+ const trimmed = lines[i].trim();
65
+ const decMatch = trimmed.match(decoratorLinePattern);
66
+ if (decMatch) {
67
+ pendingDecorators.push({ text: decMatch[1].trim(), line: i + 1 });
68
+ continue;
69
+ }
70
+ const defMatch = trimmed.match(defLinePattern);
71
+ if (defMatch) {
72
+ decoratorsByDef.set(i, pendingDecorators);
73
+ pendingDecorators = [];
74
+ continue;
75
+ }
76
+ // Non-decorator, non-def line: reset pending decorators if not blank
77
+ if (trimmed !== '' && !trimmed.startsWith('#')) {
78
+ pendingDecorators = [];
79
+ }
80
+ }
81
+ // ── Pass 3: process each def's decorator stack ────────────────────────
82
+ for (const [defLineIdx, decorators] of decoratorsByDef) {
83
+ const defLine = lines[defLineIdx].trim();
84
+ const defMatch = defLine.match(defLinePattern);
85
+ if (!defMatch)
86
+ continue;
87
+ const funcName = defMatch[1];
88
+ // Separate route decorators from security / kwargs / marshal decorators
89
+ let routePath;
90
+ let routeMethods = ['GET'];
91
+ let routeBlueprintVar;
92
+ let security = parseSecurity(decorators);
93
+ const paramNodes = parseUseKwargs(decorators, funcName, filePath, defLineIdx + 1, routeMethods);
94
+ const responseNodes = parseMarshalWith(decorators, funcName, filePath, defLineIdx + 1);
95
+ for (const dec of decorators) {
96
+ // @blueprint.route('/path', methods=(...))
97
+ const routeMatch = dec.text.match(/^(\w+)\.route\s*\(\s*['"]([^'"]+)['"]\s*(?:,\s*methods\s*=\s*\(([^)]*)\))?\s*\)/);
98
+ if (routeMatch) {
99
+ routeBlueprintVar = routeMatch[1];
100
+ routePath = routeMatch[2];
101
+ if (routeMatch[3]) {
102
+ routeMethods = routeMatch[3]
103
+ .split(',')
104
+ .map((m) => m.trim().replace(/['"]/g, ''))
105
+ .filter((m) => m.length > 0);
106
+ }
107
+ }
108
+ }
109
+ if (routePath !== undefined && routeBlueprintVar !== undefined) {
110
+ // Check if this blueprint var has an inline url_prefix in the same file
111
+ const bpDef = nodes.find((n) => n.kind === 'blueprint-definition' && n.name === routeBlueprintVar);
112
+ const isResolved = (bpDef === null || bpDef === void 0 ? void 0 : bpDef.urlPrefix) !== undefined || (bpDef === null || bpDef === void 0 ? void 0 : bpDef.resolution) === 'resolved';
113
+ nodes.push({
114
+ id: (0, types_1.makeNodeId)(filePath, 'endpoint', funcName, defLineIdx + 1),
115
+ kind: 'endpoint',
116
+ name: funcName,
117
+ filePath,
118
+ line: defLineIdx + 1,
119
+ httpPath: routePath,
120
+ httpMethods: routeMethods,
121
+ protocol: 'rest',
122
+ security,
123
+ resolution: isResolved ? 'resolved' : 'cross-file-unresolved',
124
+ diagnosticMessage: isResolved
125
+ ? undefined
126
+ : `Endpoint '${funcName}' url_prefix depends on register_blueprint() call in another file`,
127
+ });
128
+ nodes.push(...paramNodes);
129
+ nodes.push(...responseNodes);
130
+ }
131
+ }
132
+ // ── Pass 4: collect register_blueprint() calls ────────────────────────
133
+ // Pattern: app.register_blueprint(varName[, url_prefix='/...'])
134
+ const registerPattern = /\bregister_blueprint\s*\(\s*(\w+)(?:\s*,\s*url_prefix\s*=\s*['"]([^'"]*)['"]\s*)?\)/;
135
+ for (let i = 0; i < lines.length; i++) {
136
+ const m = lines[i].match(registerPattern);
137
+ if (!m)
138
+ continue;
139
+ const targetVar = m[1];
140
+ const urlPrefix = m[2];
141
+ nodes.push({
142
+ id: (0, types_1.makeNodeId)(filePath, 'blueprint-registration', targetVar, i + 1),
143
+ kind: 'blueprint-registration',
144
+ name: targetVar,
145
+ filePath,
146
+ line: i + 1,
147
+ urlPrefix: urlPrefix !== null && urlPrefix !== void 0 ? urlPrefix : undefined,
148
+ targetModule: targetVar,
149
+ resolution: 'cross-file-unresolved', // target module may be in another file
150
+ diagnosticMessage: `register_blueprint('${targetVar}') — target may be defined in another file`,
151
+ });
152
+ }
153
+ return nodes;
154
+ }
155
+ }
156
+ exports.FlaskBlueprintDetector = FlaskBlueprintDetector;
157
+ // ─── Security parsing ─────────────────────────────────────────────────────────
158
+ function parseSecurity(decorators) {
159
+ for (const dec of decorators) {
160
+ const text = dec.text;
161
+ // @jwt_required(optional=True) — v4+ optional
162
+ if (/^jwt_required\s*\(\s*optional\s*=\s*True\s*\)/.test(text)) {
163
+ return { type: 'jwt', required: false, optional: true, sourcePattern: '@jwt_required(optional=True)' };
164
+ }
165
+ // @jwt_required(fresh=True)
166
+ if (/^jwt_required\s*\(\s*fresh\s*=\s*True\s*\)/.test(text)) {
167
+ return { type: 'jwt', required: true, optional: false, sourcePattern: '@jwt_required(fresh=True)' };
168
+ }
169
+ // @jwt_required() or @jwt_required — required auth (RULE-SA04)
170
+ if (/^jwt_required(\s*\(\s*\))?$/.test(text)) {
171
+ return { type: 'jwt', required: true, optional: false, sourcePattern: '@jwt_required' };
172
+ }
173
+ // @jwt_optional — v3 bare decorator (RULE-SA04: must NEVER be classified as public)
174
+ if (/^jwt_optional(\s*\(\s*\))?$/.test(text)) {
175
+ return { type: 'jwt', required: false, optional: true, sourcePattern: '@jwt_optional' };
176
+ }
177
+ }
178
+ return undefined;
179
+ }
180
+ // ─── @use_kwargs parsing ──────────────────────────────────────────────────────
181
+ function parseUseKwargs(decorators, funcName, filePath, baseLine, routeMethods) {
182
+ const nodes = [];
183
+ for (const dec of decorators) {
184
+ // @use_kwargs({...}[, location='...'])
185
+ if (!dec.text.startsWith('use_kwargs'))
186
+ continue;
187
+ // Extract explicit location kwarg
188
+ const locationMatch = dec.text.match(/location\s*=\s*['"](\w+)['"]/);
189
+ const explicitLocation = locationMatch ? locationMatch[1] : undefined;
190
+ // Infer location from HTTP methods if not explicit
191
+ const inferredLocation = inferParamLocation(routeMethods, explicitLocation);
192
+ // Extract field names from simple dict patterns like {'tag': fields.Str(), 'limit': fields.Int()}
193
+ const fieldPattern = /['"](\w+)['"]\s*:\s*fields\.\w+/g;
194
+ let fieldMatch;
195
+ while ((fieldMatch = fieldPattern.exec(dec.text)) !== null) {
196
+ const paramName = fieldMatch[1];
197
+ const isRequired = new RegExp(`['"]${paramName}['"].*?required\\s*=\\s*True`).test(dec.text);
198
+ nodes.push({
199
+ id: (0, types_1.makeNodeId)(filePath, 'parameter', `${funcName}.${paramName}`, dec.line),
200
+ kind: 'parameter',
201
+ name: `${funcName}.${paramName}`,
202
+ filePath,
203
+ line: dec.line,
204
+ parameterName: paramName,
205
+ parameterLocation: inferredLocation,
206
+ required: isRequired,
207
+ resolution: 'resolved',
208
+ });
209
+ }
210
+ // Schema class passed directly: @use_kwargs(UserSchema)
211
+ const schemaMatch = dec.text.match(/^use_kwargs\s*\(\s*(\w+Schema)\s*\)/);
212
+ if (schemaMatch) {
213
+ nodes.push({
214
+ id: (0, types_1.makeNodeId)(filePath, 'parameter', `${funcName}.schema:${schemaMatch[1]}`, dec.line),
215
+ kind: 'parameter',
216
+ name: `${funcName}.schema:${schemaMatch[1]}`,
217
+ filePath,
218
+ line: dec.line,
219
+ parameterName: schemaMatch[1],
220
+ parameterLocation: inferredLocation,
221
+ resolution: 'cross-file-unresolved',
222
+ diagnosticMessage: `Schema class '${schemaMatch[1]}' fields not resolved — defined in another file`,
223
+ });
224
+ }
225
+ }
226
+ return nodes;
227
+ }
228
+ function inferParamLocation(methods, explicit) {
229
+ if (explicit)
230
+ return explicit;
231
+ const upper = methods.map((m) => m.toUpperCase());
232
+ if (upper.some((m) => m === 'POST' || m === 'PUT' || m === 'PATCH'))
233
+ return 'json';
234
+ return 'query'; // GET, HEAD, DELETE, OPTIONS
235
+ }
236
+ // ─── @marshal_with parsing ────────────────────────────────────────────────────
237
+ function parseMarshalWith(decorators, funcName, filePath, baseLine) {
238
+ const nodes = [];
239
+ for (const dec of decorators) {
240
+ if (!dec.text.startsWith('marshal_with'))
241
+ continue;
242
+ // Extract code= kwarg
243
+ const codeMatch = dec.text.match(/code\s*=\s*(\d+)/);
244
+ const statusCode = codeMatch ? parseInt(codeMatch[1], 10) : 200;
245
+ // Extract schema name (first argument)
246
+ const schemaMatch = dec.text.match(/^marshal_with\s*\(\s*(\w+)/);
247
+ const schemaName = schemaMatch ? schemaMatch[1] : 'unknown';
248
+ nodes.push({
249
+ id: (0, types_1.makeNodeId)(filePath, 'response-schema', `${funcName}.response:${statusCode}`, dec.line),
250
+ kind: 'response-schema',
251
+ name: `${funcName}.response:${statusCode}`,
252
+ filePath,
253
+ line: dec.line,
254
+ responseStatusCode: statusCode,
255
+ modelName: schemaName !== 'None' ? schemaName : undefined,
256
+ resolution: schemaName === 'unknown' || schemaName === 'None' ? 'resolved' : 'cross-file-unresolved',
257
+ diagnosticMessage: schemaName !== 'None' && schemaName !== 'unknown'
258
+ ? `Response schema '${schemaName}' may be defined in another file`
259
+ : undefined,
260
+ });
261
+ }
262
+ return nodes;
263
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Spring Boot DDD layer detector (Feature 27)
3
+ *
4
+ * Detects from raw Java/Kotlin file content WITHOUT requiring @Service/@Repository
5
+ * annotations (RULE-SA03). Uses structural inference:
6
+ *
7
+ * 1. Repository interfaces — interfaces named XRepository/XRepo/XStore/XGateway
8
+ * or interfaces containing findByX / save / delete methods
9
+ * 2. Repository implementations — classes implementing a XRepository interface
10
+ * 3. CQRS handlers — classes with execute/handle/apply methods taking XCommand/XQuery
11
+ * 4. DDD service layer — classes in 'application'/'command'/'query'/'usecase' packages
12
+ * 5. Domain layer — classes in 'domain' package
13
+ * 6. Infrastructure layer — classes in 'infrastructure'/'persistence'/'adapter' packages
14
+ *
15
+ * RULE-SA01: directory name is used only as a tie-breaker, never as primary evidence
16
+ * RULE-SA03: @Service/@Repository absence does NOT prevent node creation
17
+ */
18
+ import type { DetectedNode, Detector } from './types';
19
+ export declare class SpringDddDetector implements Detector {
20
+ readonly name = "spring-ddd";
21
+ detect(fileContent: string, filePath: string): DetectedNode[];
22
+ }
23
+ //# sourceMappingURL=springDddDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"springDddDetector.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/detectors/springDddDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGtD,qBAAa,iBAAkB,YAAW,QAAQ;IAChD,QAAQ,CAAC,IAAI,gBAAgB;IAE7B,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE;CAiE9D"}
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ /**
3
+ * Spring Boot DDD layer detector (Feature 27)
4
+ *
5
+ * Detects from raw Java/Kotlin file content WITHOUT requiring @Service/@Repository
6
+ * annotations (RULE-SA03). Uses structural inference:
7
+ *
8
+ * 1. Repository interfaces — interfaces named XRepository/XRepo/XStore/XGateway
9
+ * or interfaces containing findByX / save / delete methods
10
+ * 2. Repository implementations — classes implementing a XRepository interface
11
+ * 3. CQRS handlers — classes with execute/handle/apply methods taking XCommand/XQuery
12
+ * 4. DDD service layer — classes in 'application'/'command'/'query'/'usecase' packages
13
+ * 5. Domain layer — classes in 'domain' package
14
+ * 6. Infrastructure layer — classes in 'infrastructure'/'persistence'/'adapter' packages
15
+ *
16
+ * RULE-SA01: directory name is used only as a tie-breaker, never as primary evidence
17
+ * RULE-SA03: @Service/@Repository absence does NOT prevent node creation
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.SpringDddDetector = void 0;
21
+ const types_1 = require("./types");
22
+ class SpringDddDetector {
23
+ constructor() {
24
+ this.name = 'spring-ddd';
25
+ }
26
+ detect(fileContent, filePath) {
27
+ if (!filePath.endsWith('.java') && !filePath.endsWith('.kt'))
28
+ return [];
29
+ const nodes = [];
30
+ const lines = fileContent.split('\n');
31
+ // Infer DDD layer from package path (tie-breaker only — RULE-SA01)
32
+ const dddLayer = inferDddLayerFromPath(filePath);
33
+ // ── Detect repository interfaces ──────────────────────────────────────
34
+ const repoInterfaces = detectRepositoryInterfaces(fileContent, filePath);
35
+ for (const repo of repoInterfaces) {
36
+ nodes.push({
37
+ id: (0, types_1.makeNodeId)(filePath, 'ddd-repository-interface', repo.name, repo.line),
38
+ kind: 'ddd-repository-interface',
39
+ name: repo.name,
40
+ filePath,
41
+ line: repo.line,
42
+ dddLayer: dddLayer !== null && dddLayer !== void 0 ? dddLayer : 'domain',
43
+ resolution: 'resolved',
44
+ validationConstraints: { methods: repo.methods.join(',') },
45
+ });
46
+ }
47
+ // ── Detect repository implementations ─────────────────────────────────
48
+ const repoImpls = detectRepositoryImplementations(fileContent, filePath);
49
+ for (const impl of repoImpls) {
50
+ nodes.push({
51
+ id: (0, types_1.makeNodeId)(filePath, 'ddd-repository-impl', impl.className, impl.line),
52
+ kind: 'ddd-repository-impl',
53
+ name: impl.className,
54
+ filePath,
55
+ line: impl.line,
56
+ dddLayer: dddLayer !== null && dddLayer !== void 0 ? dddLayer : 'infrastructure',
57
+ implementsInterface: impl.interfaceName,
58
+ resolution: impl.interfaceResolved ? 'resolved' : 'cross-file-unresolved',
59
+ diagnosticMessage: impl.interfaceResolved
60
+ ? undefined
61
+ : `Interface '${impl.interfaceName}' may be defined in another file`,
62
+ });
63
+ }
64
+ // ── Detect CQRS handlers ───────────────────────────────────────────────
65
+ const cqrsHandlers = detectCqrsHandlers(fileContent, filePath);
66
+ for (const handler of cqrsHandlers) {
67
+ nodes.push({
68
+ id: (0, types_1.makeNodeId)(filePath, 'cqrs-handler', handler.className, handler.line),
69
+ kind: 'cqrs-handler',
70
+ name: handler.className,
71
+ filePath,
72
+ line: handler.line,
73
+ dddLayer: dddLayer !== null && dddLayer !== void 0 ? dddLayer : 'application',
74
+ cqrsHandlerType: handler.handlerType,
75
+ cqrsParameterType: handler.parameterType,
76
+ resolution: 'resolved',
77
+ validationConstraints: { handleMethod: handler.handleMethod },
78
+ });
79
+ }
80
+ // ── Detect application service classes ────────────────────────────────
81
+ const serviceClasses = detectServiceClasses(lines, filePath, dddLayer);
82
+ nodes.push(...serviceClasses);
83
+ return nodes;
84
+ }
85
+ }
86
+ exports.SpringDddDetector = SpringDddDetector;
87
+ function detectRepositoryInterfaces(source, filePath) {
88
+ var _a, _b;
89
+ const results = [];
90
+ const lines = source.split('\n');
91
+ // Named interfaces: interface ArticleRepository / ArticleRepo / ArticleStore / ArticleGateway
92
+ const namedPattern = /(?:public\s+)?interface\s+(\w+(?:Repository|Repo|Store|Gateway))\b/;
93
+ let currentIface = null;
94
+ let braceDepth = 0;
95
+ for (let i = 0; i < lines.length; i++) {
96
+ const line = lines[i];
97
+ if (!currentIface) {
98
+ const m = line.match(namedPattern);
99
+ if (m) {
100
+ currentIface = { name: m[1], methods: [], line: i + 1 };
101
+ braceDepth = 0;
102
+ }
103
+ }
104
+ if (currentIface) {
105
+ braceDepth += ((_a = line.match(/\{/g)) !== null && _a !== void 0 ? _a : []).length;
106
+ braceDepth -= ((_b = line.match(/\}/g)) !== null && _b !== void 0 ? _b : []).length;
107
+ const methodMatch = line.match(/\b(findBy\w+|find\w+|getBy\w+|save|saveAll|delete|deleteById|existsBy\w+|countBy\w+)\s*\(/);
108
+ if (methodMatch)
109
+ currentIface.methods.push(methodMatch[1]);
110
+ if (braceDepth <= 0) {
111
+ if (currentIface.methods.length > 0)
112
+ results.push(currentIface);
113
+ currentIface = null;
114
+ }
115
+ }
116
+ }
117
+ // Also detect generic interfaces that have at least 2 repository-style methods
118
+ const genericPattern = /(?:public\s+)?interface\s+(\w+)\b/g;
119
+ let gm;
120
+ while ((gm = genericPattern.exec(source)) !== null) {
121
+ const name = gm[1];
122
+ if (results.some((r) => r.name === name))
123
+ continue;
124
+ const startIdx = gm.index;
125
+ const braceStart = source.indexOf('{', startIdx);
126
+ if (braceStart < 0)
127
+ continue;
128
+ const body = extractBraceBody(source, braceStart);
129
+ const repoMethodPattern = /\b(findBy\w+|save|delete|deleteById)\s*\(/g;
130
+ const methods = [];
131
+ let rm;
132
+ while ((rm = repoMethodPattern.exec(body)) !== null)
133
+ methods.push(rm[1]);
134
+ if (methods.length >= 2) {
135
+ const lineNum = source.substring(0, startIdx).split('\n').length;
136
+ results.push({ name, methods, line: lineNum });
137
+ }
138
+ }
139
+ return results;
140
+ }
141
+ function detectRepositoryImplementations(source, filePath) {
142
+ const results = [];
143
+ const implPattern = /class\s+(\w+)(?:\s+extends\s+\w+)?\s+implements\s+([\w,\s<>]+?)(?:\{|$)/gm;
144
+ let m;
145
+ while ((m = implPattern.exec(source)) !== null) {
146
+ const className = m[1];
147
+ const ifaceList = m[2];
148
+ // Look for *Repository/*Repo/*Store/*Gateway in the implements list
149
+ const repoIfaceMatch = ifaceList.match(/\b(\w+(?:Repository|Repo|Store|Gateway))\b/);
150
+ if (!repoIfaceMatch)
151
+ continue;
152
+ const lineNum = source.substring(0, m.index).split('\n').length;
153
+ results.push({
154
+ className,
155
+ interfaceName: repoIfaceMatch[1],
156
+ interfaceResolved: false, // cross-file resolution needed
157
+ line: lineNum,
158
+ });
159
+ }
160
+ return results;
161
+ }
162
+ function detectCqrsHandlers(source, filePath) {
163
+ const results = [];
164
+ const lines = source.split('\n');
165
+ // Pattern: public ReturnType execute/handle/apply(CreateArticleCommand cmd)
166
+ const handlerPattern = /(?:public\s+\S+\s+)?(execute|handle|apply)\s*\(\s*(\w+(Command|Query|Event))\s+\w+\s*\)/;
167
+ let currentClass = '';
168
+ for (let i = 0; i < lines.length; i++) {
169
+ const line = lines[i];
170
+ const classMatch = line.match(/class\s+(\w+)/);
171
+ if (classMatch)
172
+ currentClass = classMatch[1];
173
+ const handlerMatch = line.match(handlerPattern);
174
+ if (handlerMatch && currentClass) {
175
+ const handlerTypeSuffix = handlerMatch[3].toLowerCase();
176
+ results.push({
177
+ className: currentClass,
178
+ handlerType: handlerTypeSuffix,
179
+ handleMethod: handlerMatch[1],
180
+ parameterType: handlerMatch[2],
181
+ line: i + 1,
182
+ });
183
+ }
184
+ }
185
+ return results;
186
+ }
187
+ function detectServiceClasses(lines, filePath, dddLayer) {
188
+ const nodes = [];
189
+ let currentClass = '';
190
+ let currentClassLine = 0;
191
+ for (let i = 0; i < lines.length; i++) {
192
+ const line = lines[i];
193
+ // class ArticleCommandService / ArticleQueryService / ArticleApplicationService
194
+ const classMatch = line.match(/class\s+(\w+(?:CommandService|QueryService|ApplicationService|UseCase|Interactor))\b/);
195
+ if (classMatch) {
196
+ currentClass = classMatch[1];
197
+ currentClassLine = i + 1;
198
+ nodes.push({
199
+ id: (0, types_1.makeNodeId)(filePath, 'ddd-service', currentClass, currentClassLine),
200
+ kind: 'ddd-service',
201
+ name: currentClass,
202
+ filePath,
203
+ line: currentClassLine,
204
+ dddLayer: dddLayer !== null && dddLayer !== void 0 ? dddLayer : 'application',
205
+ resolution: 'resolved',
206
+ });
207
+ }
208
+ }
209
+ return nodes;
210
+ }
211
+ function inferDddLayerFromPath(filePath) {
212
+ const normalized = filePath.replace(/\\/g, '/').toLowerCase();
213
+ if (normalized.includes('/domain/'))
214
+ return 'domain';
215
+ if (normalized.includes('/application/') ||
216
+ normalized.includes('/command/') ||
217
+ normalized.includes('/query/') ||
218
+ normalized.includes('/usecase/'))
219
+ return 'application';
220
+ if (normalized.includes('/infrastructure/') ||
221
+ normalized.includes('/persistence/') ||
222
+ normalized.includes('/adapter/'))
223
+ return 'infrastructure';
224
+ return undefined;
225
+ }
226
+ function extractBraceBody(source, openBrace) {
227
+ let depth = 1;
228
+ let i = openBrace + 1;
229
+ while (i < source.length && depth > 0) {
230
+ if (source[i] === '{')
231
+ depth++;
232
+ if (source[i] === '}')
233
+ depth--;
234
+ i++;
235
+ }
236
+ return source.substring(openBrace, i);
237
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Shared types for pipeline detectors (Feature 27).
3
+ *
4
+ * Detectors analyse raw file content (string) and return DetectedNode[]
5
+ * that the cross-file resolution pass consumes to build graph nodes.
6
+ */
7
+ import type { SecurityClassification, NodeResolution, EndpointProtocol, DecoratorStack } from '../../ast/astTypes';
8
+ export type DetectedNodeKind = 'endpoint' | 'api-call' | 'route-prefix' | 'auth-guard' | 'auth-interceptor' | 'auth-middleware' | 'injectable-service' | 'ddd-service' | 'ddd-repository-interface' | 'ddd-repository-impl' | 'cqrs-handler' | 'model' | 'model-field' | 'validation-rule' | 'model-method' | 'model-hook' | 'response-schema' | 'vuex-action' | 'vuex-dispatch' | 'error-handler' | 'exception-branch' | 'router-mount' | 'middleware-chain' | 'blueprint-definition' | 'blueprint-registration' | 'parameter' | 'mybatis-mapper' | 'mybatis-query';
9
+ /**
10
+ * A node detected by a file-content detector.
11
+ *
12
+ * Detectors never emit endpoint nodes before url_prefix / router.use() prefix
13
+ * is resolved (RULE-SA02). When the URL depends on cross-file data that is not
14
+ * yet available the detector emits the node with `resolution: 'cross-file-unresolved'`.
15
+ */
16
+ export interface DetectedNode {
17
+ /** Stable within a single scan — derived from filePath + kind + name + line */
18
+ id: string;
19
+ /** Kind of semantic construct this node represents */
20
+ kind: DetectedNodeKind;
21
+ /** Human-readable name of the construct (function name, class name, path, etc.) */
22
+ name: string;
23
+ /** Absolute path to the file that was scanned */
24
+ filePath: string;
25
+ /** 1-based line number of the primary token */
26
+ line?: number;
27
+ /** HTTP path as written in source — may be a prefix or a full path */
28
+ httpPath?: string;
29
+ /** HTTP methods for this endpoint */
30
+ httpMethods?: string[];
31
+ /** Transport protocol */
32
+ protocol?: EndpointProtocol;
33
+ /** Security classification when statically determinable */
34
+ security?: SecurityClassification;
35
+ /**
36
+ * Resolution status:
37
+ * - `resolved` — all required cross-file data is available
38
+ * - `cross-file-unresolved` — depends on data from another file not yet scanned
39
+ * - `partial` — some data resolved, some not
40
+ */
41
+ resolution: NodeResolution;
42
+ /** Human-readable reason when resolution is not 'resolved' */
43
+ diagnosticMessage?: string;
44
+ /** All decorators / annotations associated with this node */
45
+ decoratorStack?: DecoratorStack;
46
+ /** Parameter name when kind is 'parameter' */
47
+ parameterName?: string;
48
+ /** Parameter location: 'query' | 'json' | 'headers' | 'path' */
49
+ parameterLocation?: string;
50
+ /** Whether the parameter is required */
51
+ required?: boolean;
52
+ /** Validation constraints (type, min, max, pattern, etc.) */
53
+ validationConstraints?: Record<string, string | number | boolean>;
54
+ /** For injectable-service nodes: the injected token/class name */
55
+ injectedClass?: string;
56
+ /** How injection was detected */
57
+ injectionStyle?: 'constructor' | 'inject-fn' | 'decorator' | 'property';
58
+ /** The url_prefix / mount path when kind is 'route-prefix' or 'router-mount' */
59
+ urlPrefix?: string;
60
+ /** Target module or variable being mounted */
61
+ targetModule?: string;
62
+ /** Linked model/class name for validation / method nodes */
63
+ modelName?: string;
64
+ /** For response-schema nodes: the HTTP status code */
65
+ responseStatusCode?: number;
66
+ /** DDD layer classification inferred from code (RULE-SA03) */
67
+ dddLayer?: 'domain' | 'application' | 'infrastructure' | 'adapter';
68
+ /** Interface name being implemented */
69
+ implementsInterface?: string;
70
+ /** CQRS handler type */
71
+ cqrsHandlerType?: 'command' | 'query' | 'event';
72
+ /** CQRS parameter type (e.g. 'CreateArticleCommand') */
73
+ cqrsParameterType?: string;
74
+ /** For error-handler / exception-branch nodes: the error name or type matched */
75
+ errorName?: string;
76
+ /** HTTP status code produced by this error branch */
77
+ errorStatusCode?: number;
78
+ }
79
+ /**
80
+ * Common interface every detector must implement.
81
+ */
82
+ export interface Detector {
83
+ /** Human-readable detector name */
84
+ readonly name: string;
85
+ /**
86
+ * Analyse the raw file content and return all detected nodes.
87
+ *
88
+ * Rules that apply to ALL detectors:
89
+ * - NEVER infer node type from directory name (RULE-SA01)
90
+ * - NEVER emit an endpoint node before url_prefix is resolved (RULE-SA02)
91
+ * - Flag unresolved nodes as `resolution: 'cross-file-unresolved'` — do NOT drop them
92
+ * - @jwt_optional → security.optional = true, NEVER security = 'public'
93
+ */
94
+ detect(fileContent: string, filePath: string): DetectedNode[];
95
+ }
96
+ export declare function makeNodeId(filePath: string, kind: DetectedNodeKind, name: string, line?: number): string;
97
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/detectors/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * Shared types for pipeline detectors (Feature 27).
4
+ *
5
+ * Detectors analyse raw file content (string) and return DetectedNode[]
6
+ * that the cross-file resolution pass consumes to build graph nodes.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.makeNodeId = makeNodeId;
10
+ // ─── Utility: deterministic node ID ──────────────────────────────────────────
11
+ function makeNodeId(filePath, kind, name, line) {
12
+ const safeFile = filePath.replace(/\\/g, '/');
13
+ const safeName = name.replace(/\s+/g, '_');
14
+ return `${safeFile}:${kind}:${safeName}${line != null ? `:${line}` : ''}`;
15
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"reporting.d.ts","sourceRoot":"","sources":["../../src/reporting.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAIzD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,+GAA+G;IAC/G,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,eAAe,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;CAClB;AAID,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAMxD;AAwND;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACvC,aAAa,CAAC,EAAE,iBAAiB,GAChC,IAAI,CAqBN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,MAAM,EAAE,CAYV"}
1
+ {"version":3,"file":"reporting.d.ts","sourceRoot":"","sources":["../../src/reporting.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAIzD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,+GAA+G;IAC/G,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,eAAe,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;CAClB;AAID,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAMxD;AAwND;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACvC,aAAa,CAAC,EAAE,iBAAiB,GAChC,IAAI,CAqBN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,MAAM,EAAE,CAaV"}