@saptools/service-flow 0.1.13 → 0.1.15
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 +17 -0
- package/README.md +5 -0
- package/TECHNICAL-NOTE.md +11 -4
- package/dist/{chunk-Q7W7TE3W.js → chunk-ZSUL2X77.js} +42 -14
- package/dist/chunk-ZSUL2X77.js.map +1 -0
- package/dist/cli.js +125 -26
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-Q7W7TE3W.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.15
|
|
4
|
+
|
|
5
|
+
- Fixed `symbol_calls.evidence_json` persistence so inserted rows store the parser evidence object instead of a numeric repository id, with explicit initial resolved/unresolved statuses.
|
|
6
|
+
- Added strict doctor diagnostics for non-object symbol-call evidence, documented unresolved/unknown parser-quality thresholds, and outbound-call source-symbol ownership ratios.
|
|
7
|
+
- Made exported public static class methods addressable through relative imports while keeping non-exported, private/protected, and package-imported class member calls conservative.
|
|
8
|
+
- Indexed exported shorthand object maps as alias symbols and added conservative proxy-variable evidence for `const worker = ExportedClass.staticFactory(); worker.method()` flows when the factory comes from a relative import.
|
|
9
|
+
- Preserved 0.1.14 symbol-call noise filtering, semantic unknown DB targets, terminal transport-client classification, and strict parser-quality aggregates.
|
|
10
|
+
|
|
11
|
+
## 0.1.14
|
|
12
|
+
|
|
13
|
+
- Made local symbol-call indexing opt-in: CAP DSL, request helpers, package namespace/CommonJS calls, global runtime APIs, and service-client transport helpers are filtered unless indexed local or relative-import evidence makes the edge actionable.
|
|
14
|
+
- Expanded local DB query entity extraction for `SELECT.one(Entity)`, `UPSERT.into(Entity)`, `UPDATE.entity(Entity)`, static element access such as `this.model['Books']`, and clearer dynamic-query warning reasons.
|
|
15
|
+
- Persisted unknown DB query graph targets as semantic `db_entity:unknown` terminal nodes with source `callId` and parser-warning evidence, so fresh relinks no longer store numeric call ids as DB targets.
|
|
16
|
+
- Classified unresolved local service-client `.send`, `.emit`, `.publish`, and `.on` calls as terminal transport/client calls when the model does not declare a matching operation, while preserving real declared operations.
|
|
17
|
+
- Tightened doctor default local-service warnings and added compact `doctor --strict` symbol-call and DB-query quality aggregates with capped top unresolved examples.
|
|
18
|
+
- Added neutral regression fixtures for symbol-call noise, DB query forms, and local service-client methods.
|
|
19
|
+
|
|
3
20
|
## 0.1.13
|
|
4
21
|
|
|
5
22
|
- 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.
|
package/README.md
CHANGED
|
@@ -18,6 +18,10 @@ Index independent Git repositories, persist CAP/CDS facts in SQLite, resolve cro
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## 0.1.15 quality update
|
|
22
|
+
|
|
23
|
+
`service-flow` 0.1.15 fixes symbol-call evidence persistence so fresh databases store object-shaped parser evidence, extends strict doctor checks for evidence and parser-quality ratios, and conservatively resolves exported static class-member and exported shorthand object-map helper flows through relative imports without restoring broad symbol-call fallback noise.
|
|
24
|
+
|
|
21
25
|
## ✨ Features
|
|
22
26
|
|
|
23
27
|
- 🧭 **Cross-repository CAP tracing** — starts from a repo, service, operation path, operation name, or handler and follows the indexed flow across workspace boundaries
|
|
@@ -69,6 +73,7 @@ npm install @saptools/service-flow
|
|
|
69
73
|
- Fresh databases include foreign keys for key graph, run, and diagnostic tables. Migrated legacy stores that still lack that metadata are reported by doctor with `legacy_schema_weaker_foreign_keys`; rebuild into a fresh database if strict structural parity is required.
|
|
70
74
|
- Parser warnings describe analysis completeness, while routing status describes graph behavior. A terminal DB edge can remain terminal while still exposing parser warning evidence about an unknown entity.
|
|
71
75
|
|
|
76
|
+
|
|
72
77
|
## 🚀 Quick Start
|
|
73
78
|
|
|
74
79
|
```bash
|
package/TECHNICAL-NOTE.md
CHANGED
|
@@ -6,13 +6,20 @@
|
|
|
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.
|
|
9
|
+
## 0.1.15 audit follow-up notes
|
|
10
|
+
|
|
11
|
+
- Symbol-call rows now require object-shaped parser evidence in strict doctor; this catches numeric JSON regressions that `json_valid()` alone would miss.
|
|
12
|
+
- Relative-import symbol resolution remains opt-in, but exported public static class members and exported shorthand object-map aliases are now addressable with explicit evidence (`exported_class_member`, `exported_object_shorthand`, and `relative_import_proxy_member`).
|
|
13
|
+
- Strict parser-quality aggregates document thresholds for symbol-call unresolved ratio (5%), local DB query unknown ratio (25%), and outbound calls without source-symbol ownership (1%).
|
|
14
|
+
|
|
15
|
+
## 0.1.14 audit follow-up notes
|
|
10
16
|
|
|
11
17
|
- 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
18
|
- 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).
|
|
14
|
-
-
|
|
15
|
-
- Symbol-call indexing is
|
|
19
|
+
- CAP DB entity extraction for local `cds.run(...)` calls uses TypeScript AST traversal across chained query expressions, including `SELECT.from(Entity)`, `SELECT.one.from(Entity)`, `SELECT.one(Entity)`, `INSERT.into(Entity)`, `UPSERT.into(Entity)`, `UPDATE(Entity)`, `UPDATE.entity(Entity)`, `DELETE.from(Entity)`, and static element access such as `this.model['Books']`. Dynamic or unknowable query targets remain terminal graph edges with parser-warning evidence such as `dynamic_entity_expression`.
|
|
20
|
+
- Fresh relinks persist unknown DB query targets semantically as `to_kind = db_entity` and `to_id = unknown`; no schema migration is required for this row-level graph change. JSON, table, and Mermaid output use `Entity: unknown`, while evidence keeps the source `callId` and parser warning for machines.
|
|
21
|
+
- Symbol-call indexing is opt-in. Same-file symbols, indexed `this.method()` calls, exact relative import/export matches, and exported object-literal helper methods are kept; CAP DSL, request helpers, package namespace/CommonJS calls, global runtime APIs, and generic transport helpers are filtered unless local indexed evidence makes the edge actionable.
|
|
22
|
+
- `doctor --strict` reports compact parser-quality aggregates for symbol calls and local DB query known/unknown ratios; default doctor stays focused on actionable warnings.
|
|
16
23
|
- 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
24
|
|
|
18
25
|
## 0.1.4 trace-correctness additions
|
|
@@ -559,6 +559,7 @@ function entityFromExpression(expr) {
|
|
|
559
559
|
if (!expr) return void 0;
|
|
560
560
|
if (ts4.isIdentifier(expr) || ts4.isStringLiteral(expr) || ts4.isNoSubstitutionTemplateLiteral(expr)) return expr.text;
|
|
561
561
|
if (ts4.isPropertyAccessExpression(expr) && expr.expression.kind === ts4.SyntaxKind.ThisKeyword) return expr.name.text;
|
|
562
|
+
if (ts4.isElementAccessExpression(expr) && expr.argumentExpression && (ts4.isStringLiteral(expr.argumentExpression) || ts4.isNoSubstitutionTemplateLiteral(expr.argumentExpression))) return expr.argumentExpression.text;
|
|
562
563
|
return void 0;
|
|
563
564
|
}
|
|
564
565
|
function expressionName(expr) {
|
|
@@ -566,29 +567,45 @@ function expressionName(expr) {
|
|
|
566
567
|
if (ts4.isPropertyAccessExpression(expr)) return `${expressionName(expr.expression)}.${expr.name.text}`;
|
|
567
568
|
return expr.getText();
|
|
568
569
|
}
|
|
569
|
-
function
|
|
570
|
-
|
|
570
|
+
function variableInitializers(source) {
|
|
571
|
+
const initializers = /* @__PURE__ */ new Map();
|
|
572
|
+
const visit = (node) => {
|
|
573
|
+
if (ts4.isVariableDeclaration(node) && ts4.isIdentifier(node.name) && node.initializer && (node.parent.flags & ts4.NodeFlags.Const) !== 0) initializers.set(node.name.text, node.initializer);
|
|
574
|
+
ts4.forEachChild(node, visit);
|
|
575
|
+
};
|
|
576
|
+
visit(source);
|
|
577
|
+
return initializers;
|
|
578
|
+
}
|
|
579
|
+
function queryEntityFromAst(expr, initializers = /* @__PURE__ */ new Map()) {
|
|
580
|
+
if (ts4.isParenthesizedExpression(expr) || ts4.isAwaitExpression(expr)) return queryEntityFromAst(expr.expression, initializers);
|
|
581
|
+
if (ts4.isIdentifier(expr) && initializers.has(expr.text)) return queryEntityFromAst(initializers.get(expr.text), initializers);
|
|
571
582
|
if (ts4.isCallExpression(expr)) {
|
|
572
583
|
const name = expressionName(expr.expression);
|
|
573
|
-
if (name === "cds.run") return queryEntityFromAst(expr.arguments[0]);
|
|
574
|
-
if (
|
|
584
|
+
if (name === "cds.run") return queryEntityFromAst(expr.arguments[0], initializers);
|
|
585
|
+
if (["SELECT.one.from", "SELECT.from", "SELECT.one", "INSERT.into", "UPSERT.into", "DELETE.from", "UPDATE.entity"].includes(name)) return entityFromExpression(expr.arguments[0]);
|
|
575
586
|
if (name === "UPDATE") return entityFromExpression(expr.arguments[0]);
|
|
576
587
|
const receiver = ts4.isPropertyAccessExpression(expr.expression) ? expr.expression.expression : void 0;
|
|
577
|
-
if (receiver) return queryEntityFromAst(receiver);
|
|
588
|
+
if (receiver) return queryEntityFromAst(receiver, initializers);
|
|
578
589
|
}
|
|
579
590
|
return void 0;
|
|
580
591
|
}
|
|
581
592
|
function extractQueryEntity(expr) {
|
|
582
593
|
const source = ts4.createSourceFile("query.ts", `const __query = (${expr});`, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
|
|
594
|
+
const initializers = variableInitializers(source);
|
|
583
595
|
let found;
|
|
584
596
|
const visit = (node) => {
|
|
585
597
|
if (found) return;
|
|
586
|
-
if (ts4.isParenthesizedExpression(node)) found = queryEntityFromAst(node.expression);
|
|
598
|
+
if (ts4.isParenthesizedExpression(node)) found = queryEntityFromAst(node.expression, initializers);
|
|
587
599
|
ts4.forEachChild(node, visit);
|
|
588
600
|
};
|
|
589
601
|
visit(source);
|
|
590
602
|
return found;
|
|
591
603
|
}
|
|
604
|
+
function queryWarning(expr) {
|
|
605
|
+
if (/^\s*[`'"]/.test(expr)) return "raw_sql_or_cql_expression";
|
|
606
|
+
if (/^\s*\w+\s*$/.test(expr)) return "query_variable_without_static_initializer";
|
|
607
|
+
return "dynamic_entity_expression";
|
|
608
|
+
}
|
|
592
609
|
async function parseOutboundCalls(repoPath, filePath) {
|
|
593
610
|
const text = await fs6.readFile(path7.join(repoPath, filePath), "utf8");
|
|
594
611
|
const out = [];
|
|
@@ -620,7 +637,7 @@ async function parseOutboundCalls(repoPath, filePath) {
|
|
|
620
637
|
sourceFile: normalizePath(filePath),
|
|
621
638
|
sourceLine: lineOf3(text, m.index ?? 0),
|
|
622
639
|
confidence: entity ? 0.9 : 0.55,
|
|
623
|
-
unresolvedReason: entity ? void 0 :
|
|
640
|
+
unresolvedReason: entity ? void 0 : queryWarning(expr)
|
|
624
641
|
});
|
|
625
642
|
}
|
|
626
643
|
for (const m of text.matchAll(
|
|
@@ -668,7 +685,8 @@ function parseLocalServiceCalls(text, filePath) {
|
|
|
668
685
|
aliasChain: parsed.chain,
|
|
669
686
|
sourceFile: normalizePath(filePath),
|
|
670
687
|
sourceLine: lineOf3(text, node.getStart(source)),
|
|
671
|
-
confidence: 0.9
|
|
688
|
+
confidence: 0.9,
|
|
689
|
+
unresolvedReason: ["send", "emit", "publish", "on"].includes(parsed.operation) ? "transport_client_method" : void 0
|
|
672
690
|
});
|
|
673
691
|
}
|
|
674
692
|
ts4.forEachChild(node, visit);
|
|
@@ -1168,6 +1186,10 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
1168
1186
|
c.score += 0.2;
|
|
1169
1187
|
c.reasons.push(signals.repoId !== void 0 ? "explicit_local_service_call" : "explicit_dynamic_override");
|
|
1170
1188
|
}
|
|
1189
|
+
if (signals.repoId !== void 0 && candidates.length === 1 && signals.serviceName && c.reasons.includes("local_service_name_mismatch") && (c.operationPath === signals.operationPath || c.operationName === signals.operationPath.replace(/^\//, ""))) {
|
|
1190
|
+
c.score = Math.max(c.score, 0.9);
|
|
1191
|
+
c.reasons.push("same_repo_unique_operation_path_with_lookup_mismatch");
|
|
1192
|
+
}
|
|
1171
1193
|
}
|
|
1172
1194
|
for (const c of candidates) c.score = Math.max(0, Math.min(1, c.score));
|
|
1173
1195
|
candidates.sort(
|
|
@@ -1187,7 +1209,7 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
1187
1209
|
candidates,
|
|
1188
1210
|
reasons: ["operation_path_only_has_no_strong_target_signal"]
|
|
1189
1211
|
};
|
|
1190
|
-
if (best && best.score >= 0.9 && (best.servicePath === signals.servicePath || Boolean(signals.serviceName && !best.reasons.includes("local_service_name_mismatch"))) && (best.operationPath === signals.operationPath || best.operationName === signals.operationPath.replace(/^\//, "")) && (!second || best.score - second.score >= 0.25))
|
|
1212
|
+
if (best && best.score >= 0.9 && (best.servicePath === signals.servicePath || Boolean(signals.serviceName && (!best.reasons.includes("local_service_name_mismatch") || best.reasons.includes("same_repo_unique_operation_path_with_lookup_mismatch")))) && (best.operationPath === signals.operationPath || best.operationName === signals.operationPath.replace(/^\//, "")) && (!second || best.score - second.score >= 0.25))
|
|
1191
1213
|
return {
|
|
1192
1214
|
status: "resolved",
|
|
1193
1215
|
target: best,
|
|
@@ -1320,6 +1342,10 @@ function insertCallEdge(db, workspaceId, call, vars, generation) {
|
|
|
1320
1342
|
const isOperationCall = callType.startsWith("remote") || callType === "local_service_call";
|
|
1321
1343
|
const resolution = isOperationCall ? resolveOperation(db, { servicePath, operationPath: op, serviceName: call.local_service_name, repoId: callType === "local_service_call" ? Number(call.repo_id) : void 0, alias: applyVariables(call.aliasExpr ?? call.alias, vars), destination: destination ? applyVariables(destination, vars) : void 0, isDynamic, hasExplicitOverride: Object.keys(vars).length > 0 || callType === "local_service_call" }, workspaceId) : { status: "unresolved", candidates: [], reasons: [] };
|
|
1322
1344
|
const evidence = callEvidence(call, resolution, servicePath, op, destination ? applyVariables(destination, vars) : void 0);
|
|
1345
|
+
if (callType === "local_service_call" && call.unresolved_reason === "transport_client_method" && !resolution.target && resolution.candidates.length === 0) {
|
|
1346
|
+
db.prepare("INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)").run(workspaceId, "HANDLER_CALLS_EXTERNAL_HTTP", "terminal", "call", String(call.id), "external", String(op || "transport_client_method"), Number(call.confidence ?? 0.5), JSON.stringify({ ...evidence, classification: "transport_client_method" }), 0, generation);
|
|
1347
|
+
return { status: "terminal" };
|
|
1348
|
+
}
|
|
1323
1349
|
if (resolution.target) {
|
|
1324
1350
|
db.prepare("INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?)").run(workspaceId, callType === "local_service_call" ? "LOCAL_CALL_RESOLVES_TO_OPERATION" : "REMOTE_CALL_RESOLVES_TO_OPERATION", "resolved", "call", String(call.id), "operation", String(resolution.target.operationId), resolution.target.score, JSON.stringify(evidence), isDynamic ? 1 : 0, generation);
|
|
1325
1351
|
return { status: "resolved" };
|
|
@@ -1327,11 +1353,13 @@ function insertCallEdge(db, workspaceId, call, vars, generation) {
|
|
|
1327
1353
|
const edgeType = callType === "local_db_query" ? "HANDLER_RUNS_DB_QUERY" : callType === "external_http" ? "HANDLER_CALLS_EXTERNAL_HTTP" : callType === "async_emit" ? "HANDLER_EMITS_EVENT" : callType === "async_subscribe" ? "EVENT_CONSUMED_BY_HANDLER" : resolution.status === "dynamic" ? "DYNAMIC_EDGE_CANDIDATE" : "UNRESOLVED_EDGE";
|
|
1328
1354
|
const status = edgeType === "DYNAMIC_EDGE_CANDIDATE" ? "dynamic" : resolution.status === "ambiguous" ? "ambiguous" : edgeType === "UNRESOLVED_EDGE" ? "unresolved" : "terminal";
|
|
1329
1355
|
const unresolvedReason = status === "terminal" ? null : String(call.unresolved_reason ?? (resolution.status === "ambiguous" ? "Ambiguous operation candidates require a strong service signal" : resolution.status === "dynamic" ? `Dynamic target requires runtime variable overrides: ${(resolution.reasons.length ? resolution.reasons : ["missing runtime variables"]).join(", ")}` : "No indexed target operation matched"));
|
|
1330
|
-
|
|
1356
|
+
const targetKind = callType === "local_db_query" ? "db_entity" : callType.startsWith("async_") ? "event" : "external";
|
|
1357
|
+
const targetId = callType === "local_db_query" ? String(call.query_entity ?? "unknown") : String(call.event_name_expr ?? op ?? call.id);
|
|
1358
|
+
db.prepare("INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,unresolved_reason,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)").run(workspaceId, edgeType, status, "call", String(call.id), targetKind, targetId, Number(call.confidence ?? 0.2), JSON.stringify(evidence), isDynamic || resolution.status === "dynamic" ? 1 : 0, unresolvedReason, generation);
|
|
1331
1359
|
return { status };
|
|
1332
1360
|
}
|
|
1333
1361
|
function callEvidence(call, resolution, servicePath, op, destination) {
|
|
1334
|
-
return { sourceFile: call.source_file, sourceLine: call.source_line, file: call.source_file, line: call.source_line, repo: call.repoName, serviceAlias: call.alias, serviceAliasExpr: call.aliasExpr, destination, servicePath, operationPath: op, localServiceName: call.local_service_name, localServiceLookup: call.local_service_lookup, aliasChain: call.alias_chain_json ? JSON.parse(String(call.alias_chain_json)) : void 0, transport: call.call_type === "local_service_call" ? "local" : void 0, targetRepo: resolution.target?.repoName, targetOperation: resolution.target?.operationName, helperChain: call.helperChainJson ? JSON.parse(String(call.helperChainJson)) : void 0, candidates: resolution.candidates, candidateCount: resolution.candidates.length, resolutionStatus: resolution.status, resolutionReasons: resolution.reasons, analysisCompleteness: call.unresolved_reason ? "partial" : "complete", parserWarning: call.unresolved_reason ? { code: "parser_warning", message: call.unresolved_reason } : void 0 };
|
|
1362
|
+
return { sourceFile: call.source_file, sourceLine: call.source_line, file: call.source_file, line: call.source_line, callId: call.id, repo: call.repoName, serviceAlias: call.alias, serviceAliasExpr: call.aliasExpr, destination, servicePath, operationPath: op, localServiceName: call.local_service_name, localServiceLookup: call.local_service_lookup, aliasChain: call.alias_chain_json ? JSON.parse(String(call.alias_chain_json)) : void 0, transport: call.call_type === "local_service_call" ? "local" : void 0, targetRepo: resolution.target?.repoName, targetOperation: resolution.target?.operationName, helperChain: call.helperChainJson ? JSON.parse(String(call.helperChainJson)) : void 0, candidates: resolution.candidates, candidateCount: resolution.candidates.length, resolutionStatus: resolution.status, resolutionReasons: resolution.reasons, analysisCompleteness: call.unresolved_reason ? "partial" : "complete", parserWarning: call.unresolved_reason ? { code: "parser_warning", message: call.unresolved_reason } : void 0 };
|
|
1335
1363
|
}
|
|
1336
1364
|
function linkImplementations(db, workspaceId, generation) {
|
|
1337
1365
|
const operations = db.prepare(`SELECT o.id operationId,o.operation_path operationPath,o.operation_name operationName,s.service_path servicePath,s.repo_id modelRepoId,r.name modelRepo,r.package_name modelPackage,r.kind modelKind FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id WHERE r.workspace_id=?`).all(workspaceId);
|
|
@@ -1782,7 +1810,7 @@ function edgeTarget(row, evidence) {
|
|
|
1782
1810
|
const operationPath = typeof evidence.operationPath === "string" ? evidence.operationPath : void 0;
|
|
1783
1811
|
const targetOperation = typeof evidence.targetOperation === "string" ? evidence.targetOperation : void 0;
|
|
1784
1812
|
const targetRepo = typeof evidence.targetRepo === "string" ? evidence.targetRepo : "";
|
|
1785
|
-
if (row.edge_type === "HANDLER_RUNS_DB_QUERY") return
|
|
1813
|
+
if (row.edge_type === "HANDLER_RUNS_DB_QUERY") return `Entity: ${row.to_id || "unknown"}`;
|
|
1786
1814
|
return servicePath && operationPath ? `${servicePath}${operationPath}` : targetOperation ? `${targetRepo}:${targetOperation}` : row.to_id;
|
|
1787
1815
|
}
|
|
1788
1816
|
function trace(db, start, options) {
|
|
@@ -1880,7 +1908,7 @@ function trace(db, start, options) {
|
|
|
1880
1908
|
nodes.set(targetNode, opNode ?? {
|
|
1881
1909
|
id: targetNode,
|
|
1882
1910
|
kind: effectiveRow.to_kind,
|
|
1883
|
-
label: effectiveRow.to_id
|
|
1911
|
+
label: effectiveRow.to_kind === "db_entity" ? `Entity: ${effectiveRow.to_id || "unknown"}` : effectiveRow.to_id
|
|
1884
1912
|
});
|
|
1885
1913
|
const to = edgeTarget(effectiveRow, evidence);
|
|
1886
1914
|
edges.push({
|
|
@@ -1963,4 +1991,4 @@ export {
|
|
|
1963
1991
|
linkWorkspace,
|
|
1964
1992
|
trace
|
|
1965
1993
|
};
|
|
1966
|
-
//# sourceMappingURL=chunk-
|
|
1994
|
+
//# sourceMappingURL=chunk-ZSUL2X77.js.map
|