vibe-splain 2.7.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +195 -175
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2175,7 +2175,6 @@ import { join as join7 } from "path";
|
|
|
2175
2175
|
import { writeFile as writeFile7, readFile as readFile6 } from "fs/promises";
|
|
2176
2176
|
var FUNCTION_TYPES2 = /* @__PURE__ */ new Set([
|
|
2177
2177
|
"function_declaration",
|
|
2178
|
-
"function",
|
|
2179
2178
|
"function_expression",
|
|
2180
2179
|
"arrow_function",
|
|
2181
2180
|
"method_definition",
|
|
@@ -2191,24 +2190,33 @@ var FUNCTION_TYPES2 = /* @__PURE__ */ new Set([
|
|
|
2191
2190
|
function firstLine2(s) {
|
|
2192
2191
|
return s.split("\n")[0].trim();
|
|
2193
2192
|
}
|
|
2193
|
+
function walkNodes(node, cb) {
|
|
2194
|
+
cb(node);
|
|
2195
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2196
|
+
const child = node.child(i);
|
|
2197
|
+
if (child)
|
|
2198
|
+
walkNodes(child, cb);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2194
2201
|
function resolveCallee(node) {
|
|
2195
2202
|
if (node.type === "identifier") {
|
|
2196
2203
|
return { root: node.text, prop: null };
|
|
2197
2204
|
}
|
|
2198
|
-
if (node.type === "member_expression"
|
|
2205
|
+
if (node.type === "member_expression") {
|
|
2199
2206
|
const obj = node.childForFieldName("object");
|
|
2200
2207
|
const prop = node.childForFieldName("property");
|
|
2201
2208
|
if (obj && prop) {
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
const nested = resolveCallee(obj);
|
|
2206
|
-
if (nested) {
|
|
2207
|
-
return { root: nested.root, prop: nested.prop ? `${nested.prop}.${prop.text}` : prop.text };
|
|
2208
|
-
}
|
|
2209
|
+
const nested = resolveCallee(obj);
|
|
2210
|
+
if (nested) {
|
|
2211
|
+
return { root: nested.root, prop: nested.prop ? `${nested.prop}.${prop.text}` : prop.text };
|
|
2209
2212
|
}
|
|
2210
2213
|
}
|
|
2211
2214
|
}
|
|
2215
|
+
if (node.type === "parenthesized_expression" || node.type === "await_expression") {
|
|
2216
|
+
const inner = node.namedChildren.find((c) => c.type !== "(" && c.type !== ")" && c.type !== "await");
|
|
2217
|
+
if (inner)
|
|
2218
|
+
return resolveCallee(inner);
|
|
2219
|
+
}
|
|
2212
2220
|
return null;
|
|
2213
2221
|
}
|
|
2214
2222
|
async function runActionBinding(projectRoot, inv, res) {
|
|
@@ -2255,25 +2263,26 @@ async function runActionBinding(projectRoot, inv, res) {
|
|
|
2255
2263
|
});
|
|
2256
2264
|
}
|
|
2257
2265
|
const functions = [];
|
|
2266
|
+
const nodeToRecord = /* @__PURE__ */ new Map();
|
|
2258
2267
|
if (!w.tree)
|
|
2259
2268
|
continue;
|
|
2260
|
-
const
|
|
2261
|
-
cb(node);
|
|
2262
|
-
for (const child of node.children)
|
|
2263
|
-
walkNodes(child, cb);
|
|
2264
|
-
};
|
|
2265
|
-
const functionNodes = [];
|
|
2269
|
+
const allNodes = [];
|
|
2266
2270
|
walkNodes(w.tree.rootNode, (n) => {
|
|
2267
|
-
|
|
2268
|
-
functionNodes.push(n);
|
|
2271
|
+
allNodes.push(n);
|
|
2269
2272
|
});
|
|
2270
|
-
for (const node of
|
|
2273
|
+
for (const node of allNodes) {
|
|
2274
|
+
if (!FUNCTION_TYPES2.has(node.type))
|
|
2275
|
+
continue;
|
|
2276
|
+
const startLine = node.startPosition.row + 1;
|
|
2277
|
+
const startCol = node.startPosition.column;
|
|
2278
|
+
const endLine = node.endPosition.row + 1;
|
|
2279
|
+
const isDuplicate = functions.some((f) => f.startLine === startLine && f.endLine === endLine);
|
|
2280
|
+
if (isDuplicate)
|
|
2281
|
+
continue;
|
|
2271
2282
|
functionsExtracted++;
|
|
2272
2283
|
let displayName = "";
|
|
2273
2284
|
let nameSource = "position_fallback";
|
|
2274
2285
|
const p = node.parent;
|
|
2275
|
-
const startLine = node.startPosition.row + 1;
|
|
2276
|
-
const startCol = node.startPosition.column;
|
|
2277
2286
|
if (node.childForFieldName("name")) {
|
|
2278
2287
|
displayName = node.childForFieldName("name").text;
|
|
2279
2288
|
nameSource = node.type === "method_definition" ? "method_definition" : "function_declaration";
|
|
@@ -2330,176 +2339,179 @@ async function runActionBinding(projectRoot, inv, res) {
|
|
|
2330
2339
|
evidenceText: firstLine2(node.text).slice(0, 200)
|
|
2331
2340
|
};
|
|
2332
2341
|
functions.push(fnRecord);
|
|
2342
|
+
nodeToRecord.set(node.id, fnRecord);
|
|
2333
2343
|
}
|
|
2334
|
-
for (const
|
|
2335
|
-
|
|
2336
|
-
if (!fnNode)
|
|
2344
|
+
for (const node of allNodes) {
|
|
2345
|
+
if (node.type !== "call_expression" && node.type !== "call")
|
|
2337
2346
|
continue;
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
let curr = callNode.parent;
|
|
2346
|
-
while (curr && curr !== fnNode) {
|
|
2347
|
-
if (FUNCTION_TYPES2.has(curr.type)) {
|
|
2348
|
-
isNested = true;
|
|
2349
|
-
break;
|
|
2350
|
-
}
|
|
2351
|
-
curr = curr.parent;
|
|
2352
|
-
}
|
|
2353
|
-
if (isNested)
|
|
2354
|
-
continue;
|
|
2355
|
-
callsExtracted++;
|
|
2356
|
-
const calleeNode = callNode.childForFieldName("function");
|
|
2357
|
-
if (!calleeNode)
|
|
2358
|
-
continue;
|
|
2359
|
-
let actualCalleeNode = calleeNode;
|
|
2360
|
-
if (calleeNode.type === "await_expression" && calleeNode.children.length > 1) {
|
|
2361
|
-
actualCalleeNode = calleeNode.children[1];
|
|
2347
|
+
let curr = node.parent;
|
|
2348
|
+
let containingFnRecord = null;
|
|
2349
|
+
while (curr) {
|
|
2350
|
+
const rec = nodeToRecord.get(curr.id);
|
|
2351
|
+
if (rec) {
|
|
2352
|
+
containingFnRecord = rec;
|
|
2353
|
+
break;
|
|
2362
2354
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2355
|
+
curr = curr.parent;
|
|
2356
|
+
}
|
|
2357
|
+
if (!containingFnRecord)
|
|
2358
|
+
continue;
|
|
2359
|
+
callsExtracted++;
|
|
2360
|
+
const calleeNode = node.childForFieldName("function") || node.namedChild(0);
|
|
2361
|
+
if (!calleeNode)
|
|
2362
|
+
continue;
|
|
2363
|
+
const resolvedCallee = resolveCallee(calleeNode);
|
|
2364
|
+
if (!resolvedCallee)
|
|
2365
|
+
continue;
|
|
2366
|
+
const { root: calleeRoot, prop: calleeProperty } = resolvedCallee;
|
|
2367
|
+
const calleeText = calleeNode.text.slice(0, 100);
|
|
2368
|
+
const sourceLine = node.startPosition.row + 1;
|
|
2369
|
+
let isSemantic = false;
|
|
2370
|
+
let actionKind = null;
|
|
2371
|
+
let targetModel = null;
|
|
2372
|
+
let targetOperation = null;
|
|
2373
|
+
if (calleeRoot === "prisma" && calleeProperty) {
|
|
2374
|
+
if (/\.(create|update|upsert|delete|deleteMany|updateMany|createMany|executeRaw|queryRaw)$/.test("." + calleeProperty)) {
|
|
2375
|
+
isSemantic = true;
|
|
2376
|
+
actionKind = "database_write";
|
|
2377
|
+
const parts = calleeProperty.split(".");
|
|
2378
|
+
if (parts.length >= 2) {
|
|
2379
|
+
targetModel = parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
|
|
2380
|
+
targetOperation = parts[parts.length - 1];
|
|
2381
|
+
}
|
|
2382
|
+
} else if (/\.(findMany|findUnique|findFirst|findFirstOrThrow|findUniqueOrThrow|count|aggregate|groupBy)$/.test("." + calleeProperty)) {
|
|
2383
|
+
isSemantic = true;
|
|
2384
|
+
actionKind = "database_read";
|
|
2385
|
+
const parts = calleeProperty.split(".");
|
|
2386
|
+
if (parts.length >= 2) {
|
|
2387
|
+
targetModel = parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
|
|
2388
|
+
targetOperation = parts[parts.length - 1];
|
|
2391
2389
|
}
|
|
2392
2390
|
}
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2391
|
+
} else if (calleeRoot === "trpc" && calleeProperty && /\.(useMutation|mutate|mutateAsync)$/.test("." + calleeProperty)) {
|
|
2392
|
+
isSemantic = true;
|
|
2393
|
+
actionKind = "external_api_call";
|
|
2394
|
+
const parts = calleeProperty.split(".");
|
|
2395
|
+
if (parts.length >= 2) {
|
|
2396
|
+
targetModel = parts[0];
|
|
2397
|
+
targetOperation = parts[parts.length - 1];
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
if (!isSemantic) {
|
|
2401
|
+
if (calleeRoot === "fetch" || calleeRoot === "axios" && calleeProperty && /\.(get|post|put|patch|delete)$/.test("." + calleeProperty)) {
|
|
2402
|
+
isSemantic = true;
|
|
2403
|
+
actionKind = "external_api_call";
|
|
2404
|
+
} else if (/validate|schema\.parse|schema\.safeParse|z\.parse/i.test(calleeText) || /validate|validator/i.test(calleeRoot)) {
|
|
2405
|
+
isSemantic = true;
|
|
2406
|
+
actionKind = "validation";
|
|
2407
|
+
} else if (/getSession|getServerSession|auth\(\)|verifyToken|requireAuth|checkPermission/i.test(calleeText) || /auth|session/i.test(calleeRoot) && calleeProperty && /check|verify|get|require/i.test("." + calleeProperty)) {
|
|
2408
|
+
isSemantic = true;
|
|
2409
|
+
actionKind = "auth_check";
|
|
2410
|
+
} else if (/sendEmail|sendMail|mailer\./i.test(calleeText)) {
|
|
2411
|
+
isSemantic = true;
|
|
2412
|
+
actionKind = "email_send";
|
|
2413
|
+
} else if (/createCalendarEvent|updateCalendarEvent|deleteCalendarEvent|calendar\.events\.(insert|update|delete)/i.test(calleeText)) {
|
|
2414
|
+
isSemantic = true;
|
|
2415
|
+
actionKind = "calendar_mutation";
|
|
2416
|
+
} else if (/triggerWebhook|sendWebhook|webhook\.send/i.test(calleeText)) {
|
|
2417
|
+
isSemantic = true;
|
|
2418
|
+
actionKind = "webhook_delivery";
|
|
2419
|
+
} else if (/stripe\.webhooks\.constructEvent|validateWebhook|verifySignature/i.test(calleeText)) {
|
|
2420
|
+
isSemantic = true;
|
|
2421
|
+
actionKind = "webhook_ingress";
|
|
2422
|
+
} else if (/revalidatePath|revalidateTag/i.test(calleeText)) {
|
|
2423
|
+
isSemantic = true;
|
|
2424
|
+
actionKind = "cache_revalidation";
|
|
2425
|
+
} else if (/posthog\.|mixpanel\.|amplitude\.|ga\(/i.test(calleeText)) {
|
|
2426
|
+
isSemantic = true;
|
|
2427
|
+
actionKind = "analytics_event";
|
|
2428
|
+
} else if (calleeRoot === "redirect" || /router|redirect|notFound|permanentRedirect/.test(calleeRoot) && calleeProperty && /push|replace|back/i.test("." + calleeProperty)) {
|
|
2429
|
+
isSemantic = true;
|
|
2430
|
+
actionKind = "redirect";
|
|
2431
|
+
} else if (/cookies\(\)|headers\(\)/.test(calleeText) || calleeRoot === "cookies" || calleeRoot === "headers") {
|
|
2432
|
+
isSemantic = true;
|
|
2433
|
+
actionKind = "side_effect";
|
|
2434
|
+
} else if (/checkRateLimitAndThrowError/i.test(calleeText)) {
|
|
2435
|
+
isSemantic = true;
|
|
2436
|
+
actionKind = "auth_check";
|
|
2437
|
+
} else {
|
|
2438
|
+
const emailImport = imports.find((i) => i.localName === calleeRoot && /nodemailer|resend|sendgrid|postmark|mailgun/i.test(i.moduleSpecifier));
|
|
2439
|
+
if (emailImport) {
|
|
2404
2440
|
isSemantic = true;
|
|
2405
2441
|
actionKind = "email_send";
|
|
2406
|
-
} else if (/createCalendarEvent|updateCalendarEvent|deleteCalendarEvent|calendar\.events\.(insert|update|delete)/i.test(calleeText)) {
|
|
2407
|
-
isSemantic = true;
|
|
2408
|
-
actionKind = "calendar_mutation";
|
|
2409
|
-
} else if (/triggerWebhook|sendWebhook|webhook\.send/i.test(calleeText)) {
|
|
2410
|
-
isSemantic = true;
|
|
2411
|
-
actionKind = "webhook_delivery";
|
|
2412
|
-
} else if (/stripe\.webhooks\.constructEvent|validateWebhook|verifySignature/i.test(calleeText)) {
|
|
2413
|
-
isSemantic = true;
|
|
2414
|
-
actionKind = "webhook_ingress";
|
|
2415
|
-
} else if (/revalidatePath|revalidateTag/i.test(calleeText)) {
|
|
2416
|
-
isSemantic = true;
|
|
2417
|
-
actionKind = "cache_revalidation";
|
|
2418
|
-
} else if (/posthog\.|mixpanel\.|amplitude\.|ga\(/i.test(calleeText)) {
|
|
2419
|
-
isSemantic = true;
|
|
2420
|
-
actionKind = "analytics_event";
|
|
2421
|
-
} else if (calleeRoot === "redirect" || /router|redirect|notFound|permanentRedirect/.test(calleeRoot) && calleeProperty && /push|replace|back/i.test("." + calleeProperty)) {
|
|
2422
|
-
isSemantic = true;
|
|
2423
|
-
actionKind = "redirect";
|
|
2424
|
-
} else {
|
|
2425
|
-
const emailImport = imports.find((i) => i.localName === calleeRoot && /nodemailer|resend|sendgrid|postmark|mailgun/i.test(i.moduleSpecifier));
|
|
2426
|
-
if (emailImport) {
|
|
2427
|
-
isSemantic = true;
|
|
2428
|
-
actionKind = "email_send";
|
|
2429
|
-
}
|
|
2430
2442
|
}
|
|
2431
2443
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
artifact.actionIndex[actionKind]
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
artifact.actionIndex[key1]
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
artifact.actionIndex[key2]
|
|
2459
|
-
|
|
2444
|
+
}
|
|
2445
|
+
if (isSemantic && actionKind) {
|
|
2446
|
+
semanticActionsExtracted++;
|
|
2447
|
+
const actionId = `${containingFnRecord.functionId}::${actionKind}::${sourceLine}`;
|
|
2448
|
+
containingFnRecord.semanticActions.push({
|
|
2449
|
+
actionId,
|
|
2450
|
+
sourceFunctionId: containingFnRecord.functionId,
|
|
2451
|
+
actionKind,
|
|
2452
|
+
targetModel,
|
|
2453
|
+
targetOperation,
|
|
2454
|
+
calleeText,
|
|
2455
|
+
sourceLine,
|
|
2456
|
+
confidence: "high",
|
|
2457
|
+
evidenceText: firstLine2(node.text).slice(0, 200)
|
|
2458
|
+
});
|
|
2459
|
+
if (!artifact.actionIndex[actionKind])
|
|
2460
|
+
artifact.actionIndex[actionKind] = [];
|
|
2461
|
+
artifact.actionIndex[actionKind].push(containingFnRecord.functionId);
|
|
2462
|
+
if (targetModel) {
|
|
2463
|
+
const key1 = `${actionKind}::${targetModel}`;
|
|
2464
|
+
if (!artifact.actionIndex[key1])
|
|
2465
|
+
artifact.actionIndex[key1] = [];
|
|
2466
|
+
artifact.actionIndex[key1].push(containingFnRecord.functionId);
|
|
2467
|
+
if (targetOperation) {
|
|
2468
|
+
const key2 = `${actionKind}::${targetModel}::${targetOperation}`;
|
|
2469
|
+
if (!artifact.actionIndex[key2])
|
|
2470
|
+
artifact.actionIndex[key2] = [];
|
|
2471
|
+
artifact.actionIndex[key2].push(containingFnRecord.functionId);
|
|
2460
2472
|
}
|
|
2473
|
+
}
|
|
2474
|
+
} else {
|
|
2475
|
+
let resolvedTargetFunctionId = null;
|
|
2476
|
+
let resolvedFilePath = null;
|
|
2477
|
+
let resolutionKind = "unresolved";
|
|
2478
|
+
let confidence = "unresolved";
|
|
2479
|
+
const sameFileFn = functions.find((f) => f.displayName === calleeRoot);
|
|
2480
|
+
if (sameFileFn) {
|
|
2481
|
+
resolvedTargetFunctionId = sameFileFn.functionId;
|
|
2482
|
+
resolutionKind = "same_file_function";
|
|
2483
|
+
confidence = "high";
|
|
2461
2484
|
} else {
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
const sameFileFn = functions.find((f) => f.displayName === calleeRoot);
|
|
2467
|
-
if (sameFileFn) {
|
|
2468
|
-
resolvedTargetFunctionId = sameFileFn.functionId;
|
|
2469
|
-
resolutionKind = "same_file_function";
|
|
2485
|
+
const namedImp = imports.find((i) => i.localName === calleeRoot && i.importKind !== "namespace" && !i.isTypeOnly);
|
|
2486
|
+
if (namedImp && namedImp.resolvedFilePath) {
|
|
2487
|
+
resolvedFilePath = namedImp.resolvedFilePath;
|
|
2488
|
+
resolutionKind = "named_import_match";
|
|
2470
2489
|
confidence = "high";
|
|
2471
2490
|
} else {
|
|
2472
|
-
const
|
|
2473
|
-
if (
|
|
2474
|
-
resolvedFilePath =
|
|
2475
|
-
resolutionKind = "
|
|
2476
|
-
confidence = "
|
|
2477
|
-
} else {
|
|
2478
|
-
const nsImp = imports.find((i) => i.localName === calleeRoot && i.importKind === "namespace");
|
|
2479
|
-
if (nsImp && nsImp.resolvedFilePath) {
|
|
2480
|
-
resolvedFilePath = nsImp.resolvedFilePath;
|
|
2481
|
-
resolutionKind = "namespace_import_property";
|
|
2482
|
-
confidence = "medium";
|
|
2483
|
-
}
|
|
2491
|
+
const nsImp = imports.find((i) => i.localName === calleeRoot && i.importKind === "namespace");
|
|
2492
|
+
if (nsImp && nsImp.resolvedFilePath) {
|
|
2493
|
+
resolvedFilePath = nsImp.resolvedFilePath;
|
|
2494
|
+
resolutionKind = "namespace_import_property";
|
|
2495
|
+
confidence = "medium";
|
|
2484
2496
|
}
|
|
2485
2497
|
}
|
|
2486
|
-
if (resolutionKind !== "unresolved")
|
|
2487
|
-
callsResolved++;
|
|
2488
|
-
fnRecord.calls.push({
|
|
2489
|
-
callId,
|
|
2490
|
-
sourceFunctionId: fnRecord.functionId,
|
|
2491
|
-
calleeText,
|
|
2492
|
-
calleeRoot,
|
|
2493
|
-
calleeProperty,
|
|
2494
|
-
sourceLine,
|
|
2495
|
-
sourceSpan: { startLine: callNode.startPosition.row + 1, endLine: callNode.endPosition.row + 1 },
|
|
2496
|
-
resolvedTargetFunctionId,
|
|
2497
|
-
resolvedFilePath,
|
|
2498
|
-
resolutionKind,
|
|
2499
|
-
confidence,
|
|
2500
|
-
evidenceText: firstLine2(callNode.text).slice(0, 200)
|
|
2501
|
-
});
|
|
2502
2498
|
}
|
|
2499
|
+
if (resolutionKind !== "unresolved")
|
|
2500
|
+
callsResolved++;
|
|
2501
|
+
containingFnRecord.calls.push({
|
|
2502
|
+
callId: `${containingFnRecord.functionId}::${calleeText}::${sourceLine}`,
|
|
2503
|
+
sourceFunctionId: containingFnRecord.functionId,
|
|
2504
|
+
calleeText,
|
|
2505
|
+
calleeRoot,
|
|
2506
|
+
calleeProperty,
|
|
2507
|
+
sourceLine,
|
|
2508
|
+
sourceSpan: { startLine: node.startPosition.row + 1, endLine: node.endPosition.row + 1 },
|
|
2509
|
+
resolvedTargetFunctionId,
|
|
2510
|
+
resolvedFilePath,
|
|
2511
|
+
resolutionKind,
|
|
2512
|
+
confidence,
|
|
2513
|
+
evidenceText: firstLine2(node.text).slice(0, 200)
|
|
2514
|
+
});
|
|
2503
2515
|
}
|
|
2504
2516
|
}
|
|
2505
2517
|
artifact.files[filePath] = {
|
|
@@ -2992,6 +3004,9 @@ async function runScoring(projectRoot, cr, binding) {
|
|
|
2992
3004
|
const callPts = Math.min(3, resolvedOutbound);
|
|
2993
3005
|
fnScore += callPts;
|
|
2994
3006
|
reasons.push(`Has ${resolvedOutbound} resolved outbound calls`);
|
|
3007
|
+
} else if (fn.calls.length > 0) {
|
|
3008
|
+
fnScore += 1;
|
|
3009
|
+
reasons.push(`Has ${fn.calls.length} outbound calls`);
|
|
2995
3010
|
}
|
|
2996
3011
|
const writesModel = fn.semanticActions.some((a) => a.actionKind === "database_write" && a.targetModel);
|
|
2997
3012
|
if (writesModel) {
|
|
@@ -3003,10 +3018,15 @@ async function runScoring(projectRoot, cr, binding) {
|
|
|
3003
3018
|
fnScore += 1;
|
|
3004
3019
|
reasons.push("Performs auth/validation");
|
|
3005
3020
|
}
|
|
3021
|
+
const hasEvidenceOverlap = rawEvidence.some((e) => fn.startLine <= e.endLine && fn.endLine >= e.startLine);
|
|
3022
|
+
if (hasEvidenceOverlap) {
|
|
3023
|
+
fnScore += 2;
|
|
3024
|
+
reasons.push("Overlaps with raw evidence span");
|
|
3025
|
+
}
|
|
3006
3026
|
return { fn, fnScore, reasons };
|
|
3007
3027
|
});
|
|
3008
3028
|
scoredFunctions.sort((a, b) => b.fnScore - a.fnScore);
|
|
3009
|
-
const topFns = scoredFunctions.slice(0, 5);
|
|
3029
|
+
const topFns = scoredFunctions.filter((x) => x.reasons.length > 0).slice(0, 5);
|
|
3010
3030
|
if (topFns.length > 0) {
|
|
3011
3031
|
criticalFunctions = topFns.map(({ fn, reasons }) => {
|
|
3012
3032
|
const evidence = fn.semanticActions.slice(0, 5).sort((a, b) => a.sourceLine - b.sourceLine).map((a) => ({
|
package/package.json
CHANGED