@saptools/service-flow 0.1.34 → 0.1.35

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
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ classifyODataPathIntent,
3
4
  containsSupportedOutboundCall,
4
5
  discoverRepositories,
5
6
  linkWorkspace,
@@ -12,7 +13,7 @@ import {
12
13
  parsePackageJson,
13
14
  parseServiceBindings,
14
15
  trace
15
- } from "./chunk-5SR4SFSU.js";
16
+ } from "./chunk-R47GA5VW.js";
16
17
 
17
18
  // src/cli.ts
18
19
  import { Command } from "commander";
@@ -193,7 +194,7 @@ function migrate(db) {
193
194
  // package.json
194
195
  var package_default = {
195
196
  name: "@saptools/service-flow",
196
- version: "0.1.34",
197
+ version: "0.1.35",
197
198
  description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
198
199
  type: "module",
199
200
  publishConfig: {
@@ -1306,6 +1307,32 @@ function remoteEntityOperationCollisionQuality(db) {
1306
1307
  }
1307
1308
  return { severity: examples.length > 0 ? "warning" : "info", code: "strict_remote_entity_operation_collision_quality", message: "Terminal remote entity edges that look like indexed operation invocations", collisionCount: examples.length, examples: examples.slice(0, 10) };
1308
1309
  }
1310
+ function remoteEntityDynamicOperationFalsePositiveQuality(db) {
1311
+ const rows = db.prepare(`SELECT c.id callId,c.source_file sourceFile,c.source_line sourceLine,c.method method,c.operation_path_expr rawPath,e.id graphEdgeId,e.status status,e.to_kind targetKind,e.unresolved_reason unresolvedReason,e.evidence_json evidenceJson
1312
+ FROM outbound_calls c JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT)
1313
+ WHERE c.call_type LIKE 'remote_entity_%' AND e.status IN ('dynamic','unresolved') AND e.to_kind='operation_candidate'
1314
+ ORDER BY c.source_file,c.source_line LIMIT 100`).all();
1315
+ const examples = [];
1316
+ for (const row of rows) {
1317
+ const rawPath = String(row.rawPath ?? "");
1318
+ const method = String(row.method ?? "GET");
1319
+ const intent = classifyODataPathIntent(rawPath, method);
1320
+ const entityIntent = ["entity_key_read", "entity_navigation_query", "entity_media"].includes(intent.kind) || intent.kind === "entity_mutation" && (intent.hasEntityKeyPredicate || intent.hasNavigationSuffix);
1321
+ if (!entityIntent) continue;
1322
+ let candidateCount;
1323
+ try {
1324
+ const evidence = JSON.parse(String(row.evidenceJson ?? "{}"));
1325
+ candidateCount = Number(evidence.indexedOperationCandidateCount ?? evidence.candidateCount ?? 0);
1326
+ } catch {
1327
+ candidateCount = 0;
1328
+ }
1329
+ const reason = String(row.unresolvedReason ?? "");
1330
+ const keyEvidence = intent.keyPredicatePlaceholderKeys.length > 0 || reason.includes("runtime variable") || reason.includes("placeholder");
1331
+ if (candidateCount > 0 || !keyEvidence) continue;
1332
+ examples.push({ sourceFile: row.sourceFile, sourceLine: row.sourceLine, rawPath, method, pathIntent: intent.kind, keyPlaceholderKeys: intent.keyPredicatePlaceholderKeys, navigationOrMediaSuffix: intent.navigationSuffix ?? intent.mediaOrPropertySuffix, operationCandidateCount: candidateCount, graphEdgeId: row.graphEdgeId, recommendedRemediation: "Reindex and relink with service-flow 0.1.35 or newer so entity key placeholders remain entity-addressing evidence." });
1333
+ }
1334
+ return { severity: examples.length > 0 ? "warning" : "info", code: "strict_remote_entity_dynamic_operation_false_positive_quality", message: "Parser-classified entity paths linked as dynamic operation candidates without indexed operation evidence", falsePositiveCount: examples.length, examples: examples.slice(0, 10) };
1335
+ }
1309
1336
  function localServiceDiagnostics(db, strict) {
1310
1337
  const rows = db.prepare(`SELECT e.status status,e.unresolved_reason reason,e.evidence_json evidenceJson FROM graph_edges e JOIN outbound_calls c ON e.from_kind='call' AND c.id=CAST(e.from_id AS INTEGER) WHERE c.call_type='local_service_call'`).all();
1311
1338
  const implementationContext = rows.filter((row) => row.status === "resolved" && String(row.evidenceJson ?? "").includes("implementation_context_caller_ownership")).length;
@@ -1375,6 +1402,7 @@ function parserQualityDiagnostics(db, strict) {
1375
1402
  const invocation = odataInvocationResolutionQuality(db);
1376
1403
  const remoteAction = remoteActionTargetQuality(db);
1377
1404
  const entityOperationCollision = remoteEntityOperationCollisionQuality(db);
1405
+ const entityDynamicFalsePositive = remoteEntityDynamicOperationFalsePositiveQuality(db);
1378
1406
  const externalHttp = externalHttpTargetQuality(db);
1379
1407
  const aliasQuality = identityAliasBindingQuality(db);
1380
1408
  const noBindingQuality = remoteActionNoBindingQuality(db);
@@ -1393,6 +1421,7 @@ function parserQualityDiagnostics(db, strict) {
1393
1421
  nestedThisQuality,
1394
1422
  remoteQuery,
1395
1423
  entityOperationCollision,
1424
+ entityDynamicFalsePositive,
1396
1425
  invocation,
1397
1426
  remoteAction,
1398
1427
  externalHttp,