@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/CHANGELOG.md +7 -0
- package/TECHNICAL-NOTE.md +5 -0
- package/dist/{chunk-5SR4SFSU.js → chunk-R47GA5VW.js} +40 -9
- package/dist/chunk-R47GA5VW.js.map +1 -0
- package/dist/cli.js +31 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-5SR4SFSU.js.map +0 -1
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-
|
|
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.
|
|
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,
|