@saptools/service-flow 0.1.12 → 0.1.13
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/CHANGELOG.md +10 -0
- package/README.md +3 -3
- package/TECHNICAL-NOTE.md +10 -1
- package/dist/{chunk-MXYYARUP.js → chunk-Q7W7TE3W.js} +93 -23
- package/dist/chunk-Q7W7TE3W.js.map +1 -0
- package/dist/cli.js +39 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MXYYARUP.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.13
|
|
4
|
+
|
|
5
|
+
- Added implementation-context fallback for local `cds.services.*` calls so helper packages can resolve model-package operations only when handler/dependency/registration evidence ties the caller repository to the target operation.
|
|
6
|
+
- Preserved same-repository local service resolution as the strongest path and added explicit evidence/reasons for implementation-context ownership, rejected candidates, and candidate-without-caller-ownership cases.
|
|
7
|
+
- Replaced fragile CAP DB query string extraction with AST traversal for chained `cds.run(SELECT/INSERT/UPDATE/DELETE...)` forms, including multiline `columns(...).where(...)` chains and `this.EntityName` targets.
|
|
8
|
+
- Kept genuinely dynamic DB queries terminal while exposing parser-warning evidence and rendering unknown targets as `Entity: unknown` instead of raw numeric call ids in table/Mermaid output.
|
|
9
|
+
- Further reduced symbol-call noise by filtering built-in collection/string methods, logger calls, global built-ins, third-party package property calls, and unindexed `this.container.method()` calls while retaining indexed local helpers and relative object-literal helper imports.
|
|
10
|
+
- Added doctor aggregate visibility for local service calls resolved by implementation context and calls left unresolved because candidates lack caller ownership.
|
|
11
|
+
- Updated README and technical notes to clarify generated constants remain low-level parser output rather than persisted graph facts.
|
|
12
|
+
|
|
3
13
|
## 0.1.12
|
|
4
14
|
|
|
5
15
|
- Resolved same-repository local CAP service calls by qualified CDS name, simple service name, and service path, with explicit local transport and alias-chain evidence.
|
package/README.md
CHANGED
|
@@ -152,11 +152,11 @@ target handler up to `--depth` instead of showing only calls in the first file.
|
|
|
152
152
|
|
|
153
153
|
`service-flow trace` starts from the selected handler method symbol, renders outbound calls owned by that symbol, and follows conservative local helper-call facts. Supported helper edges include same-file functions, `this.method()` calls, and exactly mapped relative imports/exports that resolve to an indexed executable symbol. Calls from unrelated functions in the same source file are not included merely because the file path matches.
|
|
154
154
|
|
|
155
|
-
Local CAP calls through `cds.services.<Service>.<operation>()`, bracket service lookups, and simple aliases are indexed as local operation calls. Linking stays within the same repository
|
|
155
|
+
Local CAP calls through `cds.services.<Service>.<operation>()`, bracket service lookups, and simple aliases are indexed as local operation calls. Linking first stays within the same repository and matches the target operation by exact qualified CDS service name, exact simple service name, exact service path, or an unambiguous service-path suffix. If no same-repository service exists, the linker can use implementation-context evidence to resolve model-package operations for helper packages: a resolved/ambiguous implementation candidate, registration package, or dependency/import edge must tie the caller repository to the model operation. Name-only global matches are preserved as unresolved candidate evidence rather than guessed links. Entity accessors such as `cds.services.db.entities(...)` are treated as entity metadata access, not operation calls.
|
|
156
156
|
|
|
157
157
|
Conservative local symbol traversal intentionally excludes decorators, built-ins such as `JSON.parse`, collection methods, third-party APIs, and arbitrary property chains unless the callee can plausibly resolve to an indexed local symbol. Named export lists such as `export { loadTemplate as publicLoadTemplate }` are indexed with the public exported name so relative imports can resolve. One-level object-literal helpers are indexed as symbols named like `cacheHelper.getConfiguration`; nested object literals are not yet expanded beyond the first helper level. `parseGeneratedConstants` remains a public low-level parser export for callers that need it, but generated constants are not persisted as graph facts in this patch; linking uses the deterministic decorator normalizer described above.
|
|
158
158
|
JSON output includes typed nodes for calls, operations, database entities,
|
|
159
|
-
external destinations, and unresolved/dynamic candidates when edges exist.
|
|
159
|
+
external destinations, and unresolved/dynamic candidates when edges exist. Chained CAP DB queries inside `cds.run(...)` are parsed with TypeScript AST evidence for `SELECT`, `INSERT`, `UPDATE`, and `DELETE` forms. When the query target is genuinely dynamic, graph status remains terminal and JSON retains `parserWarning` evidence, while table and Mermaid render the target as `Entity: unknown` rather than a numeric call id.
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
162
|
service-flow trace --workspace /path/to/workspace --repo facade-service --operation doWork
|
|
@@ -287,7 +287,7 @@ service-flow clean --workspace /path/to/workspace
|
|
|
287
287
|
| Local data access | `cds.run(SELECT...)` and local entity query evidence |
|
|
288
288
|
| Async channels | Event Mesh-style `emit`, `publish`, and `on` facts |
|
|
289
289
|
| External calls | Cloud SDK-style HTTP/destination calls and external edge evidence |
|
|
290
|
-
| Generated constants |
|
|
290
|
+
| Generated constants | low-level `parseGeneratedConstants` parser output for integrations; not persisted as first-class graph facts in this patch |
|
|
291
291
|
|
|
292
292
|
---
|
|
293
293
|
|
package/TECHNICAL-NOTE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Service Flow
|
|
1
|
+
# Service Flow Resolution Notes
|
|
2
2
|
|
|
3
3
|
- Imported helper bindings: TypeScript imports are resolved for relative modules. When a caller assigns `const client = await connectToService()`, the analyzer follows the imported symbol to an exported helper that returns `cds.connect.to(...)` and persists caller-variable evidence plus the helper source/export chain.
|
|
4
4
|
- Candidate ranking: operation-path matches start as weak candidates. A resolved operation edge requires a strong signal such as exact service path, CDS alias/destination context, or explicit dynamic variable overrides. Otherwise candidates are preserved in edge evidence as ambiguous or unresolved.
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
- Trace cycle safety: trace queues carry repository IDs, visited scope keys are independent of depth, graph edge IDs are emitted once, and revisiting an already-seen downstream operation scope creates a cycle marker instead of recursive expansion.
|
|
7
7
|
- SQLite reliability: the package uses a persistent SQLite connection per opened database, bound parameters, transactions, WAL, busy timeouts, read-only openings for query commands, and connection-local foreign-key enforcement. Native driver loading failures produce an actionable startup error before output rendering.
|
|
8
8
|
|
|
9
|
+
## 0.1.13 audit follow-up notes
|
|
10
|
+
|
|
11
|
+
- Local CAP service calls keep same-repository service ownership as the strongest resolution path. When the caller repository does not own the CDS service model, the linker searches workspace operations by service identity and operation name/path, then requires caller ownership evidence from implementation edges, ambiguous implementation candidates, registration packages, or resolved dependency/import edges before resolving. Candidate operations without caller evidence are retained with `local_service_candidate_without_caller_ownership` rather than guessed.
|
|
12
|
+
- Trace traversal can use local-call context to choose the caller repository's handler from an otherwise ambiguous global implementation edge. This is scoped to the local call and does not rewrite the global `OPERATION_IMPLEMENTED_BY_HANDLER` ambiguity.
|
|
13
|
+
- CAP DB entity extraction for local `cds.run(...)` calls uses TypeScript AST traversal across chained query expressions, including `SELECT.one.from(Entity).columns(...).where(...)`, `INSERT.into(Entity)`, `UPDATE(Entity)`, and `DELETE.from(Entity)`. Dynamic or unknowable query targets remain terminal graph edges with parser-warning evidence.
|
|
14
|
+
- Human output never labels an unknown DB query target with the raw call id. Table and Mermaid output use `Entity: unknown`; JSON keeps the parser warning and call id evidence for machines.
|
|
15
|
+
- Symbol-call indexing is intentionally conservative for property accesses. Built-in collection/string calls, global built-ins, logger calls, third-party package properties, and unindexed `this.someContainer.method()` calls are filtered unless indexed local helper or relative import evidence makes the edge actionable.
|
|
16
|
+
- Generated constants remain low-level parser output through `parseGeneratedConstants`. They are not persisted as graph facts; implementation linking only uses deterministic decorator normalization for common generated action/function names.
|
|
17
|
+
|
|
9
18
|
## 0.1.4 trace-correctness additions
|
|
10
19
|
|
|
11
20
|
- Helper exports are normalized through a public-to-local export map, so `export { helper }` and `export { helper as publicHelper }` both resolve to the local declaration that contains the `cds.connect.to(...)` call.
|
|
@@ -555,22 +555,39 @@ function matchingParen(text, open) {
|
|
|
555
555
|
}
|
|
556
556
|
return -1;
|
|
557
557
|
}
|
|
558
|
-
function
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
558
|
+
function entityFromExpression(expr) {
|
|
559
|
+
if (!expr) return void 0;
|
|
560
|
+
if (ts4.isIdentifier(expr) || ts4.isStringLiteral(expr) || ts4.isNoSubstitutionTemplateLiteral(expr)) return expr.text;
|
|
561
|
+
if (ts4.isPropertyAccessExpression(expr) && expr.expression.kind === ts4.SyntaxKind.ThisKeyword) return expr.name.text;
|
|
562
|
+
return void 0;
|
|
563
|
+
}
|
|
564
|
+
function expressionName(expr) {
|
|
565
|
+
if (ts4.isIdentifier(expr)) return expr.text;
|
|
566
|
+
if (ts4.isPropertyAccessExpression(expr)) return `${expressionName(expr.expression)}.${expr.name.text}`;
|
|
567
|
+
return expr.getText();
|
|
568
|
+
}
|
|
569
|
+
function queryEntityFromAst(expr) {
|
|
570
|
+
if (ts4.isParenthesizedExpression(expr) || ts4.isAwaitExpression(expr)) return queryEntityFromAst(expr.expression);
|
|
571
|
+
if (ts4.isCallExpression(expr)) {
|
|
572
|
+
const name = expressionName(expr.expression);
|
|
573
|
+
if (name === "cds.run") return queryEntityFromAst(expr.arguments[0]);
|
|
574
|
+
if (name === "SELECT.one.from" || name === "SELECT.from" || name === "INSERT.into" || name === "DELETE.from") return entityFromExpression(expr.arguments[0]);
|
|
575
|
+
if (name === "UPDATE") return entityFromExpression(expr.arguments[0]);
|
|
576
|
+
const receiver = ts4.isPropertyAccessExpression(expr.expression) ? expr.expression.expression : void 0;
|
|
577
|
+
if (receiver) return queryEntityFromAst(receiver);
|
|
578
|
+
}
|
|
579
|
+
return void 0;
|
|
571
580
|
}
|
|
572
581
|
function extractQueryEntity(expr) {
|
|
573
|
-
|
|
582
|
+
const source = ts4.createSourceFile("query.ts", `const __query = (${expr});`, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
|
|
583
|
+
let found;
|
|
584
|
+
const visit = (node) => {
|
|
585
|
+
if (found) return;
|
|
586
|
+
if (ts4.isParenthesizedExpression(node)) found = queryEntityFromAst(node.expression);
|
|
587
|
+
ts4.forEachChild(node, visit);
|
|
588
|
+
};
|
|
589
|
+
visit(source);
|
|
590
|
+
return found;
|
|
574
591
|
}
|
|
575
592
|
async function parseOutboundCalls(repoPath, filePath) {
|
|
576
593
|
const text = await fs6.readFile(path7.join(repoPath, filePath), "utf8");
|
|
@@ -1074,7 +1091,7 @@ function substituteVariables(template, vars) {
|
|
|
1074
1091
|
// src/linker/service-resolver.ts
|
|
1075
1092
|
function rows(db, operationPath, workspaceId) {
|
|
1076
1093
|
return db.prepare(
|
|
1077
|
-
`SELECT o.id operationId,r.name repoName,s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score,'' reasons
|
|
1094
|
+
`SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score,'' reasons
|
|
1078
1095
|
FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id
|
|
1079
1096
|
WHERE (? IS NULL OR r.workspace_id=?) AND (o.operation_path=? OR o.operation_name=?) ORDER BY r.name,s.service_path,o.operation_name`
|
|
1080
1097
|
).all(
|
|
@@ -1098,11 +1115,21 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
1098
1115
|
candidates: [],
|
|
1099
1116
|
reasons: ["missing_operation_path"]
|
|
1100
1117
|
};
|
|
1101
|
-
const
|
|
1118
|
+
const allCandidates = rows(db, signals.operationPath, workspaceId).map((c) => ({
|
|
1102
1119
|
...c,
|
|
1103
1120
|
score: 0.2,
|
|
1104
1121
|
reasons: ["operation_path_match"]
|
|
1105
1122
|
}));
|
|
1123
|
+
let candidates = allCandidates.filter((c) => matchesLocalRepo(db, c.operationId, signals.repoId));
|
|
1124
|
+
if (candidates.length === 0 && signals.repoId !== void 0 && signals.serviceName) {
|
|
1125
|
+
candidates = implementationContextCandidates(db, allCandidates, signals.repoId, signals.serviceName);
|
|
1126
|
+
if (candidates.length === 0)
|
|
1127
|
+
return {
|
|
1128
|
+
status: "unresolved",
|
|
1129
|
+
candidates: allCandidates.filter((c) => serviceMatches(c, signals.serviceName)),
|
|
1130
|
+
reasons: allCandidates.some((c) => serviceMatches(c, signals.serviceName)) ? ["local_service_candidate_without_caller_ownership"] : ["no_operation_candidates"]
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1106
1133
|
if (candidates.length === 0)
|
|
1107
1134
|
return {
|
|
1108
1135
|
status: "unresolved",
|
|
@@ -1173,6 +1200,34 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
1173
1200
|
reasons: ["candidate_score_below_resolution_threshold"]
|
|
1174
1201
|
};
|
|
1175
1202
|
}
|
|
1203
|
+
function serviceMatches(candidate, serviceName) {
|
|
1204
|
+
if (!serviceName) return false;
|
|
1205
|
+
const simple = serviceName.split(".").at(-1) ?? serviceName;
|
|
1206
|
+
return candidate.qualifiedName === serviceName || candidate.serviceName === serviceName || candidate.serviceName === simple || candidate.servicePath === serviceName || candidate.servicePath === `/${serviceName}` || candidate.servicePath === `/${simple}` || candidate.servicePath.endsWith(`/${simple}`);
|
|
1207
|
+
}
|
|
1208
|
+
function implementationContextCandidates(db, candidates, callerRepoId, serviceName) {
|
|
1209
|
+
const matching = candidates.filter((candidate) => serviceMatches(candidate, serviceName));
|
|
1210
|
+
const owned = matching.map((candidate) => ownershipReason(db, candidate, callerRepoId)).filter((item) => Boolean(item));
|
|
1211
|
+
if (owned.length === 0) return [];
|
|
1212
|
+
const direct = owned.filter((item) => item.reason !== "caller_depends_on_model_package");
|
|
1213
|
+
const chosen = direct.length > 0 ? direct : owned.length === 1 ? owned : [];
|
|
1214
|
+
return chosen.map((item) => ({ ...item.candidate, score: 0.95, reasons: [...item.candidate.reasons, "implementation_context_caller_ownership", item.reason] }));
|
|
1215
|
+
}
|
|
1216
|
+
function ownershipReason(db, candidate, callerRepoId) {
|
|
1217
|
+
const edge = db.prepare("SELECT status,evidence_json,to_id FROM graph_edges WHERE edge_type='OPERATION_IMPLEMENTED_BY_HANDLER' AND from_kind='operation' AND from_id=? ORDER BY CASE status WHEN 'resolved' THEN 0 WHEN 'ambiguous' THEN 1 ELSE 2 END LIMIT 1").get(String(candidate.operationId));
|
|
1218
|
+
if (edge?.status === "resolved") {
|
|
1219
|
+
const row = db.prepare("SELECT hc.repo_id repoId FROM handler_methods hm JOIN handler_classes hc ON hc.id=hm.handler_class_id WHERE hm.id=?").get(edge.to_id);
|
|
1220
|
+
if (row?.repoId === callerRepoId) return { candidate, reason: "resolved_implementation_handler_repo_matches_caller" };
|
|
1221
|
+
}
|
|
1222
|
+
if (edge?.evidence_json) {
|
|
1223
|
+
const evidence = JSON.parse(edge.evidence_json);
|
|
1224
|
+
const hit = evidence.candidates?.find((item) => item.accepted && (Number(item.handlerPackage?.id) === callerRepoId || Number(item.applicationPackage?.id) === callerRepoId));
|
|
1225
|
+
if (hit) return { candidate, reason: edge.status === "ambiguous" ? "ambiguous_implementation_candidate_repo_matches_caller" : "registration_package_matches_caller" };
|
|
1226
|
+
}
|
|
1227
|
+
const dep = db.prepare("SELECT 1 FROM graph_edges WHERE edge_type='REPO_IMPORTS_HELPER_PACKAGE' AND status='resolved' AND from_kind='repo' AND from_id=? AND to_id=?").get(String(callerRepoId), String(candidate.repoId));
|
|
1228
|
+
if (dep) return { candidate, reason: "caller_depends_on_model_package" };
|
|
1229
|
+
return void 0;
|
|
1230
|
+
}
|
|
1176
1231
|
function matchesLocalRepo(db, operationId, repoId) {
|
|
1177
1232
|
if (repoId === void 0) return true;
|
|
1178
1233
|
const row = db.prepare("SELECT s.repo_id repoId FROM cds_operations o JOIN cds_services s ON s.id=o.service_id WHERE o.id=?").get(operationId);
|
|
@@ -1227,8 +1282,8 @@ function linkWorkspace(db, workspaceId, vars = {}) {
|
|
|
1227
1282
|
const generation = nextGraphGeneration(db, workspaceId);
|
|
1228
1283
|
db.prepare("DELETE FROM graph_edges WHERE workspace_id=?").run(workspaceId);
|
|
1229
1284
|
const deps = linkHelperPackages(db, workspaceId, generation);
|
|
1230
|
-
const callSummary = linkCalls(db, workspaceId, vars, generation);
|
|
1231
1285
|
const impl = linkImplementations(db, workspaceId, generation);
|
|
1286
|
+
const callSummary = linkCalls(db, workspaceId, vars, generation);
|
|
1232
1287
|
db.prepare("UPDATE repositories SET graph_generation=?, graph_stale_reason=NULL, graph_stale_at=NULL WHERE workspace_id=?").run(generation, workspaceId);
|
|
1233
1288
|
return { ...callSummary, edgeCount: deps.edgeCount + callSummary.edgeCount + impl.edgeCount, dependencyResolvedCount: deps.resolvedCount, dependencyAmbiguousCount: deps.ambiguousCount, implementationResolvedCount: impl.resolvedCount, implementationAmbiguousCount: impl.ambiguousCount, implementationUnresolvedCount: impl.unresolvedCount };
|
|
1234
1289
|
});
|
|
@@ -1633,6 +1688,17 @@ function implementationScope(db, operationId) {
|
|
|
1633
1688
|
const row = db.prepare("SELECT hc.repo_id repoId,hc.source_file sourceFile,s.id symbolId FROM handler_methods hm JOIN handler_classes hc ON hc.id=hm.handler_class_id LEFT JOIN symbols s ON s.repo_id=hc.repo_id AND s.source_file=hc.source_file AND s.name=hm.method_name WHERE hm.id=?").get(edge.to_id);
|
|
1634
1689
|
return { repoId: row?.repoId, files: new Set(row?.sourceFile ? [row.sourceFile] : []), symbolId: row?.symbolId, edge };
|
|
1635
1690
|
}
|
|
1691
|
+
function contextImplementationMethodId(edge, callerRepoId) {
|
|
1692
|
+
if (!edge || edge.status !== "ambiguous" || callerRepoId === void 0) return void 0;
|
|
1693
|
+
const evidence = JSON.parse(String(edge.evidence_json || "{}"));
|
|
1694
|
+
const candidate = evidence.candidates?.find((item) => item.accepted && (Number(item.handlerPackage?.id) === callerRepoId || Number(item.applicationPackage?.id) === callerRepoId));
|
|
1695
|
+
return candidate?.methodId === void 0 ? void 0 : String(candidate.methodId);
|
|
1696
|
+
}
|
|
1697
|
+
function handlerScope(db, methodId) {
|
|
1698
|
+
const row = db.prepare("SELECT hc.repo_id repoId,hc.source_file sourceFile,s.id symbolId FROM handler_methods hm JOIN handler_classes hc ON hc.id=hm.handler_class_id LEFT JOIN symbols s ON s.repo_id=hc.repo_id AND s.source_file=hc.source_file AND s.name=hm.method_name WHERE hm.id=?").get(methodId);
|
|
1699
|
+
if (!row) return void 0;
|
|
1700
|
+
return { repoId: row.repoId, files: new Set(row.sourceFile ? [row.sourceFile] : []), symbolId: row.symbolId };
|
|
1701
|
+
}
|
|
1636
1702
|
function includeCall(type, options) {
|
|
1637
1703
|
if (!options.includeDb && type === "local_db_query") return false;
|
|
1638
1704
|
if (!options.includeExternal && type === "external_http") return false;
|
|
@@ -1716,6 +1782,7 @@ function edgeTarget(row, evidence) {
|
|
|
1716
1782
|
const operationPath = typeof evidence.operationPath === "string" ? evidence.operationPath : void 0;
|
|
1717
1783
|
const targetOperation = typeof evidence.targetOperation === "string" ? evidence.targetOperation : void 0;
|
|
1718
1784
|
const targetRepo = typeof evidence.targetRepo === "string" ? evidence.targetRepo : "";
|
|
1785
|
+
if (row.edge_type === "HANDLER_RUNS_DB_QUERY") return /^\d+$/.test(row.to_id) ? "Entity: unknown" : `Entity: ${row.to_id}`;
|
|
1719
1786
|
return servicePath && operationPath ? `${servicePath}${operationPath}` : targetOperation ? `${targetRepo}:${targetOperation}` : row.to_id;
|
|
1720
1787
|
}
|
|
1721
1788
|
function trace(db, start, options) {
|
|
@@ -1827,9 +1894,11 @@ function trace(db, start, options) {
|
|
|
1827
1894
|
});
|
|
1828
1895
|
if (effectiveRow.to_kind === "operation") {
|
|
1829
1896
|
const implementation = implementationScope(db, effectiveRow.to_id);
|
|
1897
|
+
const contextMethodId = contextImplementationMethodId(implementation.edge, current.repoId);
|
|
1898
|
+
const contextNode = contextMethodId ? handlerMethodNode(db, contextMethodId) : void 0;
|
|
1830
1899
|
if (implementation.edge) {
|
|
1831
1900
|
const implEvidence = JSON.parse(String(implementation.edge.evidence_json || "{}"));
|
|
1832
|
-
const handlerNode = implementation.edge.status === "resolved" ? handlerMethodNode(db, implementation.edge.to_id) :
|
|
1901
|
+
const handlerNode = implementation.edge.status === "resolved" ? handlerMethodNode(db, implementation.edge.to_id) : contextNode;
|
|
1833
1902
|
const implTo = handlerNode?.label ? String(handlerNode.label) : `${implementation.edge.to_kind}:${implementation.edge.to_id}`;
|
|
1834
1903
|
if (handlerNode) nodes.set(String(handlerNode.id), handlerNode);
|
|
1835
1904
|
edges.push({
|
|
@@ -1843,10 +1912,11 @@ function trace(db, start, options) {
|
|
|
1843
1912
|
});
|
|
1844
1913
|
}
|
|
1845
1914
|
if (current.depth >= maxDepth) continue;
|
|
1846
|
-
const
|
|
1847
|
-
const
|
|
1848
|
-
|
|
1849
|
-
|
|
1915
|
+
const contextScope = contextMethodId ? handlerScope(db, contextMethodId) : void 0;
|
|
1916
|
+
const files = contextScope?.files ?? (implementation.files.size > 0 ? implementation.files : handlerFilesForOperation(db, effectiveRow.to_id));
|
|
1917
|
+
const symbolIds = contextScope?.symbolId ? /* @__PURE__ */ new Set([contextScope.symbolId]) : implementation.symbolId ? /* @__PURE__ */ new Set([implementation.symbolId]) : void 0;
|
|
1918
|
+
if ((implementation.edge?.status === "resolved" || contextScope) && files.size > 0) {
|
|
1919
|
+
const targetRepoId = contextScope?.repoId ?? implementation.repoId ?? db.prepare(
|
|
1850
1920
|
"SELECT s.repo_id repoId FROM cds_operations o JOIN cds_services s ON s.id=o.service_id WHERE o.id=?"
|
|
1851
1921
|
).get(effectiveRow.to_id)?.repoId;
|
|
1852
1922
|
const nextKey = `${targetRepoId ?? "*"}:${[...symbolIds ?? /* @__PURE__ */ new Set(["*"])].sort().join(",")}:${[...files].sort().join(",")}`;
|
|
@@ -1893,4 +1963,4 @@ export {
|
|
|
1893
1963
|
linkWorkspace,
|
|
1894
1964
|
trace
|
|
1895
1965
|
};
|
|
1896
|
-
//# sourceMappingURL=chunk-
|
|
1966
|
+
//# sourceMappingURL=chunk-Q7W7TE3W.js.map
|