@saptools/service-flow 0.1.19 → 0.1.21
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 +13 -0
- package/README.md +13 -5
- package/TECHNICAL-NOTE.md +14 -0
- package/dist/{chunk-YG66SHAR.js → chunk-TSMDIKML.js} +169 -23
- package/dist/chunk-TSMDIKML.js.map +1 -0
- package/dist/cli.js +28 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-YG66SHAR.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
parsePackageJson,
|
|
13
13
|
parseServiceBindings,
|
|
14
14
|
trace
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-TSMDIKML.js";
|
|
16
16
|
|
|
17
17
|
// src/cli.ts
|
|
18
18
|
import { Command } from "commander";
|
|
@@ -188,7 +188,7 @@ function migrate(db) {
|
|
|
188
188
|
// package.json
|
|
189
189
|
var package_default = {
|
|
190
190
|
name: "@saptools/service-flow",
|
|
191
|
-
version: "0.1.
|
|
191
|
+
version: "0.1.21",
|
|
192
192
|
description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
|
|
193
193
|
type: "module",
|
|
194
194
|
publishConfig: {
|
|
@@ -1179,9 +1179,11 @@ function parserQualityDiagnostics(db, strict) {
|
|
|
1179
1179
|
const outboundWithoutOwnershipRatio = outboundTotal === 0 ? 0 : Number((outboundWithoutOwnership / outboundTotal).toFixed(4));
|
|
1180
1180
|
const remoteQuery = remoteQueryTargetQuality(db);
|
|
1181
1181
|
const invocation = odataInvocationResolutionQuality(db);
|
|
1182
|
+
const remoteAction = remoteActionTargetQuality(db);
|
|
1182
1183
|
return [
|
|
1183
1184
|
remoteQuery,
|
|
1184
1185
|
invocation,
|
|
1186
|
+
remoteAction,
|
|
1185
1187
|
{ severity: Number(evidence.nonObject ?? 0) > 0 ? "warning" : "info", code: "strict_symbol_call_evidence_quality", message: "Symbol-call evidence JSON object aggregate", total: Number(evidence.total ?? 0), nonObject: Number(evidence.nonObject ?? 0) },
|
|
1186
1188
|
{ severity: Number(outboundEvidence.missing ?? 0) + Number(outboundEvidence.invalid ?? 0) + Number(outboundEvidence.nonObject ?? 0) > 0 ? "warning" : "info", code: "strict_outbound_evidence_quality", message: "Outbound parser evidence JSON object aggregate", total: Number(outboundEvidence.total ?? 0), missing: Number(outboundEvidence.missing ?? 0), invalid: Number(outboundEvidence.invalid ?? 0), nonObject: Number(outboundEvidence.nonObject ?? 0), examples: outboundEvidenceExamples },
|
|
1187
1189
|
{ severity: Number(graphEvidence.nonObject ?? 0) > 0 || Number(graphEvidence.withOutboundEvidence ?? 0) < Number(graphEvidence.total ?? 0) ? "warning" : "info", code: "strict_graph_evidence_quality", message: "Call-derived graph evidence and parser-evidence propagation aggregate", total: Number(graphEvidence.total ?? 0), nonObject: Number(graphEvidence.nonObject ?? 0), withOutboundEvidence: Number(graphEvidence.withOutboundEvidence ?? 0), examples: graphEvidenceExamples },
|
|
@@ -1206,14 +1208,33 @@ function remoteQueryTargetQuality(db) {
|
|
|
1206
1208
|
const unresolved = Number(aggregate.unresolved ?? 0);
|
|
1207
1209
|
return { severity: numericTargets + unresolved > 0 ? "warning" : "info", code: "strict_remote_query_target_quality", message: "Remote query terminal target quality aggregate", totalRemoteQueryCalls: Number(aggregate.total ?? 0), terminalRemoteQueryEdges: Number(aggregate.terminal ?? 0), numericTargetCount: numericTargets, unresolvedRemoteQueryCount: unresolved, examples };
|
|
1208
1210
|
}
|
|
1211
|
+
function remoteActionTargetQuality(db) {
|
|
1212
|
+
const aggregate = db.prepare(`SELECT COUNT(*) total,
|
|
1213
|
+
SUM(CASE WHEN e.status='unresolved' THEN 1 ELSE 0 END) unresolved,
|
|
1214
|
+
SUM(CASE WHEN e.status='unresolved' AND e.to_id GLOB '[0-9]*' THEN 1 ELSE 0 END) numericTargets,
|
|
1215
|
+
SUM(CASE WHEN e.status='unresolved' AND (e.to_id='Remote action: unknown path' OR e.to_id='Remote action: dynamic path') THEN 1 ELSE 0 END) semanticTargets
|
|
1216
|
+
FROM outbound_calls c LEFT JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT) WHERE c.call_type='remote_action'`).get();
|
|
1217
|
+
const examples = db.prepare(`SELECT c.source_file sourceFile,c.source_line sourceLine,c.operation_path_expr operationPath,e.status status,e.to_id target
|
|
1218
|
+
FROM outbound_calls c LEFT JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT)
|
|
1219
|
+
WHERE c.call_type='remote_action' AND e.status='unresolved' AND e.to_id GLOB '[0-9]*' ORDER BY c.source_file,c.source_line LIMIT 5`).all();
|
|
1220
|
+
const numericTargets = Number(aggregate.numericTargets ?? 0);
|
|
1221
|
+
return { severity: numericTargets > 0 ? "warning" : "info", code: "strict_remote_action_target_quality", message: "Remote action unresolved target quality aggregate", totalRemoteActionCalls: Number(aggregate.total ?? 0), unresolvedRemoteActionCalls: Number(aggregate.unresolved ?? 0), numericUnresolvedTargetCount: numericTargets, semanticUnknownOrDynamicTargetCount: Number(aggregate.semanticTargets ?? 0), examples };
|
|
1222
|
+
}
|
|
1209
1223
|
function odataInvocationResolutionQuality(db) {
|
|
1224
|
+
const aggregate = db.prepare(`SELECT COUNT(*) total,
|
|
1225
|
+
SUM(CASE WHEN e.status='resolved' THEN 1 ELSE 0 END) resolved,
|
|
1226
|
+
SUM(CASE WHEN e.status='dynamic' THEN 1 ELSE 0 END) dynamic,
|
|
1227
|
+
SUM(CASE WHEN e.status='ambiguous' THEN 1 ELSE 0 END) ambiguous,
|
|
1228
|
+
SUM(CASE WHEN e.status='unresolved' THEN 1 ELSE 0 END) unresolved
|
|
1229
|
+
FROM outbound_calls c JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT)
|
|
1230
|
+
WHERE c.call_type='remote_action' AND c.operation_path_expr LIKE '%(%'`).get();
|
|
1210
1231
|
const rows = db.prepare(`SELECT c.id id,c.operation_path_expr operationPathExpr,c.source_file sourceFile,c.source_line sourceLine,e.status status,e.unresolved_reason unresolvedReason
|
|
1211
1232
|
FROM outbound_calls c JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT)
|
|
1212
|
-
WHERE c.call_type='remote_action' AND e.status
|
|
1233
|
+
WHERE c.call_type='remote_action' AND e.status IN ('unresolved','ambiguous') AND c.operation_path_expr LIKE '%(%'
|
|
1213
1234
|
ORDER BY c.source_file,c.source_line LIMIT 100`).all();
|
|
1214
1235
|
const examples = [];
|
|
1215
1236
|
let unresolvedMatchingIndexedOperation = 0;
|
|
1216
|
-
let
|
|
1237
|
+
let ambiguousNormalizedCalls = 0;
|
|
1217
1238
|
for (const row of rows) {
|
|
1218
1239
|
const normalized = normalizeODataOperationInvocationPath(row.operationPathExpr);
|
|
1219
1240
|
if (!normalized?.wasInvocation) continue;
|
|
@@ -1221,11 +1242,11 @@ function odataInvocationResolutionQuality(db) {
|
|
|
1221
1242
|
const simpleName = normalizedName.split(".").at(-1) ?? normalizedName;
|
|
1222
1243
|
const candidates = db.prepare("SELECT s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName FROM cds_operations o JOIN cds_services s ON s.id=o.service_id WHERE o.operation_path IN (?,?) OR o.operation_name IN (?,?) ORDER BY s.service_path,o.operation_name LIMIT 5").all(normalized.normalizedOperationPath, `/${simpleName}`, normalizedName, simpleName);
|
|
1223
1244
|
if (candidates.length === 0) continue;
|
|
1224
|
-
|
|
1225
|
-
if (
|
|
1245
|
+
if (row.status === "ambiguous") ambiguousNormalizedCalls += 1;
|
|
1246
|
+
if (row.status === "unresolved") unresolvedMatchingIndexedOperation += 1;
|
|
1226
1247
|
if (examples.length < 5) examples.push({ sourceFile: row.sourceFile, sourceLine: row.sourceLine, rawOperationPath: row.operationPathExpr, normalizedOperationPath: normalized.normalizedOperationPath, candidateCount: candidates.length, candidates });
|
|
1227
1248
|
}
|
|
1228
|
-
return { severity: unresolvedMatchingIndexedOperation > 0 ? "warning" : "info", code: "strict_odata_invocation_resolution_quality", message: "OData invocation-path resolution quality aggregate",
|
|
1249
|
+
return { severity: unresolvedMatchingIndexedOperation + ambiguousNormalizedCalls > 0 ? "warning" : "info", code: "strict_odata_invocation_resolution_quality", message: "OData invocation-path resolution quality aggregate", totalInvocationRemoteActions: Number(aggregate.total ?? 0), resolvedInvocationCalls: Number(aggregate.resolved ?? 0), dynamicInvocationCalls: Number(aggregate.dynamic ?? 0), ambiguousInvocationCalls: Number(aggregate.ambiguous ?? 0), unresolvedInvocationCalls: Number(aggregate.unresolved ?? 0), ambiguousNormalizedCalls, unresolvedNormalizedCallsWithIndexedCandidates: unresolvedMatchingIndexedOperation, examples };
|
|
1229
1250
|
}
|
|
1230
1251
|
function createProgram() {
|
|
1231
1252
|
const program = new Command();
|