@saptools/service-flow 0.1.52 → 0.1.54
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 +12 -0
- package/README.md +11 -12
- package/TECHNICAL-NOTE.md +18 -3
- package/dist/{chunk-PTLDSHRC.js → chunk-ERIZHM5C.js} +2646 -1067
- package/dist/chunk-ERIZHM5C.js.map +1 -0
- package/dist/cli.js +162 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/001-doctor-projection.ts +136 -0
- package/src/cli/doctor.ts +20 -3
- package/src/db/repositories.ts +10 -2
- package/src/linker/000-implementation-candidates.ts +674 -0
- package/src/linker/001-implementation-evidence-projection.ts +191 -0
- package/src/linker/002-call-evidence.ts +226 -0
- package/src/linker/cross-repo-linker.ts +27 -453
- package/src/linker/dynamic-edge-resolver.ts +35 -0
- package/src/linker/external-http-target.ts +24 -3
- package/src/linker/helper-package-linker.ts +18 -2
- package/src/linker/service-resolver.ts +45 -2
- package/src/output/doctor-output.ts +13 -4
- package/src/output/table-output.ts +15 -4
- package/src/trace/000-dynamic-target-types.ts +12 -0
- package/src/trace/001-dynamic-identity.ts +45 -17
- package/src/trace/003-dynamic-references.ts +236 -35
- package/src/trace/004-dynamic-candidate-sources.ts +155 -0
- package/src/trace/005-implementation-selection.ts +187 -0
- package/src/trace/006-contextual-projection.ts +30 -0
- package/src/trace/007-implementation-start-diagnostic.ts +61 -0
- package/src/trace/008-contextual-runtime-state.ts +294 -0
- package/src/trace/009-selected-handler-provenance.ts +186 -0
- package/src/trace/dynamic-targets.ts +205 -159
- package/src/trace/evidence.ts +132 -34
- package/src/trace/implementation-hints.ts +148 -8
- package/src/trace/selectors.ts +74 -9
- package/src/trace/trace-engine.ts +97 -125
- package/src/utils/000-bounded-projection.ts +166 -0
- package/dist/chunk-PTLDSHRC.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -29,12 +29,13 @@ import {
|
|
|
29
29
|
parsePackageJson,
|
|
30
30
|
parseServiceBindings,
|
|
31
31
|
parseVars,
|
|
32
|
+
projectBoundedInOrder,
|
|
32
33
|
reposByName,
|
|
33
34
|
selectorRepoAmbiguousDiagnostic,
|
|
34
35
|
trace,
|
|
35
36
|
upsertRepository,
|
|
36
37
|
upsertWorkspace
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-ERIZHM5C.js";
|
|
38
39
|
|
|
39
40
|
// src/cli.ts
|
|
40
41
|
import { Command } from "commander";
|
|
@@ -236,7 +237,7 @@ function migrate(db) {
|
|
|
236
237
|
// package.json
|
|
237
238
|
var package_default = {
|
|
238
239
|
name: "@saptools/service-flow",
|
|
239
|
-
version: "0.1.
|
|
240
|
+
version: "0.1.54",
|
|
240
241
|
description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
|
|
241
242
|
type: "module",
|
|
242
243
|
publishConfig: {
|
|
@@ -1130,13 +1131,128 @@ async function indexWorkspace(db, workspaceId, options) {
|
|
|
1130
1131
|
}
|
|
1131
1132
|
}
|
|
1132
1133
|
|
|
1134
|
+
// src/cli/001-doctor-projection.ts
|
|
1135
|
+
var boundedDoctorArrayKeys = /* @__PURE__ */ new Set([
|
|
1136
|
+
"candidates",
|
|
1137
|
+
"candidateScores",
|
|
1138
|
+
"candidateFamilies",
|
|
1139
|
+
"candidateEvidence",
|
|
1140
|
+
"candidatePaths",
|
|
1141
|
+
"candidateRawPaths",
|
|
1142
|
+
"candidateNormalizedOperationPaths",
|
|
1143
|
+
"normalizedCandidateOperations",
|
|
1144
|
+
"candidateLiterals",
|
|
1145
|
+
"bindingCandidates",
|
|
1146
|
+
"bindingAlternatives",
|
|
1147
|
+
"registrations",
|
|
1148
|
+
"implementationHintSuggestions",
|
|
1149
|
+
"selectableImplementationRepositories",
|
|
1150
|
+
"matchedHints",
|
|
1151
|
+
"candidateSuggestions",
|
|
1152
|
+
"dynamicTargetCandidates",
|
|
1153
|
+
"dynamicTargetCandidateSuggestions",
|
|
1154
|
+
"rejectedCandidates",
|
|
1155
|
+
"suggestedVarSets",
|
|
1156
|
+
"copyableExamples",
|
|
1157
|
+
"examples",
|
|
1158
|
+
"expandedExamples",
|
|
1159
|
+
"selectorSuggestions",
|
|
1160
|
+
"serviceSuggestions",
|
|
1161
|
+
"repositories"
|
|
1162
|
+
]);
|
|
1163
|
+
function boundDoctorDiagnostics(diagnostics) {
|
|
1164
|
+
return diagnostics.map(boundDoctorDiagnostic);
|
|
1165
|
+
}
|
|
1166
|
+
function boundDoctorDiagnostic(diagnostic) {
|
|
1167
|
+
const bounded = boundDoctorValue(diagnostic);
|
|
1168
|
+
return isDiagnostic(bounded) ? bounded : {};
|
|
1169
|
+
}
|
|
1170
|
+
function boundDoctorValue(value) {
|
|
1171
|
+
if (Array.isArray(value)) return value.map(boundDoctorValue);
|
|
1172
|
+
if (!isDiagnostic(value)) return value;
|
|
1173
|
+
const input = value;
|
|
1174
|
+
const output = {};
|
|
1175
|
+
for (const [key, child] of Object.entries(input)) {
|
|
1176
|
+
if (!Array.isArray(child) || !boundedDoctorArrayKeys.has(key)) {
|
|
1177
|
+
output[key] = boundDoctorValue(child);
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
const projection = projectBoundedInOrder(child.map(boundDoctorValue));
|
|
1181
|
+
output[key] = projection.items;
|
|
1182
|
+
addProjectionMetadata(output, input, key, projection);
|
|
1183
|
+
}
|
|
1184
|
+
return output;
|
|
1185
|
+
}
|
|
1186
|
+
function isDiagnostic(value) {
|
|
1187
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1188
|
+
}
|
|
1189
|
+
function addProjectionMetadata(output, input, key, projection) {
|
|
1190
|
+
const names = projectionNames(key);
|
|
1191
|
+
const total = Math.max(
|
|
1192
|
+
numericValue(input[names.total]),
|
|
1193
|
+
projection.totalCount,
|
|
1194
|
+
siblingCollectionCount(input, key)
|
|
1195
|
+
);
|
|
1196
|
+
output[names.total] = total;
|
|
1197
|
+
output[names.shown] = projection.shownCount;
|
|
1198
|
+
output[names.omitted] = Math.max(0, total - projection.shownCount);
|
|
1199
|
+
}
|
|
1200
|
+
function siblingCollectionCount(input, key) {
|
|
1201
|
+
if (key !== "examples" || !Array.isArray(input.expandedExamples)) return 0;
|
|
1202
|
+
return input.expandedExamples.length;
|
|
1203
|
+
}
|
|
1204
|
+
function projectionNames(key) {
|
|
1205
|
+
const stem = projectionStem(key);
|
|
1206
|
+
return {
|
|
1207
|
+
total: `${stem}Count`,
|
|
1208
|
+
shown: `shown${upperFirst(stem)}Count`,
|
|
1209
|
+
omitted: `omitted${upperFirst(stem)}Count`
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
function projectionStem(key) {
|
|
1213
|
+
const stems = {
|
|
1214
|
+
candidates: "candidate",
|
|
1215
|
+
candidateScores: "candidateScore",
|
|
1216
|
+
candidateFamilies: "candidateFamily",
|
|
1217
|
+
candidateEvidence: "candidateEvidence",
|
|
1218
|
+
candidatePaths: "candidatePath",
|
|
1219
|
+
candidateRawPaths: "candidateRawPath",
|
|
1220
|
+
candidateNormalizedOperationPaths: "candidateNormalizedOperationPath",
|
|
1221
|
+
normalizedCandidateOperations: "normalizedCandidateOperation",
|
|
1222
|
+
candidateLiterals: "candidateLiteral",
|
|
1223
|
+
bindingCandidates: "bindingCandidate",
|
|
1224
|
+
bindingAlternatives: "bindingAlternative",
|
|
1225
|
+
registrations: "registration",
|
|
1226
|
+
implementationHintSuggestions: "implementationHintSuggestion",
|
|
1227
|
+
selectableImplementationRepositories: "selectableImplementationRepository",
|
|
1228
|
+
matchedHints: "matchedHint",
|
|
1229
|
+
candidateSuggestions: "candidateSuggestion",
|
|
1230
|
+
dynamicTargetCandidates: "dynamicTargetCandidate",
|
|
1231
|
+
dynamicTargetCandidateSuggestions: "dynamicTargetCandidateSuggestion",
|
|
1232
|
+
rejectedCandidates: "rejectedCandidate",
|
|
1233
|
+
suggestedVarSets: "suggestedVarSet",
|
|
1234
|
+
copyableExamples: "copyableExample",
|
|
1235
|
+
examples: "example",
|
|
1236
|
+
expandedExamples: "expandedExample",
|
|
1237
|
+
selectorSuggestions: "selectorSuggestion",
|
|
1238
|
+
serviceSuggestions: "serviceSuggestion"
|
|
1239
|
+
};
|
|
1240
|
+
return stems[key] ?? "repository";
|
|
1241
|
+
}
|
|
1242
|
+
function numericValue(value) {
|
|
1243
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1244
|
+
}
|
|
1245
|
+
function upperFirst(value) {
|
|
1246
|
+
return value ? `${value[0]?.toUpperCase() ?? ""}${value.slice(1)}` : value;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1133
1249
|
// src/cli/doctor.ts
|
|
1134
1250
|
function linkUpgradeWarnings(db) {
|
|
1135
1251
|
return [...schemaDriftDiagnostics(db, true), ...analyzerVersionDiagnostics(db, true)].filter((item) => ["schema_legacy_columns_present", "external_target_columns_missing_data", "reindex_required_after_upgrade", "reindex_required_after_analyzer_upgrade"].includes(String(item.code)));
|
|
1136
1252
|
}
|
|
1137
1253
|
function doctorDiagnostics(db, strict, options = {}) {
|
|
1138
1254
|
const diagnostics = db.prepare("SELECT severity,code,message,source_file sourceFile,source_line sourceLine FROM diagnostics ORDER BY id").all();
|
|
1139
|
-
return [
|
|
1255
|
+
return boundDoctorDiagnostics([
|
|
1140
1256
|
...diagnostics,
|
|
1141
1257
|
...healthDiagnostics(db, strict),
|
|
1142
1258
|
...remoteTargetWithoutImplementationDiagnostics(db, strict, Boolean(options.detail)),
|
|
@@ -1144,7 +1260,7 @@ function doctorDiagnostics(db, strict, options = {}) {
|
|
|
1144
1260
|
...schemaDriftDiagnostics(db, strict),
|
|
1145
1261
|
...analyzerVersionDiagnostics(db, strict),
|
|
1146
1262
|
...parserQualityDiagnostics(db, strict, options)
|
|
1147
|
-
];
|
|
1263
|
+
]);
|
|
1148
1264
|
}
|
|
1149
1265
|
function healthDiagnostics(db, strict) {
|
|
1150
1266
|
return db.prepare(
|
|
@@ -1356,6 +1472,10 @@ function addImplementationCategory(grouped, row) {
|
|
|
1356
1472
|
const current = grouped.get(key) ?? { category, baseOperation, reason, candidateFamily: family, count: 0, servicePaths: [], examples: [] };
|
|
1357
1473
|
const hintSuggestions = implementationSuggestions(evidence);
|
|
1358
1474
|
const candidates = asRecords(evidence.candidates);
|
|
1475
|
+
const candidateCount2 = Math.max(
|
|
1476
|
+
numericValue2(evidence.candidateCount),
|
|
1477
|
+
candidates.length
|
|
1478
|
+
);
|
|
1359
1479
|
current.count += 1;
|
|
1360
1480
|
current.servicePaths.push(String(row.servicePath ?? evidence.servicePath ?? ""));
|
|
1361
1481
|
current.examples.push({
|
|
@@ -1363,7 +1483,15 @@ function addImplementationCategory(grouped, row) {
|
|
|
1363
1483
|
operation: row.operationName,
|
|
1364
1484
|
status: row.status,
|
|
1365
1485
|
reason: row.unresolvedReason,
|
|
1366
|
-
candidateCount:
|
|
1486
|
+
candidateCount: candidateCount2,
|
|
1487
|
+
shownCandidateCount: Math.min(
|
|
1488
|
+
candidateCount2,
|
|
1489
|
+
numericValue2(evidence.shownCandidateCount) || candidates.length
|
|
1490
|
+
),
|
|
1491
|
+
omittedCandidateCount: Math.max(
|
|
1492
|
+
0,
|
|
1493
|
+
candidateCount2 - (numericValue2(evidence.shownCandidateCount) || candidates.length)
|
|
1494
|
+
),
|
|
1367
1495
|
candidateEvidence: candidates.slice(0, 3),
|
|
1368
1496
|
implementationHintSuggestions: hintSuggestions
|
|
1369
1497
|
});
|
|
@@ -1701,6 +1829,9 @@ function remoteActionNoBindingQuality(db) {
|
|
|
1701
1829
|
function candidateCount(value) {
|
|
1702
1830
|
return Number(parseObject(value).candidateCount ?? 0);
|
|
1703
1831
|
}
|
|
1832
|
+
function numericValue2(value) {
|
|
1833
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1834
|
+
}
|
|
1704
1835
|
function parseObject(value) {
|
|
1705
1836
|
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
1706
1837
|
try {
|
|
@@ -1716,13 +1847,20 @@ function asRecords(value) {
|
|
|
1716
1847
|
|
|
1717
1848
|
// src/output/table-output.ts
|
|
1718
1849
|
function location(evidence) {
|
|
1850
|
+
const selected = isRecord(evidence.selectedHandler) ? evidence.selectedHandler : void 0;
|
|
1851
|
+
const selectedFile = selected?.sourceFile;
|
|
1852
|
+
const selectedLine = selected?.sourceLine;
|
|
1853
|
+
if (selectedFile || selectedLine)
|
|
1854
|
+
return `${String(selectedFile ?? "")}:${String(selectedLine ?? "")}`;
|
|
1719
1855
|
const file = evidence.file ?? evidence.sourceFile ?? evidence.handlerSourceFile ?? evidence.operationSourceFile ?? evidence.registrationSourceFile;
|
|
1720
1856
|
const line = evidence.line ?? evidence.sourceLine ?? evidence.handlerSourceLine ?? evidence.operationSourceLine ?? evidence.registrationSourceLine;
|
|
1721
1857
|
if (file || line) return `${String(file ?? "")}:${String(line ?? "")}`;
|
|
1722
1858
|
const candidates = evidence.candidates;
|
|
1859
|
+
if (Array.isArray(candidates) && candidates.some((candidate) => isRecord(candidate) && candidate.methodId !== void 0)) return ":";
|
|
1723
1860
|
if (Array.isArray(candidates) && candidates.length > 0) {
|
|
1724
|
-
const first = candidates
|
|
1725
|
-
|
|
1861
|
+
const first = candidates.find(isRecord);
|
|
1862
|
+
if (first)
|
|
1863
|
+
return `${String(first.sourceFile ?? "")}:${String(first.sourceLine ?? "")}`;
|
|
1726
1864
|
}
|
|
1727
1865
|
return ":";
|
|
1728
1866
|
}
|
|
@@ -1766,8 +1904,10 @@ function hintLines(evidence) {
|
|
|
1766
1904
|
const hints = suggestions.flatMap((item) => isRecord(item) && typeof item.cli === "string" ? [item.cli] : []);
|
|
1767
1905
|
const unique = [...new Set(hints)];
|
|
1768
1906
|
const shown = unique.slice(0, 3).map((hint) => `try ${hint}`);
|
|
1769
|
-
|
|
1770
|
-
|
|
1907
|
+
const omitted = numberValue(evidence.omittedImplementationHintSuggestionCount);
|
|
1908
|
+
const remaining = Math.max(0, unique.length - shown.length) + omitted;
|
|
1909
|
+
if (remaining > 0)
|
|
1910
|
+
shown.push(`... ${remaining} additional hint(s) omitted; use a scoped --implementation-hint`);
|
|
1771
1911
|
return [...dynamicLines, ...shown];
|
|
1772
1912
|
}
|
|
1773
1913
|
function dynamicHintLines(evidence) {
|
|
@@ -1857,8 +1997,12 @@ function compactMessage(diagnostic) {
|
|
|
1857
1997
|
}
|
|
1858
1998
|
function suggestedHintLines(diagnostic) {
|
|
1859
1999
|
const direct = cliHints(diagnostic.suggestedHints);
|
|
1860
|
-
|
|
1861
|
-
return cappedHints(
|
|
2000
|
+
const omitted = numericValue3(diagnostic.omittedImplementationHintSuggestionCount);
|
|
2001
|
+
if (direct.length > 0) return cappedHints(direct, omitted);
|
|
2002
|
+
return cappedHints(
|
|
2003
|
+
cliHintsFromSuggestions(diagnostic.implementationHintSuggestions),
|
|
2004
|
+
omitted
|
|
2005
|
+
);
|
|
1862
2006
|
}
|
|
1863
2007
|
function cliHints(value) {
|
|
1864
2008
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -1873,12 +2017,17 @@ function cliHintsFromSuggestions(value) {
|
|
|
1873
2017
|
function isRecord2(value) {
|
|
1874
2018
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1875
2019
|
}
|
|
1876
|
-
function cappedHints(hints) {
|
|
2020
|
+
function cappedHints(hints, omitted) {
|
|
1877
2021
|
const unique = [...new Set(hints)];
|
|
1878
2022
|
const shown = unique.slice(0, 3);
|
|
1879
|
-
|
|
2023
|
+
const remaining = Math.max(0, unique.length - shown.length) + omitted;
|
|
2024
|
+
if (remaining > 0)
|
|
2025
|
+
shown.push(`... ${remaining} additional hint(s) omitted; use a scoped --implementation-hint`);
|
|
1880
2026
|
return shown;
|
|
1881
2027
|
}
|
|
2028
|
+
function numericValue3(value) {
|
|
2029
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
2030
|
+
}
|
|
1882
2031
|
function cleanDoctorMessage() {
|
|
1883
2032
|
return `${pc.green("No diagnostics recorded")}
|
|
1884
2033
|
`;
|