@saptools/service-flow 0.1.14 → 0.1.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.
- package/CHANGELOG.md +17 -0
- package/README.md +6 -2
- package/TECHNICAL-NOTE.md +14 -0
- package/dist/{chunk-UPXFMLUY.js → chunk-SSSBQIMK.js} +14 -6
- package/dist/chunk-SSSBQIMK.js.map +1 -0
- package/dist/cli.js +115 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-UPXFMLUY.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.16
|
|
4
|
+
|
|
5
|
+
- Index class property arrow/function members as executable symbols so outbound calls inside handler helper properties receive precise source-symbol ownership.
|
|
6
|
+
- Add conservative synthetic callback symbols for top-level CAP lifecycle, route, and event callbacks that contain supported outbound calls.
|
|
7
|
+
- Harden proxy-member symbol-call resolution with richer evidence and avoid ambiguous repository-wide member-name fallback.
|
|
8
|
+
- Split `service-flow link` operation-call output into remote resolved, local resolved, unresolved, ambiguous, dynamic, and terminal buckets.
|
|
9
|
+
- Extend strict doctor ownership diagnostics with ownerless outbound-call details by call type and syntactic gap.
|
|
10
|
+
- Keep `OutboundCallFact.sourceSymbolQualifiedName` aligned with persistence by preferring explicit qualified names before line-range fallback.
|
|
11
|
+
|
|
12
|
+
## 0.1.15
|
|
13
|
+
|
|
14
|
+
- 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.
|
|
15
|
+
- Added strict doctor diagnostics for non-object symbol-call evidence, documented unresolved/unknown parser-quality thresholds, and outbound-call source-symbol ownership ratios.
|
|
16
|
+
- Made exported public static class methods addressable through relative imports while keeping non-exported, private/protected, and package-imported class member calls conservative.
|
|
17
|
+
- 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.
|
|
18
|
+
- Preserved 0.1.14 symbol-call noise filtering, semantic unknown DB targets, terminal transport-client classification, and strict parser-quality aggregates.
|
|
19
|
+
|
|
3
20
|
## 0.1.14
|
|
4
21
|
|
|
5
22
|
- 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.
|
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.16 quality update
|
|
22
|
+
|
|
23
|
+
`service-flow` 0.1.16 tightens source ownership and proxy evidence after the 0.1.15 audit. It indexes class property arrow/function members, creates conservative synthetic callback symbols only for top-level framework callbacks containing supported outbound calls, prefers explicit outbound source-symbol names when provided, hardens proxy-member resolution away from ambiguous global name-only matches, and splits link output into remote/local/unresolved/ambiguous/dynamic/terminal operation-call buckets.
|
|
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
|
|
@@ -131,7 +135,7 @@ service-flow index --workspace /path/to/workspace --repo identity-service --forc
|
|
|
131
135
|
|
|
132
136
|
### 🔗 `service-flow link`
|
|
133
137
|
|
|
134
|
-
Resolve indexed outbound calls after repositories have been indexed. This rebuilds the `graph_edges` table for the workspace.
|
|
138
|
+
Resolve indexed outbound calls after repositories have been indexed. This rebuilds the `graph_edges` table for the workspace. The summary separates remote operation calls resolved, local operation calls resolved, unresolved operation calls, ambiguous operation calls, dynamic operation calls, and terminal call edges so local CAP service resolutions are not labeled as remote.
|
|
135
139
|
|
|
136
140
|
```bash
|
|
137
141
|
service-flow link --workspace /path/to/workspace
|
|
@@ -151,7 +155,7 @@ target handler up to `--depth` instead of showing only calls in the first file.
|
|
|
151
155
|
|
|
152
156
|
### Symbol-scoped helper traversal
|
|
153
157
|
|
|
154
|
-
`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.
|
|
158
|
+
`service-flow trace` starts from the selected handler method symbol, renders outbound calls owned by that symbol, and follows conservative local helper-call facts. Handler helper properties such as `helper = async () => { ... }` and `helper = function () { ... }` are indexed as `ClassName.helper`; top-level CAP lifecycle, route, and event callbacks receive synthetic `module:<file>#callback:<line>` owners only when their body contains a supported outbound call or event subscription. Supported helper edges include same-file functions, `this.method()` calls, and exactly mapped relative imports/exports that resolve to an indexed executable symbol. Proxy-member calls keep factory/import evidence and avoid resolving by repository-wide member name alone when the target is ambiguous. Calls from unrelated functions in the same source file are not included merely because the file path matches.
|
|
155
159
|
|
|
156
160
|
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.
|
|
157
161
|
|
package/TECHNICAL-NOTE.md
CHANGED
|
@@ -6,6 +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.16 audit follow-up notes
|
|
10
|
+
|
|
11
|
+
- Executable symbol parsing now treats class property arrow functions and function-expression initializers as method-like symbols with `ClassName.memberName` qualified names. Their body ranges are persisted so outbound-call ownership can use the existing shortest enclosing range lookup.
|
|
12
|
+
- Synthetic callback symbols are intentionally narrow: only top-level CAP lifecycle, route, and event-registration callbacks whose bodies contain supported outbound calls/subscriptions are indexed as `module:<relative-file>#callback:<line>`. Ordinary anonymous callbacks remain out of scope to avoid broad call-graph noise.
|
|
13
|
+
- Proxy-member calls record proxy variable, factory expression, factory import source, and candidate strategy. Resolution prefers explicit object-map evidence and treats ambiguous repository-wide member-name matches as ambiguous instead of picking the first symbol row. Full whole-program TypeScript data-flow remains a non-goal.
|
|
14
|
+
- `link` summaries now distinguish remote and local operation-call resolutions, while retaining the aggregate `resolvedCount` in the programmatic result for compatibility.
|
|
15
|
+
- Strict doctor source-ownership diagnostics include ownerless groupings by call type and syntactic gap so remaining ownerless calls can be audited without weakening the threshold.
|
|
16
|
+
|
|
17
|
+
## 0.1.15 audit follow-up notes
|
|
18
|
+
|
|
19
|
+
- Symbol-call rows now require object-shaped parser evidence in strict doctor; this catches numeric JSON regressions that `json_valid()` alone would miss.
|
|
20
|
+
- 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`).
|
|
21
|
+
- 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%).
|
|
22
|
+
|
|
9
23
|
## 0.1.14 audit follow-up notes
|
|
10
24
|
|
|
11
25
|
- 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.
|
|
@@ -1186,6 +1186,10 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
1186
1186
|
c.score += 0.2;
|
|
1187
1187
|
c.reasons.push(signals.repoId !== void 0 ? "explicit_local_service_call" : "explicit_dynamic_override");
|
|
1188
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
|
+
}
|
|
1189
1193
|
}
|
|
1190
1194
|
for (const c of candidates) c.score = Math.max(0, Math.min(1, c.score));
|
|
1191
1195
|
candidates.sort(
|
|
@@ -1205,7 +1209,7 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
1205
1209
|
candidates,
|
|
1206
1210
|
reasons: ["operation_path_only_has_no_strong_target_signal"]
|
|
1207
1211
|
};
|
|
1208
|
-
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))
|
|
1209
1213
|
return {
|
|
1210
1214
|
status: "resolved",
|
|
1211
1215
|
target: best,
|
|
@@ -1314,6 +1318,8 @@ function linkCalls(db, workspaceId, vars, generation) {
|
|
|
1314
1318
|
let edgeCount = 0;
|
|
1315
1319
|
let unresolvedCount = 0;
|
|
1316
1320
|
let resolvedCount = 0;
|
|
1321
|
+
let remoteResolvedCount = 0;
|
|
1322
|
+
let localResolvedCount = 0;
|
|
1317
1323
|
let ambiguousCount = 0;
|
|
1318
1324
|
let dynamicCount = 0;
|
|
1319
1325
|
let terminalCount = 0;
|
|
@@ -1322,12 +1328,14 @@ function linkCalls(db, workspaceId, vars, generation) {
|
|
|
1322
1328
|
const result = insertCallEdge(db, workspaceId, call, vars, generation);
|
|
1323
1329
|
edgeCount += 1;
|
|
1324
1330
|
resolvedCount += result.status === "resolved" ? 1 : 0;
|
|
1331
|
+
remoteResolvedCount += result.status === "resolved" && result.callType !== "local_service_call" ? 1 : 0;
|
|
1332
|
+
localResolvedCount += result.status === "resolved" && result.callType === "local_service_call" ? 1 : 0;
|
|
1325
1333
|
unresolvedCount += result.status === "unresolved" ? 1 : 0;
|
|
1326
1334
|
ambiguousCount += result.status === "ambiguous" ? 1 : 0;
|
|
1327
1335
|
dynamicCount += result.status === "dynamic" ? 1 : 0;
|
|
1328
1336
|
terminalCount += result.status === "terminal" ? 1 : 0;
|
|
1329
1337
|
}
|
|
1330
|
-
return { edgeCount, unresolvedCount, resolvedCount, ambiguousCount, dynamicCount, terminalCount };
|
|
1338
|
+
return { edgeCount, unresolvedCount, resolvedCount, remoteResolvedCount, localResolvedCount, ambiguousCount, dynamicCount, terminalCount };
|
|
1331
1339
|
}
|
|
1332
1340
|
function insertCallEdge(db, workspaceId, call, vars, generation) {
|
|
1333
1341
|
const callType = String(call.call_type);
|
|
@@ -1340,11 +1348,11 @@ function insertCallEdge(db, workspaceId, call, vars, generation) {
|
|
|
1340
1348
|
const evidence = callEvidence(call, resolution, servicePath, op, destination ? applyVariables(destination, vars) : void 0);
|
|
1341
1349
|
if (callType === "local_service_call" && call.unresolved_reason === "transport_client_method" && !resolution.target && resolution.candidates.length === 0) {
|
|
1342
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, "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);
|
|
1343
|
-
return { status: "terminal" };
|
|
1351
|
+
return { status: "terminal", callType };
|
|
1344
1352
|
}
|
|
1345
1353
|
if (resolution.target) {
|
|
1346
1354
|
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);
|
|
1347
|
-
return { status: "resolved" };
|
|
1355
|
+
return { status: "resolved", callType };
|
|
1348
1356
|
}
|
|
1349
1357
|
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";
|
|
1350
1358
|
const status = edgeType === "DYNAMIC_EDGE_CANDIDATE" ? "dynamic" : resolution.status === "ambiguous" ? "ambiguous" : edgeType === "UNRESOLVED_EDGE" ? "unresolved" : "terminal";
|
|
@@ -1352,7 +1360,7 @@ function insertCallEdge(db, workspaceId, call, vars, generation) {
|
|
|
1352
1360
|
const targetKind = callType === "local_db_query" ? "db_entity" : callType.startsWith("async_") ? "event" : "external";
|
|
1353
1361
|
const targetId = callType === "local_db_query" ? String(call.query_entity ?? "unknown") : String(call.event_name_expr ?? op ?? call.id);
|
|
1354
1362
|
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);
|
|
1355
|
-
return { status };
|
|
1363
|
+
return { status, callType };
|
|
1356
1364
|
}
|
|
1357
1365
|
function callEvidence(call, resolution, servicePath, op, destination) {
|
|
1358
1366
|
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 };
|
|
@@ -1987,4 +1995,4 @@ export {
|
|
|
1987
1995
|
linkWorkspace,
|
|
1988
1996
|
trace
|
|
1989
1997
|
};
|
|
1990
|
-
//# sourceMappingURL=chunk-
|
|
1998
|
+
//# sourceMappingURL=chunk-SSSBQIMK.js.map
|