@saptools/service-flow 0.1.21 → 0.1.22
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 +8 -0
- package/README.md +3 -3
- package/TECHNICAL-NOTE.md +8 -0
- package/dist/{chunk-TSMDIKML.js → chunk-JIY37C44.js} +104 -10
- package/dist/chunk-JIY37C44.js.map +1 -0
- package/dist/cli.js +42 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-TSMDIKML.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-JIY37C44.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.22",
|
|
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: {
|
|
@@ -1180,7 +1180,15 @@ function parserQualityDiagnostics(db, strict) {
|
|
|
1180
1180
|
const remoteQuery = remoteQueryTargetQuality(db);
|
|
1181
1181
|
const invocation = odataInvocationResolutionQuality(db);
|
|
1182
1182
|
const remoteAction = remoteActionTargetQuality(db);
|
|
1183
|
+
const aliasQuality = identityAliasBindingQuality(db);
|
|
1184
|
+
const noBindingQuality = remoteActionNoBindingQuality(db);
|
|
1185
|
+
const contextualQuality = contextualImplementationQuality(db);
|
|
1186
|
+
const wrapperQuality = wrapperPathPropagationQuality(db);
|
|
1183
1187
|
return [
|
|
1188
|
+
aliasQuality,
|
|
1189
|
+
noBindingQuality,
|
|
1190
|
+
contextualQuality,
|
|
1191
|
+
wrapperQuality,
|
|
1184
1192
|
remoteQuery,
|
|
1185
1193
|
invocation,
|
|
1186
1194
|
remoteAction,
|
|
@@ -1194,6 +1202,38 @@ function parserQualityDiagnostics(db, strict) {
|
|
|
1194
1202
|
{ severity: outboundWithoutOwnershipRatio > outboundUnownedThreshold ? "warning" : "info", code: "strict_outbound_source_ownership_quality", message: "Outbound call source-symbol ownership aggregate", total: outboundTotal, withoutOwnership: outboundWithoutOwnership, withoutOwnershipRatio: outboundWithoutOwnershipRatio, withoutOwnershipRatioThreshold: outboundUnownedThreshold, ownerlessByType, ownerlessByCategory, ownerlessExamples }
|
|
1195
1203
|
];
|
|
1196
1204
|
}
|
|
1205
|
+
function identityAliasBindingQuality(db) {
|
|
1206
|
+
const examples = db.prepare(`SELECT c.source_file sourceFile,c.source_line sourceLine,c.service_binding_id serviceBindingId,json_extract(c.evidence_json,'$.receiver') receiverName,b.variable_name aliasSourceVariable,'same-file identifier alias still lacks a binding id' parserReason
|
|
1207
|
+
FROM outbound_calls c JOIN service_bindings b ON b.repo_id=c.repo_id AND b.source_file=c.source_file
|
|
1208
|
+
WHERE c.call_type='remote_action' AND c.service_binding_id IS NULL AND json_extract(c.evidence_json,'$.receiver') IS NOT NULL
|
|
1209
|
+
AND c.evidence_json LIKE '%' || '"aliasOf":"' || json_extract(c.evidence_json,'$.receiver') || '"' || '%'
|
|
1210
|
+
ORDER BY c.source_file,c.source_line LIMIT 5`).all();
|
|
1211
|
+
return { severity: examples.length > 0 ? "warning" : "info", code: "strict_identity_alias_binding_quality", message: "Remote sends that look like missed same-file identity aliases", missedAliasBindingCalls: examples.length, examples };
|
|
1212
|
+
}
|
|
1213
|
+
function remoteActionNoBindingQuality(db) {
|
|
1214
|
+
const rows = db.prepare(`SELECT COALESCE(json_extract(c.evidence_json,'$.receiver'),'unknown') receiverName,COALESCE(e.status,'missing_edge') status,CASE WHEN operation_path_expr LIKE '%(%' THEN 1 ELSE 0 END isODataInvocation,COUNT(*) count
|
|
1215
|
+
FROM outbound_calls c LEFT JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT)
|
|
1216
|
+
WHERE c.call_type='remote_action' AND c.operation_path_expr IS NOT NULL AND c.service_binding_id IS NULL
|
|
1217
|
+
GROUP BY receiverName,status,isODataInvocation ORDER BY count DESC,receiverName,status`).all();
|
|
1218
|
+
const examples = db.prepare(`SELECT c.source_file sourceFile,c.source_line sourceLine,json_extract(c.evidence_json,'$.receiver') receiverName,c.operation_path_expr operationPath,COALESCE(e.status,'missing_edge') status,
|
|
1219
|
+
CASE WHEN EXISTS (SELECT 1 FROM service_bindings b WHERE b.repo_id=c.repo_id AND b.source_file=c.source_file AND COALESCE(b.helper_chain_json,'') LIKE '%' || '"aliasOf":"' || json_extract(c.evidence_json,'$.receiver') || '"' || '%') THEN 'likely_missed_alias_or_helper_propagation' ELSE 'operation_path_only_call' END category
|
|
1220
|
+
FROM outbound_calls c LEFT JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT)
|
|
1221
|
+
WHERE c.call_type='remote_action' AND c.operation_path_expr IS NOT NULL AND c.service_binding_id IS NULL ORDER BY c.source_file,c.source_line LIMIT 5`).all();
|
|
1222
|
+
const total = rows.reduce((sum, row) => sum + Number(row.count ?? 0), 0);
|
|
1223
|
+
return { severity: total > 0 ? "warning" : "info", code: "strict_remote_action_no_binding_quality", message: "Remote actions with operation paths but no service binding id", total, breakdown: rows, examples };
|
|
1224
|
+
}
|
|
1225
|
+
function contextualImplementationQuality(db) {
|
|
1226
|
+
const rows = db.prepare(`SELECT COALESCE(json_extract(evidence_json,'$.contextualImplementation.status'),'not_applicable') status,COUNT(*) count
|
|
1227
|
+
FROM graph_edges WHERE edge_type='OPERATION_IMPLEMENTED_BY_HANDLER' AND status IN ('ambiguous','unresolved') GROUP BY status ORDER BY count DESC,status`).all();
|
|
1228
|
+
const total = rows.reduce((sum, row) => sum + Number(row.count ?? 0), 0);
|
|
1229
|
+
return { severity: total > 0 ? "warning" : "info", code: "strict_contextual_implementation_quality", message: "Runtime-resolved implementation hops stopped by ambiguous or unresolved implementation edges", total, breakdown: rows };
|
|
1230
|
+
}
|
|
1231
|
+
function wrapperPathPropagationQuality(db) {
|
|
1232
|
+
const examples = db.prepare(`SELECT source_file sourceFile,source_line sourceLine,json_extract(evidence_json,'$.receiver') receiverName,json_extract(evidence_json,'$.operationPathExpression') pathIdentifier,CASE WHEN json_extract(evidence_json,'$.literalCallerArgumentDetected') IS NOT NULL THEN 1 ELSE 0 END literalCallerArgumentDetected
|
|
1233
|
+
FROM outbound_calls WHERE call_type='remote_action' AND unresolved_reason='dynamic_operation_path_identifier' ORDER BY source_file,source_line LIMIT 5`).all();
|
|
1234
|
+
const aggregate = db.prepare("SELECT COUNT(*) count FROM outbound_calls WHERE call_type='remote_action' AND unresolved_reason='dynamic_operation_path_identifier'").get();
|
|
1235
|
+
return { severity: Number(aggregate.count ?? 0) > 0 ? "warning" : "info", code: "strict_wrapper_path_propagation_quality", message: "Dynamic path sends where send({ path }) used a path identifier", dynamicPathIdentifierCalls: Number(aggregate.count ?? 0), examples };
|
|
1236
|
+
}
|
|
1197
1237
|
function remoteQueryTargetQuality(db) {
|
|
1198
1238
|
const aggregate = db.prepare(`SELECT COUNT(*) total,
|
|
1199
1239
|
SUM(CASE WHEN e.edge_type='HANDLER_RUNS_REMOTE_QUERY' AND e.status='terminal' THEN 1 ELSE 0 END) terminal,
|