api-tests-coverage 1.0.14 → 1.0.16

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 (118) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-C2jmWITn.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-DE6cyzJb.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-B-Q4nGPT.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-C_5dqWCI.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-DbGIO6Kt.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-CAFpcejP.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-Di9el3wE.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-DY1boKsq.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-BSL35gyW.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-eTDXrKrv.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-M-8I3jEy.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-bSA0XiS0.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BrOIYUBs.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CliaQGD4.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-CyhcxGB1.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-BkGN4Cpz.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-BkGN4Cpz.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-Cvq8JuOb.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-BUkL7Wtq.js +1 -0
  20. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-B8oEROJc.js +4 -0
  21. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-5uki9Dw8.js +24 -0
  22. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BRNhmby2.js +43 -0
  23. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-D-ku_X8U.js +24 -0
  24. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-DGl6gPe2.js +60 -0
  25. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Co89qYBD.js +162 -0
  26. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-2r3WpWQC.js +267 -0
  27. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-CuJ5l3TK.js +65 -0
  28. package/dist/dashboard/dist/assets/graph-ZtgwAPQj.js +1 -0
  29. package/dist/dashboard/dist/assets/index-D3sRJga7.js +777 -0
  30. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-ujnMqVz3.js +2 -0
  31. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DQzfeBIo.js +139 -0
  32. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-ueIaoeks.js +89 -0
  33. package/dist/dashboard/dist/assets/layout-B1fTYUMj.js +1 -0
  34. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B7wYeLe1.js +68 -0
  35. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Bf8vKEOf.js +30 -0
  36. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-CM8qiFLR.js +7 -0
  37. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-DPTtP4Ve.js +64 -0
  38. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DEVTdH0h.js +10 -0
  39. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-Bjr5wgXg.js +145 -0
  40. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DDrhZYly.js +1 -0
  41. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Im6pH8C-.js +1 -0
  42. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-DAT3r9va.js +61 -0
  43. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-BlA8rg0m.js +162 -0
  44. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-7aSkQtVu.js +7 -0
  45. package/dist/dashboard/dist/index.html +1 -1
  46. package/dist/src/ast/astTypes.d.ts +86 -0
  47. package/dist/src/ast/astTypes.d.ts.map +1 -1
  48. package/dist/src/discovery/frameworkDetector.d.ts +28 -0
  49. package/dist/src/discovery/frameworkDetector.d.ts.map +1 -0
  50. package/dist/src/discovery/frameworkDetector.js +189 -0
  51. package/dist/src/discovery/projectDiscovery.d.ts +5 -1
  52. package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
  53. package/dist/src/discovery/projectDiscovery.js +4 -0
  54. package/dist/src/inference/routeInference.d.ts.map +1 -1
  55. package/dist/src/inference/routeInference.js +224 -1
  56. package/dist/src/languages/java/graphqlSchemaParser.d.ts +65 -0
  57. package/dist/src/languages/java/graphqlSchemaParser.d.ts.map +1 -0
  58. package/dist/src/languages/java/graphqlSchemaParser.js +164 -0
  59. package/dist/src/languages/java/mybatisXmlParser.d.ts +52 -0
  60. package/dist/src/languages/java/mybatisXmlParser.d.ts.map +1 -0
  61. package/dist/src/languages/java/mybatisXmlParser.js +107 -0
  62. package/dist/src/languages/javascript/angularDetector.d.ts +74 -0
  63. package/dist/src/languages/javascript/angularDetector.d.ts.map +1 -0
  64. package/dist/src/languages/javascript/angularDetector.js +194 -0
  65. package/dist/src/languages/javascript/hapiDetector.d.ts +40 -0
  66. package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -0
  67. package/dist/src/languages/javascript/hapiDetector.js +131 -0
  68. package/dist/src/languages/javascript/mongooseDetector.d.ts +65 -0
  69. package/dist/src/languages/javascript/mongooseDetector.d.ts.map +1 -0
  70. package/dist/src/languages/javascript/mongooseDetector.js +237 -0
  71. package/dist/src/languages/javascript/vueDetector.d.ts +40 -0
  72. package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -0
  73. package/dist/src/languages/javascript/vueDetector.js +87 -0
  74. package/dist/src/languages/python/index.d.ts +5 -1
  75. package/dist/src/languages/python/index.d.ts.map +1 -1
  76. package/dist/src/languages/python/index.js +167 -2
  77. package/dist/src/languages/python/testPatternDetector.d.ts +70 -0
  78. package/dist/src/languages/python/testPatternDetector.d.ts.map +1 -0
  79. package/dist/src/languages/python/testPatternDetector.js +201 -0
  80. package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -1
  81. package/dist/src/pipeline/stages/ast/astStage.js +6 -0
  82. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts +44 -0
  83. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -0
  84. package/dist/src/pipeline/stages/ast/baseUrlComposer.js +83 -0
  85. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts +54 -0
  86. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts.map +1 -0
  87. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.js +88 -0
  88. package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -1
  89. package/dist/src/pipeline/stages/ast/crossFileResolver.js +10 -1
  90. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts +39 -0
  91. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -0
  92. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +81 -0
  93. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts +18 -0
  94. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -0
  95. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +77 -0
  96. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts +46 -0
  97. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -0
  98. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +238 -0
  99. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts +17 -0
  100. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts.map +1 -0
  101. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.js +65 -0
  102. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts +17 -0
  103. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts.map +1 -0
  104. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.js +114 -0
  105. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts +27 -0
  106. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -0
  107. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +74 -0
  108. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts +17 -0
  109. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -0
  110. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +55 -0
  111. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts +24 -0
  112. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -0
  113. package/dist/src/pipeline/stages/ast/rulesEnforcer.js +120 -0
  114. package/dist/src/pipeline/stages/ast/types.d.ts +114 -1
  115. package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -1
  116. package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
  117. package/dist/src/pipeline/stages/tia/testLayerClassifier.js +5 -0
  118. package/package.json +1 -1
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ /**
3
+ * Mongoose schema/model detector (Feature 27, Sub-PR 6)
4
+ *
5
+ * Detects:
6
+ * 1. mongoose.Schema({...}) — field definitions, validation rules
7
+ * 2. mongoose.model('Name', schema) — model creation
8
+ * 3. .methods.* — instance method definitions
9
+ * 4. .pre()/.post() hooks — lifecycle hooks
10
+ * 5. .plugin(validator) — plugin registrations
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.detectMongooseModels = detectMongooseModels;
14
+ exports.detectExpressErrorHandlers = detectExpressErrorHandlers;
15
+ exports.detectExpressAuthMiddleware = detectExpressAuthMiddleware;
16
+ /**
17
+ * Detect Mongoose model/schema definitions from JS/TS source text.
18
+ */
19
+ function detectMongooseModels(sourceText, filePath) {
20
+ var _a, _b, _c, _d;
21
+ const models = [];
22
+ const lines = sourceText.split('\n');
23
+ // Track schema variables and their fields
24
+ const schemaVars = new Map();
25
+ // Track methods, hooks, plugins per schema var
26
+ const schemaMethods = new Map();
27
+ const schemaHooks = new Map();
28
+ const schemaPlugins = new Map();
29
+ for (let i = 0; i < lines.length; i++) {
30
+ const line = lines[i];
31
+ // new mongoose.Schema({...}) or new Schema({...})
32
+ const schemaMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\.)?Schema\s*\(/);
33
+ if (schemaMatch) {
34
+ const varName = schemaMatch[1];
35
+ const fields = extractSchemaFields(sourceText, i);
36
+ schemaVars.set(varName, { fields, line: i + 1 });
37
+ continue;
38
+ }
39
+ // mongoose.model('Name', schema)
40
+ const modelMatch = line.match(/mongoose\.model\s*\(\s*['"](\w+)['"]\s*,\s*(\w+)/);
41
+ if (modelMatch) {
42
+ const modelName = modelMatch[1];
43
+ const schemaVar = modelMatch[2];
44
+ const schemaInfo = schemaVars.get(schemaVar);
45
+ models.push({
46
+ modelName,
47
+ schemaVariable: schemaVar,
48
+ fields: (_a = schemaInfo === null || schemaInfo === void 0 ? void 0 : schemaInfo.fields) !== null && _a !== void 0 ? _a : [],
49
+ methods: (_b = schemaMethods.get(schemaVar)) !== null && _b !== void 0 ? _b : [],
50
+ hooks: (_c = schemaHooks.get(schemaVar)) !== null && _c !== void 0 ? _c : [],
51
+ plugins: (_d = schemaPlugins.get(schemaVar)) !== null && _d !== void 0 ? _d : [],
52
+ sourceFile: filePath,
53
+ line: i + 1,
54
+ });
55
+ continue;
56
+ }
57
+ // schema.methods.methodName = function
58
+ const methodMatch = line.match(/(\w+)\.methods\.(\w+)\s*=/);
59
+ if (methodMatch) {
60
+ const varName = methodMatch[1];
61
+ if (!schemaMethods.has(varName))
62
+ schemaMethods.set(varName, []);
63
+ schemaMethods.get(varName).push(methodMatch[2]);
64
+ continue;
65
+ }
66
+ // schema.pre('save', ...) or schema.post('save', ...)
67
+ const hookMatch = line.match(/(\w+)\.(pre|post)\s*\(\s*['"](\w+)['"]/);
68
+ if (hookMatch) {
69
+ const varName = hookMatch[1];
70
+ if (!schemaHooks.has(varName))
71
+ schemaHooks.set(varName, []);
72
+ schemaHooks.get(varName).push({
73
+ stage: hookMatch[2],
74
+ event: hookMatch[3],
75
+ });
76
+ continue;
77
+ }
78
+ // schema.plugin(uniqueValidator)
79
+ const pluginMatch = line.match(/(\w+)\.plugin\s*\(\s*(\w+)/);
80
+ if (pluginMatch) {
81
+ const varName = pluginMatch[1];
82
+ if (!schemaPlugins.has(varName))
83
+ schemaPlugins.set(varName, []);
84
+ schemaPlugins.get(varName).push(pluginMatch[2]);
85
+ }
86
+ }
87
+ return models;
88
+ }
89
+ /**
90
+ * Extract field definitions from a schema constructor.
91
+ * Simple regex extraction — handles the most common patterns.
92
+ */
93
+ function extractSchemaFields(source, startLine) {
94
+ var _a;
95
+ const fields = [];
96
+ const lines = source.split('\n');
97
+ // Find the opening brace of the schema definition
98
+ let braceDepth = 0;
99
+ let inSchema = false;
100
+ let schemaBody = '';
101
+ for (let i = startLine; i < Math.min(startLine + 50, lines.length); i++) {
102
+ const line = lines[i];
103
+ for (const ch of line) {
104
+ if (ch === '(' || ch === '{')
105
+ braceDepth++;
106
+ if (ch === ')' || ch === '}')
107
+ braceDepth--;
108
+ if (braceDepth >= 2 && !inSchema) {
109
+ inSchema = true;
110
+ }
111
+ if (inSchema)
112
+ schemaBody += ch;
113
+ if (inSchema && braceDepth < 2) {
114
+ inSchema = false;
115
+ break;
116
+ }
117
+ }
118
+ if (schemaBody && !inSchema)
119
+ break;
120
+ if (inSchema)
121
+ schemaBody += '\n';
122
+ }
123
+ // Parse field definitions from the schema body
124
+ // Pattern: fieldName: { type: Type, required: true, unique: true, enum: [...] }
125
+ const fieldPattern = /(\w+)\s*:\s*\{([^}]+)\}/g;
126
+ let match;
127
+ while ((match = fieldPattern.exec(schemaBody)) !== null) {
128
+ const name = match[1];
129
+ const body = match[2];
130
+ const typeMatch = body.match(/type\s*:\s*(\w+)/);
131
+ const required = /required\s*:\s*true/.test(body);
132
+ const unique = /unique\s*:\s*true/.test(body);
133
+ const enumMatch = body.match(/enum\s*:\s*\[([^\]]+)\]/);
134
+ const field = {
135
+ name,
136
+ type: (_a = typeMatch === null || typeMatch === void 0 ? void 0 : typeMatch[1]) !== null && _a !== void 0 ? _a : 'Mixed',
137
+ };
138
+ if (required)
139
+ field.required = true;
140
+ if (unique)
141
+ field.unique = true;
142
+ if (enumMatch) {
143
+ field.enumValues = enumMatch[1].split(',').map((s) => s.trim().replace(/['"]/g, '')).filter(Boolean);
144
+ }
145
+ fields.push(field);
146
+ }
147
+ // Also handle shorthand: fieldName: Type (e.g., fieldName: String)
148
+ const shorthandPattern = /(\w+)\s*:\s*(String|Number|Boolean|Date|ObjectId|Buffer|Mixed|Map)\b/g;
149
+ while ((match = shorthandPattern.exec(schemaBody)) !== null) {
150
+ const name = match[1];
151
+ // Skip if already captured as an object definition
152
+ if (!fields.some((f) => f.name === name)) {
153
+ fields.push({ name, type: match[2] });
154
+ }
155
+ }
156
+ return fields;
157
+ }
158
+ /**
159
+ * Detect Express 4-argument error handler middleware.
160
+ * Pattern: function(err, req, res, next) or (err, req, res, next) =>
161
+ */
162
+ function detectExpressErrorHandlers(sourceText, filePath) {
163
+ const handlers = [];
164
+ const lines = sourceText.split('\n');
165
+ // Pattern: matches 4-argument functions (err, req, res, next)
166
+ const errorHandlerPattern = /(?:function\s+(\w+))?\s*\(\s*(?:err|error)\s*,\s*req\s*,\s*res\s*,\s*next\s*\)/;
167
+ for (let i = 0; i < lines.length; i++) {
168
+ const line = lines[i];
169
+ const match = line.match(errorHandlerPattern);
170
+ if (!match)
171
+ continue;
172
+ const functionName = match[1];
173
+ const errorTypes = [];
174
+ const statusCodes = [];
175
+ // Scan ahead for error type checks and status codes
176
+ for (let j = i; j < Math.min(i + 30, lines.length); j++) {
177
+ const scanLine = lines[j];
178
+ // err.name === 'ValidationError'
179
+ const errorTypeMatch = scanLine.match(/err(?:or)?\.name\s*===?\s*['"](\w+)['"]/);
180
+ if (errorTypeMatch)
181
+ errorTypes.push(errorTypeMatch[1]);
182
+ // res.status(404)
183
+ const statusMatch = scanLine.match(/res\.status\s*\(\s*(\d{3})\s*\)/);
184
+ if (statusMatch)
185
+ statusCodes.push(parseInt(statusMatch[1], 10));
186
+ }
187
+ handlers.push({
188
+ functionName,
189
+ errorTypes,
190
+ statusCodes,
191
+ sourceFile: filePath,
192
+ line: i + 1,
193
+ });
194
+ }
195
+ return handlers;
196
+ }
197
+ function detectExpressAuthMiddleware(sourceText, filePath) {
198
+ const middleware = [];
199
+ const lines = sourceText.split('\n');
200
+ for (let i = 0; i < lines.length; i++) {
201
+ const line = lines[i];
202
+ // router.use(auth.required) or app.use(auth.required)
203
+ const simpleAuth = line.match(/(?:router|app)\.use\s*\(\s*auth\.(required|optional)/);
204
+ if (simpleAuth) {
205
+ middleware.push({
206
+ authType: simpleAuth[1],
207
+ sourcePattern: simpleAuth[0],
208
+ sourceFile: filePath,
209
+ line: i + 1,
210
+ });
211
+ continue;
212
+ }
213
+ // router.use('/path', auth.required, router)
214
+ const pathAuth = line.match(/(?:router|app)\.use\s*\(\s*['"]([^'"]+)['"]\s*,\s*auth\.(required|optional)/);
215
+ if (pathAuth) {
216
+ middleware.push({
217
+ authType: pathAuth[2],
218
+ pathPrefix: pathAuth[1],
219
+ sourcePattern: pathAuth[0],
220
+ sourceFile: filePath,
221
+ line: i + 1,
222
+ });
223
+ continue;
224
+ }
225
+ // passport.authenticate('jwt', { session: false })
226
+ const passportAuth = line.match(/passport\.authenticate\s*\(\s*['"](\w+)['"]/);
227
+ if (passportAuth) {
228
+ middleware.push({
229
+ authType: 'required',
230
+ sourcePattern: passportAuth[0],
231
+ sourceFile: filePath,
232
+ line: i + 1,
233
+ });
234
+ }
235
+ }
236
+ return middleware;
237
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Vue.js/Vuex pattern detector (Feature 27, Sub-PR 8)
3
+ *
4
+ * Detects:
5
+ * 1. axios.defaults.baseURL — global base URL
6
+ * 2. Vuex actions with API calls (ApiService.get, axios.get, etc.)
7
+ * 3. this.$store.dispatch('actionName') — component dispatches
8
+ * 4. ApiService.get('resource') — service-layer API calls
9
+ */
10
+ export interface VueApiCall {
11
+ actionName?: string;
12
+ method: string;
13
+ urlPattern: string;
14
+ baseUrl?: string;
15
+ sourceFile: string;
16
+ line?: number;
17
+ }
18
+ export interface VuexDispatch {
19
+ actionName: string;
20
+ sourceFile: string;
21
+ line?: number;
22
+ }
23
+ export interface VueBaseUrl {
24
+ url: string;
25
+ sourceFile: string;
26
+ line?: number;
27
+ }
28
+ /**
29
+ * Detect axios.defaults.baseURL settings.
30
+ */
31
+ export declare function detectAxiosBaseUrl(sourceText: string, filePath: string): VueBaseUrl | undefined;
32
+ /**
33
+ * Detect Vuex action definitions with API calls.
34
+ */
35
+ export declare function detectVuexActions(sourceText: string, filePath: string): VueApiCall[];
36
+ /**
37
+ * Detect this.$store.dispatch() or store.dispatch() calls.
38
+ */
39
+ export declare function detectVuexDispatches(sourceText: string, filePath: string): VuexDispatch[];
40
+ //# sourceMappingURL=vueDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vueDetector.d.ts","sourceRoot":"","sources":["../../../../src/languages/javascript/vueDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAO/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CA4CpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAkBzF"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * Vue.js/Vuex pattern detector (Feature 27, Sub-PR 8)
4
+ *
5
+ * Detects:
6
+ * 1. axios.defaults.baseURL — global base URL
7
+ * 2. Vuex actions with API calls (ApiService.get, axios.get, etc.)
8
+ * 3. this.$store.dispatch('actionName') — component dispatches
9
+ * 4. ApiService.get('resource') — service-layer API calls
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.detectAxiosBaseUrl = detectAxiosBaseUrl;
13
+ exports.detectVuexActions = detectVuexActions;
14
+ exports.detectVuexDispatches = detectVuexDispatches;
15
+ /**
16
+ * Detect axios.defaults.baseURL settings.
17
+ */
18
+ function detectAxiosBaseUrl(sourceText, filePath) {
19
+ const match = sourceText.match(/axios\.defaults\.baseURL\s*=\s*['"`]([^'"`]+)['"`]/);
20
+ if (match) {
21
+ const line = sourceText.substring(0, match.index).split('\n').length;
22
+ return { url: match[1], sourceFile: filePath, line };
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * Detect Vuex action definitions with API calls.
28
+ */
29
+ function detectVuexActions(sourceText, filePath) {
30
+ const calls = [];
31
+ const lines = sourceText.split('\n');
32
+ let currentAction = '';
33
+ for (let i = 0; i < lines.length; i++) {
34
+ const line = lines[i];
35
+ // Vuex action definition: [ACTION_NAME]({ commit }, payload) {
36
+ // or: actionName({ commit }) {
37
+ const actionMatch = line.match(/(?:\[(\w+)\]|(\w+))\s*\(\s*\{\s*(?:commit|dispatch|state|getters|rootState)/);
38
+ if (actionMatch) {
39
+ currentAction = actionMatch[1] || actionMatch[2] || '';
40
+ continue;
41
+ }
42
+ // ApiService.get/post/put/delete('resource')
43
+ const apiServiceMatch = line.match(/ApiService\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/i);
44
+ if (apiServiceMatch) {
45
+ calls.push({
46
+ actionName: currentAction || undefined,
47
+ method: apiServiceMatch[1].toUpperCase(),
48
+ urlPattern: apiServiceMatch[2],
49
+ sourceFile: filePath,
50
+ line: i + 1,
51
+ });
52
+ continue;
53
+ }
54
+ // axios.get/post/put/delete('url')
55
+ const axiosMatch = line.match(/axios\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/i);
56
+ if (axiosMatch) {
57
+ calls.push({
58
+ actionName: currentAction || undefined,
59
+ method: axiosMatch[1].toUpperCase(),
60
+ urlPattern: axiosMatch[2],
61
+ sourceFile: filePath,
62
+ line: i + 1,
63
+ });
64
+ }
65
+ }
66
+ return calls;
67
+ }
68
+ /**
69
+ * Detect this.$store.dispatch() or store.dispatch() calls.
70
+ */
71
+ function detectVuexDispatches(sourceText, filePath) {
72
+ const dispatches = [];
73
+ const lines = sourceText.split('\n');
74
+ for (let i = 0; i < lines.length; i++) {
75
+ const line = lines[i];
76
+ // this.$store.dispatch('ACTION_NAME') or store.dispatch('actionName')
77
+ const match = line.match(/(?:this\.\$store|store)\.dispatch\s*\(\s*['"](\w+)['"]/);
78
+ if (match) {
79
+ dispatches.push({
80
+ actionName: match[1],
81
+ sourceFile: filePath,
82
+ line: i + 1,
83
+ });
84
+ }
85
+ }
86
+ return dispatches;
87
+ }
@@ -2,7 +2,7 @@
2
2
  * Python language analyzer using tree-sitter-python.
3
3
  */
4
4
  import type { LanguageAnalyzer } from '../../ast/languageAnalyzer';
5
- import type { SupportedLanguage, ParsedSourceFile, SemanticModel, ResolvedHttpInteraction, SemanticAssertion, BusinessRuleRef, FlowRef, AnalysisContext } from '../../ast/astTypes';
5
+ import type { SupportedLanguage, ParsedSourceFile, SemanticModel, ResolvedHttpInteraction, SemanticAssertion, BusinessRuleRef, FlowRef, AnalysisContext, DecoratorInfo, SecurityClassification } from '../../ast/astTypes';
6
6
  export declare class PythonAnalyzer implements LanguageAnalyzer {
7
7
  readonly language: SupportedLanguage;
8
8
  parse(filePath: string, content: string): ParsedSourceFile;
@@ -12,4 +12,8 @@ export declare class PythonAnalyzer implements LanguageAnalyzer {
12
12
  extractBusinessRuleRefs(model: SemanticModel): BusinessRuleRef[];
13
13
  extractFlowRefs(_model: SemanticModel): FlowRef[];
14
14
  }
15
+ /**
16
+ * Classify JWT/auth security from decorator information.
17
+ */
18
+ export declare function classifyFlaskSecurity(decorators: DecoratorInfo[]): SecurityClassification | undefined;
15
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAIb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,eAAe,EAChB,MAAM,oBAAoB,CAAC;AA8B5B,qBAAa,cAAe,YAAW,gBAAgB;IACrD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;IAEhD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAc1D,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,GAAG,aAAa;IAuBtF,uBAAuB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,GAAG,uBAAuB,EAAE;IAwB/F,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,EAAE;IAG5D,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE;IAGhE,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,EAAE;CAGlD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/languages/python/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAIb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,eAAe,EAEf,aAAa,EAEb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAkC5B,qBAAa,cAAe,YAAW,gBAAgB;IACrD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;IAEhD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAc1D,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,GAAG,aAAa;IA6BtF,uBAAuB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,GAAG,uBAAuB,EAAE;IAwB/F,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,EAAE;IAG5D,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE;IAGhE,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,EAAE;CAGlD;AA2WD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,sBAAsB,GAAG,SAAS,CAarG"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PythonAnalyzer = void 0;
7
+ exports.classifyFlaskSecurity = classifyFlaskSecurity;
7
8
  const parserRegistry_1 = require("../../ast/parserRegistry");
8
9
  const treeSitterUtils_1 = require("../shared/treeSitterUtils");
9
10
  const resolvePaths_1 = require("../../coverage/deep-analysis/resolvePaths");
@@ -52,6 +53,9 @@ class PythonAnalyzer {
52
53
  const functions = extractPythonFunctions(root, constants);
53
54
  const assertions = extractPythonAssertions(root);
54
55
  const businessRuleRefs = extractPythonBusinessRefs(root);
56
+ // Feature 27: Flask/FastAPI pattern detection
57
+ const decoratorStacks = extractFlaskDecoratorStacks(root, parsed.filePath);
58
+ const routeRegistrations = extractFlaskRouteRegistrations(root, parsed.filePath);
55
59
  return {
56
60
  filePath: parsed.filePath,
57
61
  language: 'python',
@@ -63,6 +67,8 @@ class PythonAnalyzer {
63
67
  assertions,
64
68
  businessRuleRefs,
65
69
  flowRefs: [],
70
+ decoratorStacks,
71
+ routeRegistrations,
66
72
  };
67
73
  }
68
74
  extractHttpInteractions(model, _ctx) {
@@ -179,10 +185,12 @@ function extractPythonHttpCalls(body, constants, out) {
179
185
  if (!funcNode)
180
186
  continue;
181
187
  const funcText = (_c = funcNode.text) !== null && _c !== void 0 ? _c : '';
182
- const match = funcText.match(/(?:requests|httpx|self\.client|client|self\.app|app)\.(get|post|put|patch|delete|head|options)/i);
188
+ const match = funcText.match(/(?:requests|httpx|self\.client|client|self\.app|app|testapp|self\.testapp)\.(get|post|post_json|put|put_json|patch|patch_json|delete|delete_json|head|options)/i);
183
189
  if (!match)
184
190
  continue;
185
191
  const [, methodName] = match;
192
+ // Normalize webtest methods: post_json → POST, put_json → PUT, etc.
193
+ const normalizedMethod = methodName.replace(/_json$/i, '').toUpperCase();
186
194
  const argList = (_e = (_d = call.childForFieldName) === null || _d === void 0 ? void 0 : _d.call(call, 'arguments')) !== null && _e !== void 0 ? _e : (0, treeSitterUtils_1.firstChildOfType)(call, 'argument_list');
187
195
  if (!argList)
188
196
  continue;
@@ -195,7 +203,7 @@ function extractPythonHttpCalls(body, constants, out) {
195
203
  // Resolve f-string variables against constants
196
204
  const resolvedPath = rawPath.includes('{') ? resolveFString(rawPath, constants) : rawPath;
197
205
  out.push({
198
- method: methodName.toUpperCase(),
206
+ method: normalizedMethod,
199
207
  rawPathArg: rawPath,
200
208
  resolvedPath,
201
209
  normalizedPath: resolvedPath.startsWith('/') ? (0, resolvePaths_1.normalizePathToTemplate)(resolvedPath) : undefined,
@@ -290,4 +298,161 @@ function emptyModel(filePath) {
290
298
  flowRefs: [],
291
299
  };
292
300
  }
301
+ // ─── Flask / FastAPI pattern extraction (Feature 27) ─────────────────────────
302
+ /**
303
+ * Extract decorator stacks for Flask/FastAPI route functions.
304
+ * Groups decorators by the function they decorate:
305
+ * @blueprint.route('/articles', methods=['GET'])
306
+ * @use_kwargs({...})
307
+ * @marshal_with(ArticleSchema)
308
+ * @jwt_required
309
+ * def get_articles():
310
+ * → DecoratorStack for 'get_articles' with 4 decorators
311
+ */
312
+ function extractFlaskDecoratorStacks(root, filePath) {
313
+ var _a, _b, _c, _d, _e, _f, _g;
314
+ const stacks = [];
315
+ const fnDefs = (0, treeSitterUtils_1.findNodes)(root, ['function_definition']);
316
+ for (const fn of fnDefs) {
317
+ const nameNode = (_b = (_a = fn.childForFieldName) === null || _a === void 0 ? void 0 : _a.call(fn, 'name')) !== null && _b !== void 0 ? _b : (0, treeSitterUtils_1.firstChildOfType)(fn, 'identifier');
318
+ const funcName = (_c = nameNode === null || nameNode === void 0 ? void 0 : nameNode.text) !== null && _c !== void 0 ? _c : '';
319
+ if (!funcName)
320
+ continue;
321
+ const decorators = [];
322
+ for (let i = 0; i < ((_d = fn === null || fn === void 0 ? void 0 : fn.childCount) !== null && _d !== void 0 ? _d : 0); i++) {
323
+ const child = fn.child(i);
324
+ if ((child === null || child === void 0 ? void 0 : child.type) !== 'decorator')
325
+ continue;
326
+ const decText = (_e = child.text) !== null && _e !== void 0 ? _e : '';
327
+ const dec = parseFlaskDecorator(decText, (_f = child.startPosition) === null || _f === void 0 ? void 0 : _f.row);
328
+ if (dec)
329
+ decorators.push(dec);
330
+ }
331
+ if (decorators.length > 0) {
332
+ stacks.push({
333
+ functionName: funcName,
334
+ decorators,
335
+ sourceFile: filePath,
336
+ line: ((_g = fn.startPosition) === null || _g === void 0 ? void 0 : _g.row) ? fn.startPosition.row + 1 : undefined,
337
+ });
338
+ }
339
+ }
340
+ return stacks;
341
+ }
342
+ /**
343
+ * Parse a single Flask/FastAPI decorator into a DecoratorInfo.
344
+ */
345
+ function parseFlaskDecorator(text, line) {
346
+ // @blueprint.route('/path', methods=['GET', 'POST'])
347
+ const routeMatch = text.match(/@(\w+)\.route\s*\(\s*['"]([^'"]+)['"]/);
348
+ if (routeMatch) {
349
+ const args = { path: routeMatch[2] };
350
+ const methodsMatch = text.match(/methods\s*=\s*\[([^\]]+)\]/);
351
+ if (methodsMatch)
352
+ args.methods = methodsMatch[1].replace(/['"]/g, '').trim();
353
+ return { name: 'route', fullText: text, args, line };
354
+ }
355
+ // @use_kwargs({...}) or @use_kwargs(SchemaClass)
356
+ const useKwargsMatch = text.match(/@use_kwargs\s*\((.+)\)/s);
357
+ if (useKwargsMatch) {
358
+ return { name: 'use_kwargs', fullText: text, args: { schema: useKwargsMatch[1].trim() }, line };
359
+ }
360
+ // @marshal_with(SchemaClass)
361
+ const marshalMatch = text.match(/@marshal_with\s*\(\s*(\w+)/);
362
+ if (marshalMatch) {
363
+ return { name: 'marshal_with', fullText: text, args: { schema: marshalMatch[1] }, line };
364
+ }
365
+ // @jwt_required, @jwt_required(), @jwt_required(optional=True), @jwt_optional
366
+ if (text.includes('@jwt_required') || text.includes('@jwt_optional')) {
367
+ const isOptional = text.includes('jwt_optional') || text.includes('optional=True') || text.includes('optional = True');
368
+ return {
369
+ name: isOptional ? 'jwt_optional' : 'jwt_required',
370
+ fullText: text,
371
+ args: { optional: isOptional ? 'true' : 'false' },
372
+ line,
373
+ };
374
+ }
375
+ // @login_required
376
+ if (text.includes('@login_required')) {
377
+ return { name: 'login_required', fullText: text, args: {}, line };
378
+ }
379
+ // Generic decorator
380
+ const genericMatch = text.match(/@(\w[\w.]*)/);
381
+ if (genericMatch) {
382
+ return { name: genericMatch[1], fullText: text, line };
383
+ }
384
+ return null;
385
+ }
386
+ /**
387
+ * Extract Flask Blueprint route registrations.
388
+ * Detects: Blueprint() constructors, register_blueprint() calls,
389
+ * @blueprint.route() registrations.
390
+ */
391
+ function extractFlaskRouteRegistrations(root, filePath) {
392
+ var _a, _b, _c;
393
+ const registrations = [];
394
+ // Use regex on the full source text since tree-sitter node traversal
395
+ // for call arguments is complex. This is a targeted pattern match.
396
+ const sourceText = (_a = root.text) !== null && _a !== void 0 ? _a : '';
397
+ const lines = sourceText.split('\n');
398
+ for (let i = 0; i < lines.length; i++) {
399
+ const line = lines[i];
400
+ // Blueprint constructor: bp = Blueprint('name', __name__, url_prefix='/api')
401
+ const bpMatch = line.match(/(\w+)\s*=\s*Blueprint\s*\(\s*['"](\w+)['"](?:[^)]*?url_prefix\s*=\s*['"]([^'"]+)['"])?\s*\)/);
402
+ if (bpMatch) {
403
+ registrations.push({
404
+ registrarName: bpMatch[1],
405
+ path: (_b = bpMatch[3]) !== null && _b !== void 0 ? _b : '',
406
+ sourceFile: filePath,
407
+ line: i + 1,
408
+ });
409
+ continue;
410
+ }
411
+ // register_blueprint(bp, url_prefix='/api/articles')
412
+ const regMatch = line.match(/register_blueprint\s*\(\s*(\w+)(?:\s*,\s*url_prefix\s*=\s*['"]([^'"]+)['"])?\s*\)/);
413
+ if (regMatch) {
414
+ registrations.push({
415
+ registrarName: regMatch[1],
416
+ path: (_c = regMatch[2]) !== null && _c !== void 0 ? _c : '',
417
+ targetModule: regMatch[1],
418
+ sourceFile: filePath,
419
+ line: i + 1,
420
+ });
421
+ continue;
422
+ }
423
+ // @blueprint.route('/path', methods=[...])
424
+ const routeMatch = line.match(/@(\w+)\.route\s*\(\s*['"]([^'"]+)['"]/);
425
+ if (routeMatch) {
426
+ const methodsMatch = line.match(/methods\s*=\s*\[([^\]]+)\]/);
427
+ const methods = methodsMatch
428
+ ? methodsMatch[1].replace(/['"]/g, '').split(',').map((m) => m.trim().toUpperCase())
429
+ : ['GET'];
430
+ registrations.push({
431
+ registrarName: routeMatch[1],
432
+ path: routeMatch[2],
433
+ methods,
434
+ sourceFile: filePath,
435
+ line: i + 1,
436
+ });
437
+ }
438
+ }
439
+ return registrations;
440
+ }
441
+ /**
442
+ * Classify JWT/auth security from decorator information.
443
+ */
444
+ function classifyFlaskSecurity(decorators) {
445
+ for (const dec of decorators) {
446
+ if (dec.name === 'jwt_required') {
447
+ return { type: 'jwt', required: true, optional: false, sourcePattern: '@jwt_required' };
448
+ }
449
+ if (dec.name === 'jwt_optional') {
450
+ return { type: 'jwt', required: false, optional: true, sourcePattern: '@jwt_optional' };
451
+ }
452
+ if (dec.name === 'login_required') {
453
+ return { type: 'session', required: true, optional: false, sourcePattern: '@login_required' };
454
+ }
455
+ }
456
+ return undefined;
457
+ }
293
458
  (0, parserRegistry_1.registerAnalyzer)('python', () => new PythonAnalyzer());
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Python test pattern detector (Feature 27, Sub-PR 4)
3
+ *
4
+ * Detects:
5
+ * 1. Factory Boy factories (factory.Factory subclasses, SubFactory, Meta.model)
6
+ * 2. webtest API calls (TestApp, testapp.get/post_json/put_json/delete)
7
+ * 3. pytest fixture chains (@pytest.fixture, fixture dependencies)
8
+ */
9
+ import type { SemanticHttpCall } from '../../ast/astTypes';
10
+ export interface FactoryBoyFactory {
11
+ /** The factory class name (e.g. 'ArticleFactory') */
12
+ className: string;
13
+ /** The model the factory creates (from Meta.model) */
14
+ modelName?: string;
15
+ /** SubFactory references */
16
+ subFactories: string[];
17
+ /** RelatedFactoryList references */
18
+ relatedFactoryLists: string[];
19
+ sourceFile: string;
20
+ line?: number;
21
+ }
22
+ /**
23
+ * Detect Factory Boy factory classes from source text.
24
+ * Looks for `class XFactory(factory.Factory):` and parses Meta.model, SubFactory, RelatedFactoryList.
25
+ */
26
+ export declare function detectFactoryBoyFactories(sourceText: string, filePath: string): FactoryBoyFactory[];
27
+ export interface WebtestApiCall {
28
+ method: string;
29
+ path: string;
30
+ sourceFile: string;
31
+ line?: number;
32
+ }
33
+ /**
34
+ * Detect webtest HTTP calls from source text.
35
+ * Looks for `testapp.get('/path')`, `testapp.post_json('/path', {...})`, etc.
36
+ */
37
+ export declare function detectWebtestCalls(sourceText: string, filePath: string): WebtestApiCall[];
38
+ /**
39
+ * Convert webtest API calls into SemanticHttpCall objects.
40
+ */
41
+ export declare function webtestCallsToHttpCalls(calls: WebtestApiCall[]): SemanticHttpCall[];
42
+ export interface PytestFixture {
43
+ /** Name of the fixture function */
44
+ name: string;
45
+ /** Scope: 'function' (default), 'class', 'module', 'session' */
46
+ scope: string;
47
+ /** Parameter names that are dependencies on other fixtures */
48
+ dependencies: string[];
49
+ sourceFile: string;
50
+ line?: number;
51
+ }
52
+ /**
53
+ * Detect @pytest.fixture functions and their parameter dependencies.
54
+ */
55
+ export declare function detectPytestFixtures(sourceText: string, filePath: string): PytestFixture[];
56
+ /**
57
+ * Detect if a file creates a webtest TestApp instance.
58
+ * Looks for `TestApp(app)` or `TestApp(...)`.
59
+ */
60
+ export declare function hasWebtestTestApp(sourceText: string): boolean;
61
+ /**
62
+ * Check if a file uses real DB fixtures (not mocked).
63
+ * Looks for pytest fixtures that reference db, database, session, engine, etc.
64
+ */
65
+ export declare function hasRealDbFixtures(sourceText: string): boolean;
66
+ /**
67
+ * Check if a file uses Factory Boy factories.
68
+ */
69
+ export declare function usesFactoryBoy(sourceText: string): boolean;
70
+ //# sourceMappingURL=testPatternDetector.d.ts.map