@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/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  parsePackageJson,
13
13
  parseServiceBindings,
14
14
  trace
15
- } from "./chunk-YG66SHAR.js";
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.19",
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='unresolved' AND c.operation_path_expr LIKE '%(%'
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 ambiguousInvocationPaths = 0;
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
- unresolvedMatchingIndexedOperation += 1;
1225
- if (candidates.length > 1) ambiguousInvocationPaths += 1;
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", unresolvedRemoteActionsWithIndexedNormalizedOperation: unresolvedMatchingIndexedOperation, ambiguousInvocationPaths, examples };
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();