@saptools/service-flow 0.1.50 → 0.1.51
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/README.md +9 -1
- package/TECHNICAL-NOTE.md +6 -1
- package/dist/{chunk-52OUS3MO.js → chunk-YZJKE5UX.js} +470 -29
- package/dist/chunk-YZJKE5UX.js.map +1 -0
- package/dist/cli.js +42 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +14 -10
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli.ts +22 -0
- package/src/index.ts +1 -1
- package/src/linker/cross-repo-linker.ts +9 -1
- package/src/linker/service-resolver.ts +15 -4
- package/src/output/table-output.ts +29 -2
- package/src/trace/dynamic-branches.ts +45 -0
- package/src/trace/dynamic-targets.ts +424 -0
- package/src/trace/evidence.ts +117 -7
- package/src/trace/trace-engine.ts +11 -11
- package/src/types.ts +12 -0
- package/dist/chunk-52OUS3MO.js.map +0 -1
|
@@ -2842,8 +2842,8 @@ function buildRemoteQueryTarget(input) {
|
|
|
2842
2842
|
// src/linker/service-resolver.ts
|
|
2843
2843
|
function rows(db, operationPath, workspaceId) {
|
|
2844
2844
|
const names = operationLookupNames(operationPath);
|
|
2845
|
-
|
|
2846
|
-
`SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score
|
|
2845
|
+
const result = db.prepare(
|
|
2846
|
+
`SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score
|
|
2847
2847
|
FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id
|
|
2848
2848
|
WHERE (? IS NULL OR r.workspace_id=?) AND (o.operation_path IN (?,?) OR o.operation_name IN (?,?)) ORDER BY r.name,s.service_path,o.operation_name`
|
|
2849
2849
|
).all(
|
|
@@ -2854,6 +2854,11 @@ function rows(db, operationPath, workspaceId) {
|
|
|
2854
2854
|
names.name,
|
|
2855
2855
|
names.simpleName
|
|
2856
2856
|
);
|
|
2857
|
+
return result.map((row) => ({
|
|
2858
|
+
...row,
|
|
2859
|
+
score: Number(row.score ?? 0),
|
|
2860
|
+
reasons: []
|
|
2861
|
+
}));
|
|
2857
2862
|
}
|
|
2858
2863
|
function operationLookupNames(operationPath) {
|
|
2859
2864
|
const name = operationPath.replace(/^\//, "");
|
|
@@ -2870,7 +2875,11 @@ function resolveOperation(db, signals, workspaceId) {
|
|
|
2870
2875
|
if (missing.length > 0)
|
|
2871
2876
|
return {
|
|
2872
2877
|
status: "dynamic",
|
|
2873
|
-
candidates: signals.operationPath ? rows(db, signals.operationPath, workspaceId)
|
|
2878
|
+
candidates: signals.operationPath ? rows(db, signals.operationPath, workspaceId).map((candidate) => ({
|
|
2879
|
+
...candidate,
|
|
2880
|
+
score: 0.2,
|
|
2881
|
+
reasons: ["operation_path_match"]
|
|
2882
|
+
})) : [],
|
|
2874
2883
|
reasons: [...new Set(missing)].map((name) => `missing_variable:${name}`)
|
|
2875
2884
|
};
|
|
2876
2885
|
if (!signals.operationPath)
|
|
@@ -3233,7 +3242,13 @@ function compactCandidateScores(candidates) {
|
|
|
3233
3242
|
return candidates.flatMap((candidate) => {
|
|
3234
3243
|
const row = objectValue(candidate);
|
|
3235
3244
|
if (!row) return [];
|
|
3236
|
-
return [{
|
|
3245
|
+
return [{
|
|
3246
|
+
repo: row.repoName,
|
|
3247
|
+
servicePath: row.servicePath,
|
|
3248
|
+
operationPath: row.operationPath,
|
|
3249
|
+
score: row.score,
|
|
3250
|
+
reasons: Array.isArray(row.reasons) ? row.reasons.filter((reason) => typeof reason === "string") : ["operation_path_match"]
|
|
3251
|
+
}];
|
|
3237
3252
|
});
|
|
3238
3253
|
}
|
|
3239
3254
|
function placeholderKeys(values) {
|
|
@@ -3642,6 +3657,303 @@ function fullSelectorSuggestions(candidates) {
|
|
|
3642
3657
|
}))].sort();
|
|
3643
3658
|
}
|
|
3644
3659
|
|
|
3660
|
+
// src/trace/dynamic-targets.ts
|
|
3661
|
+
function analyzeDynamicTargetCandidates(db, evidence, workspaceId, mode, maxCandidates) {
|
|
3662
|
+
const templates = templatesFromEvidence(evidence);
|
|
3663
|
+
const missingVariables = allMissingVariables(evidence, templates);
|
|
3664
|
+
if (missingVariables.length === 0) return void 0;
|
|
3665
|
+
const order = variableOrder(templates, missingVariables);
|
|
3666
|
+
const candidates = rankedCandidates(
|
|
3667
|
+
db,
|
|
3668
|
+
candidateTargets(db, evidence, workspaceId),
|
|
3669
|
+
referenceRows(db, workspaceId),
|
|
3670
|
+
templates,
|
|
3671
|
+
order
|
|
3672
|
+
);
|
|
3673
|
+
const inference = inferenceDecision(candidates);
|
|
3674
|
+
const shownCandidates = candidates.slice(0, maxCandidates);
|
|
3675
|
+
return {
|
|
3676
|
+
mode,
|
|
3677
|
+
candidateCount: candidates.length,
|
|
3678
|
+
shownCandidateCount: shownCandidates.length,
|
|
3679
|
+
omittedCandidateCount: Math.max(0, candidates.length - shownCandidates.length),
|
|
3680
|
+
missingVariables,
|
|
3681
|
+
candidates,
|
|
3682
|
+
shownCandidates,
|
|
3683
|
+
suggestedVarSets: suggestedVarSets(candidates, missingVariables, order),
|
|
3684
|
+
inference
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
function candidateTargets(db, evidence, workspaceId) {
|
|
3688
|
+
const embedded = rowsFromEvidence(evidence.candidates);
|
|
3689
|
+
if (embedded.length > 0) return embedded;
|
|
3690
|
+
const operationPath = stringValue4(evidence.operationPath);
|
|
3691
|
+
if (!operationPath || extractPlaceholders(operationPath).length > 0) return [];
|
|
3692
|
+
return queryOperationTargets(db, operationPath, workspaceId);
|
|
3693
|
+
}
|
|
3694
|
+
function rowsFromEvidence(value) {
|
|
3695
|
+
if (!Array.isArray(value)) return [];
|
|
3696
|
+
return value.flatMap((item) => {
|
|
3697
|
+
const row = record(item);
|
|
3698
|
+
const operationId = numberValue(row.operationId);
|
|
3699
|
+
const repoName = stringValue4(row.repoName);
|
|
3700
|
+
const servicePath = stringValue4(row.servicePath);
|
|
3701
|
+
const operationPath = stringValue4(row.operationPath);
|
|
3702
|
+
const operationName = stringValue4(row.operationName) ?? operationPath?.replace(/^\//, "");
|
|
3703
|
+
if (operationId === void 0 || !repoName || !servicePath || !operationPath || !operationName) return [];
|
|
3704
|
+
return [{
|
|
3705
|
+
operationId,
|
|
3706
|
+
repoName,
|
|
3707
|
+
serviceName: stringValue4(row.serviceName) ?? "",
|
|
3708
|
+
qualifiedName: stringValue4(row.qualifiedName) ?? "",
|
|
3709
|
+
servicePath,
|
|
3710
|
+
operationPath,
|
|
3711
|
+
operationName,
|
|
3712
|
+
sourceFile: stringValue4(row.sourceFile) ?? "",
|
|
3713
|
+
sourceLine: numberValue(row.sourceLine) ?? 0,
|
|
3714
|
+
repoId: numberValue(row.repoId),
|
|
3715
|
+
packageName: stringValue4(row.packageName),
|
|
3716
|
+
score: numberValue(row.score) ?? 0,
|
|
3717
|
+
reasons: stringArray(row.reasons)
|
|
3718
|
+
}];
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
function queryOperationTargets(db, operationPath, workspaceId) {
|
|
3722
|
+
const simple = operationPath.replace(/^\//, "").split(".").at(-1) ?? operationPath;
|
|
3723
|
+
const rows2 = db.prepare(
|
|
3724
|
+
`SELECT o.id operationId,r.id repoId,r.name repoName,r.package_name packageName,
|
|
3725
|
+
s.service_name serviceName,s.qualified_name qualifiedName,s.service_path servicePath,
|
|
3726
|
+
o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,
|
|
3727
|
+
o.source_line sourceLine
|
|
3728
|
+
FROM cds_operations o JOIN cds_services s ON s.id=o.service_id
|
|
3729
|
+
JOIN repositories r ON r.id=s.repo_id
|
|
3730
|
+
WHERE (? IS NULL OR r.workspace_id=?)
|
|
3731
|
+
AND (o.operation_path IN (?,?) OR o.operation_name=?)
|
|
3732
|
+
ORDER BY r.name,s.service_path,o.operation_name`
|
|
3733
|
+
).all(workspaceId, workspaceId, operationPath, `/${simple}`, simple);
|
|
3734
|
+
return rows2.map((row) => ({
|
|
3735
|
+
operationId: Number(row.operationId),
|
|
3736
|
+
repoId: numberValue(row.repoId),
|
|
3737
|
+
repoName: String(row.repoName),
|
|
3738
|
+
packageName: stringValue4(row.packageName),
|
|
3739
|
+
serviceName: String(row.serviceName),
|
|
3740
|
+
qualifiedName: String(row.qualifiedName),
|
|
3741
|
+
servicePath: String(row.servicePath),
|
|
3742
|
+
operationPath: String(row.operationPath),
|
|
3743
|
+
operationName: String(row.operationName),
|
|
3744
|
+
sourceFile: String(row.sourceFile),
|
|
3745
|
+
sourceLine: Number(row.sourceLine),
|
|
3746
|
+
score: 0.2,
|
|
3747
|
+
reasons: ["operation_path_match"]
|
|
3748
|
+
}));
|
|
3749
|
+
}
|
|
3750
|
+
function rankedCandidates(db, candidates, references, templates, order) {
|
|
3751
|
+
const ranked = candidates.map((candidate) => candidateEvidence2(db, candidate, references, templates, order));
|
|
3752
|
+
return ranked.sort((a, b) => b.score - a.score || a.repoName.localeCompare(b.repoName) || a.servicePath.localeCompare(b.servicePath));
|
|
3753
|
+
}
|
|
3754
|
+
function candidateEvidence2(db, candidate, references, templates, order) {
|
|
3755
|
+
const state = emptyCandidate(candidate);
|
|
3756
|
+
applyDirectTemplate(state, templates.operationPath, candidate.operationPath, "operation_path");
|
|
3757
|
+
applyDirectTemplate(state, templates.servicePath, candidate.servicePath, "service_path");
|
|
3758
|
+
const refs = references.filter((item) => item.servicePath === candidate.servicePath);
|
|
3759
|
+
applyReferenceTemplate(state, templates.alias, refs, "alias");
|
|
3760
|
+
applyReferenceTemplate(state, templates.destination, refs, "destination");
|
|
3761
|
+
if (hasResolvedImplementation(db, candidate.operationId)) addScore(state, 0.1, "implementation_edge_resolved");
|
|
3762
|
+
state.missingVariables = order.filter((key) => state.derivedVariables[key] === void 0);
|
|
3763
|
+
if (state.missingVariables.length === 0) addScore(state, 0.15, "all_runtime_variables_derived");
|
|
3764
|
+
else state.rejectedReasons.push("missing_required_runtime_variable");
|
|
3765
|
+
state.score = Math.max(0, Math.min(1, state.score));
|
|
3766
|
+
state.cli = state.missingVariables.length === 0 ? cliFor(state.derivedVariables, order) : void 0;
|
|
3767
|
+
return state;
|
|
3768
|
+
}
|
|
3769
|
+
function emptyCandidate(candidate) {
|
|
3770
|
+
return {
|
|
3771
|
+
candidateOperationId: candidate.operationId,
|
|
3772
|
+
repoName: candidate.repoName,
|
|
3773
|
+
packageName: candidate.packageName ?? void 0,
|
|
3774
|
+
servicePath: candidate.servicePath,
|
|
3775
|
+
operationPath: candidate.operationPath,
|
|
3776
|
+
operationName: candidate.operationName,
|
|
3777
|
+
derivedVariables: {},
|
|
3778
|
+
derivedVariableSources: {},
|
|
3779
|
+
missingVariables: [],
|
|
3780
|
+
score: Math.max(0.2, Number(candidate.score ?? 0)),
|
|
3781
|
+
reasons: nonEmptyStrings(candidate.reasons, ["operation_path_match"]),
|
|
3782
|
+
rejectedReasons: []
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
function applyDirectTemplate(state, template, concrete, kind) {
|
|
3786
|
+
const matched = matchTemplate(template, concrete);
|
|
3787
|
+
if (!template) return;
|
|
3788
|
+
if (!matched) {
|
|
3789
|
+
state.rejectedReasons.push(`${kind}_template_mismatch`);
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
mergeDerived(state, matched, `${kind}_template`);
|
|
3793
|
+
addScore(state, kind === "service_path" ? 0.35 : 0.25, `${kind}_template_match`);
|
|
3794
|
+
}
|
|
3795
|
+
function applyReferenceTemplate(state, template, refs, kind) {
|
|
3796
|
+
if (!template || extractPlaceholders(template).length === 0) return;
|
|
3797
|
+
for (const ref of refs) {
|
|
3798
|
+
const concrete = kind === "alias" ? ref.alias : ref.destination;
|
|
3799
|
+
const matched = isConcrete(concrete) ? matchTemplate(template, concrete) : void 0;
|
|
3800
|
+
if (!matched) continue;
|
|
3801
|
+
mergeDerived(state, matched, `${ref.sourceKind}.${kind}`);
|
|
3802
|
+
addScore(state, 0.2, `${kind}_template_match`);
|
|
3803
|
+
return;
|
|
3804
|
+
}
|
|
3805
|
+
if (refs.some((ref) => isConcrete(kind === "alias" ? ref.alias : ref.destination)))
|
|
3806
|
+
state.rejectedReasons.push(`${kind}_template_mismatch`);
|
|
3807
|
+
}
|
|
3808
|
+
function inferenceDecision(candidates) {
|
|
3809
|
+
const complete = candidates.filter((candidate) => candidate.missingVariables.length === 0 && candidate.rejectedReasons.length === 0);
|
|
3810
|
+
const first = complete[0];
|
|
3811
|
+
const second = complete[1];
|
|
3812
|
+
if (!first) return { status: "unresolved", reason: "missing_required_runtime_variable" };
|
|
3813
|
+
if (first.score < 0.85) return { status: "unresolved", reason: "candidate_score_below_inference_threshold" };
|
|
3814
|
+
if (second && first.score - second.score <= 0.05) {
|
|
3815
|
+
for (const candidate of complete.filter((item) => first.score - item.score <= 0.05))
|
|
3816
|
+
candidate.rejectedReasons.push("candidate_tied_with_equal_score");
|
|
3817
|
+
return { status: "ambiguous", reason: "candidate_tied_with_equal_score" };
|
|
3818
|
+
}
|
|
3819
|
+
return {
|
|
3820
|
+
status: "resolved",
|
|
3821
|
+
candidateOperationId: first.candidateOperationId,
|
|
3822
|
+
inferredVariables: first.derivedVariables,
|
|
3823
|
+
score: first.score,
|
|
3824
|
+
reasons: first.reasons
|
|
3825
|
+
};
|
|
3826
|
+
}
|
|
3827
|
+
function suggestedVarSets(candidates, required, order) {
|
|
3828
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3829
|
+
return candidates.flatMap((candidate) => {
|
|
3830
|
+
if (required.some((key) => candidate.derivedVariables[key] === void 0)) return [];
|
|
3831
|
+
const cli = cliFor(candidate.derivedVariables, order);
|
|
3832
|
+
if (seen.has(cli)) return [];
|
|
3833
|
+
seen.add(cli);
|
|
3834
|
+
return [{ variables: candidate.derivedVariables, cli }];
|
|
3835
|
+
});
|
|
3836
|
+
}
|
|
3837
|
+
function referenceRows(db, workspaceId) {
|
|
3838
|
+
const rows2 = db.prepare(
|
|
3839
|
+
`SELECT req.alias alias,req.destination destination,req.service_path servicePath,
|
|
3840
|
+
'cds_require' sourceKind,r.name repoName
|
|
3841
|
+
FROM cds_requires req JOIN repositories r ON r.id=req.repo_id
|
|
3842
|
+
WHERE (? IS NULL OR r.workspace_id=?)
|
|
3843
|
+
UNION ALL
|
|
3844
|
+
SELECT COALESCE(b.alias,b.alias_expr) alias,b.destination_expr destination,
|
|
3845
|
+
b.service_path_expr servicePath,'service_binding' sourceKind,r.name repoName
|
|
3846
|
+
FROM service_bindings b JOIN repositories r ON r.id=b.repo_id
|
|
3847
|
+
WHERE (? IS NULL OR r.workspace_id=?)`
|
|
3848
|
+
).all(workspaceId, workspaceId, workspaceId, workspaceId);
|
|
3849
|
+
return rows2.map((row) => ({
|
|
3850
|
+
alias: stringValue4(row.alias),
|
|
3851
|
+
destination: stringValue4(row.destination),
|
|
3852
|
+
servicePath: stringValue4(row.servicePath),
|
|
3853
|
+
sourceKind: String(row.sourceKind),
|
|
3854
|
+
repoName: String(row.repoName)
|
|
3855
|
+
}));
|
|
3856
|
+
}
|
|
3857
|
+
function allMissingVariables(evidence, templates) {
|
|
3858
|
+
const fromSubstitutions = Object.values(record(evidence.runtimeSubstitutions)).flatMap((value) => stringArray(record(value).missing));
|
|
3859
|
+
const fromTemplates = [
|
|
3860
|
+
templates.servicePath,
|
|
3861
|
+
templates.operationPath,
|
|
3862
|
+
templates.alias,
|
|
3863
|
+
templates.destination
|
|
3864
|
+
].flatMap((value) => extractPlaceholders(value));
|
|
3865
|
+
return [.../* @__PURE__ */ new Set([...fromSubstitutions, ...fromTemplates])].sort();
|
|
3866
|
+
}
|
|
3867
|
+
function variableOrder(templates, missingVariables) {
|
|
3868
|
+
const ordered = [
|
|
3869
|
+
templates.servicePath,
|
|
3870
|
+
templates.operationPath,
|
|
3871
|
+
templates.alias,
|
|
3872
|
+
templates.destination
|
|
3873
|
+
].flatMap((value) => extractPlaceholders(value));
|
|
3874
|
+
return [.../* @__PURE__ */ new Set([...ordered, ...missingVariables])];
|
|
3875
|
+
}
|
|
3876
|
+
function templatesFromEvidence(evidence) {
|
|
3877
|
+
return {
|
|
3878
|
+
servicePath: stringValue4(evidence.servicePath),
|
|
3879
|
+
operationPath: stringValue4(evidence.operationPath),
|
|
3880
|
+
alias: stringValue4(evidence.serviceAliasExpr ?? evidence.serviceAlias),
|
|
3881
|
+
destination: stringValue4(evidence.destination)
|
|
3882
|
+
};
|
|
3883
|
+
}
|
|
3884
|
+
function matchTemplate(template, concrete) {
|
|
3885
|
+
if (!template || !concrete) return void 0;
|
|
3886
|
+
const keys = extractPlaceholders(template);
|
|
3887
|
+
if (keys.length === 0) return template === concrete ? {} : void 0;
|
|
3888
|
+
const regex = new RegExp(`^${templateToPattern(template)}$`);
|
|
3889
|
+
const match = regex.exec(concrete);
|
|
3890
|
+
if (!match) return void 0;
|
|
3891
|
+
const values = {};
|
|
3892
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
3893
|
+
const key = keys[index];
|
|
3894
|
+
const value = match[index + 1];
|
|
3895
|
+
if (!key || value === void 0) return void 0;
|
|
3896
|
+
if (values[key] !== void 0 && values[key] !== value) return void 0;
|
|
3897
|
+
values[key] = value;
|
|
3898
|
+
}
|
|
3899
|
+
return values;
|
|
3900
|
+
}
|
|
3901
|
+
function templateToPattern(template) {
|
|
3902
|
+
let pattern = "";
|
|
3903
|
+
let lastIndex = 0;
|
|
3904
|
+
for (const match of template.matchAll(/\$\{([^}]*)\}/g)) {
|
|
3905
|
+
pattern += escapeRegex(template.slice(lastIndex, match.index));
|
|
3906
|
+
pattern += "([^/]+?)";
|
|
3907
|
+
lastIndex = (match.index ?? 0) + match[0].length;
|
|
3908
|
+
}
|
|
3909
|
+
return `${pattern}${escapeRegex(template.slice(lastIndex))}`;
|
|
3910
|
+
}
|
|
3911
|
+
function mergeDerived(state, values, sourceKind) {
|
|
3912
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3913
|
+
if (state.derivedVariables[key] !== void 0 && state.derivedVariables[key] !== value) {
|
|
3914
|
+
state.rejectedReasons.push("template_variable_conflict");
|
|
3915
|
+
continue;
|
|
3916
|
+
}
|
|
3917
|
+
state.derivedVariables[key] = value;
|
|
3918
|
+
state.derivedVariableSources[key] = { sourceKind, value };
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
function addScore(state, amount, reason) {
|
|
3922
|
+
state.score += amount;
|
|
3923
|
+
if (!state.reasons.includes(reason)) state.reasons.push(reason);
|
|
3924
|
+
}
|
|
3925
|
+
function hasResolvedImplementation(db, operationId) {
|
|
3926
|
+
const row = db.prepare(
|
|
3927
|
+
"SELECT 1 FROM graph_edges WHERE edge_type='OPERATION_IMPLEMENTED_BY_HANDLER' AND status='resolved' AND from_kind='operation' AND from_id=? LIMIT 1"
|
|
3928
|
+
).get(String(operationId));
|
|
3929
|
+
return Boolean(row);
|
|
3930
|
+
}
|
|
3931
|
+
function cliFor(variables, order) {
|
|
3932
|
+
return order.filter((key) => variables[key] !== void 0).map((key) => `--var ${key}=${variables[key]}`).join(" ");
|
|
3933
|
+
}
|
|
3934
|
+
function record(value) {
|
|
3935
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
3936
|
+
}
|
|
3937
|
+
function stringValue4(value) {
|
|
3938
|
+
return typeof value === "string" ? value : void 0;
|
|
3939
|
+
}
|
|
3940
|
+
function numberValue(value) {
|
|
3941
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3942
|
+
}
|
|
3943
|
+
function stringArray(value) {
|
|
3944
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
3945
|
+
}
|
|
3946
|
+
function nonEmptyStrings(value, fallback) {
|
|
3947
|
+
const values = stringArray(value).filter((item) => item.length > 0);
|
|
3948
|
+
return values.length > 0 ? values : fallback;
|
|
3949
|
+
}
|
|
3950
|
+
function isConcrete(value) {
|
|
3951
|
+
return typeof value === "string" && value.length > 0 && extractPlaceholders(value).length === 0;
|
|
3952
|
+
}
|
|
3953
|
+
function escapeRegex(value) {
|
|
3954
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3645
3957
|
// src/trace/evidence.ts
|
|
3646
3958
|
function baseTraceEvidence(row, call, persistedEvidence, contextualEvidence) {
|
|
3647
3959
|
const evidence = { ...persistedEvidence, ...contextualEvidence ?? {} };
|
|
@@ -3660,23 +3972,45 @@ function baseTraceEvidence(row, call, persistedEvidence, contextualEvidence) {
|
|
|
3660
3972
|
persistedResolution: persistedResolution(row)
|
|
3661
3973
|
};
|
|
3662
3974
|
}
|
|
3663
|
-
function runtimeResolution(db, row, evidence,
|
|
3664
|
-
const substituted = evidenceWithRuntimeVariables(evidence, vars);
|
|
3665
|
-
|
|
3975
|
+
function runtimeResolution(db, row, evidence, options, workspaceId, contextualUnresolvedReason) {
|
|
3976
|
+
const substituted = evidenceWithRuntimeVariables(evidence, options.vars);
|
|
3977
|
+
const dynamicMode = options.dynamicMode ?? "strict";
|
|
3978
|
+
const analysis = analyzeDynamicTargetCandidates(
|
|
3979
|
+
db,
|
|
3980
|
+
substituted,
|
|
3981
|
+
workspaceId,
|
|
3982
|
+
dynamicMode,
|
|
3983
|
+
positiveCandidateCap(options.maxDynamicCandidates)
|
|
3984
|
+
);
|
|
3985
|
+
const enriched = analysis ? evidenceWithDynamicAnalysis(substituted, analysis) : substituted;
|
|
3986
|
+
if (dynamicMode === "infer") {
|
|
3987
|
+
const inferred = inferredTarget(analysis);
|
|
3988
|
+
if (inferred) {
|
|
3989
|
+
const resolvedRow = { ...row, status: "resolved", to_kind: "operation", to_id: String(inferred.operationId), unresolved_reason: void 0, confidence: inferred.score };
|
|
3990
|
+
const resolution2 = { status: "resolved", target: inferred, candidates: [], reasons: inferred.reasons };
|
|
3991
|
+
return { row: resolvedRow, evidence: withEffectiveResolution(enriched, resolvedRow, void 0, resolution2), target: inferred };
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
if (!isRemoteRuntimeCandidate(row, evidence, options.vars)) {
|
|
3666
3995
|
const unresolvedReason2 = contextualUnresolvedReason ?? row.unresolved_reason;
|
|
3667
|
-
const withSections = withEffectiveResolution(
|
|
3996
|
+
const withSections = withEffectiveResolution(enriched, row, unresolvedReason2);
|
|
3668
3997
|
return { row, evidence: withSections, unresolvedReason: unresolvedReason2 };
|
|
3669
3998
|
}
|
|
3670
|
-
const resolution = resolveRuntimeOperation(db,
|
|
3999
|
+
const resolution = resolveRuntimeOperation(db, enriched, workspaceId);
|
|
3671
4000
|
if (resolution.target) {
|
|
3672
4001
|
const resolvedRow = { ...row, status: "resolved", to_kind: "operation", to_id: String(resolution.target.operationId), unresolved_reason: void 0, confidence: Math.max(0, Math.min(1, resolution.target.score)) };
|
|
3673
|
-
return { row: resolvedRow, evidence: withEffectiveResolution(
|
|
4002
|
+
return { row: resolvedRow, evidence: withEffectiveResolution(enriched, resolvedRow, void 0, resolution), target: resolution.target };
|
|
3674
4003
|
}
|
|
3675
4004
|
const unresolvedReason = runtimeUnresolvedReason(resolution);
|
|
3676
|
-
return { row, evidence: withEffectiveResolution(
|
|
4005
|
+
return { row, evidence: withEffectiveResolution(enriched, row, unresolvedReason, resolution), unresolvedReason };
|
|
3677
4006
|
}
|
|
3678
4007
|
function runtimeVariableDiagnostic(edges) {
|
|
3679
4008
|
const missing = /* @__PURE__ */ new Set();
|
|
4009
|
+
let candidateCount = 0;
|
|
4010
|
+
let shownCandidateCount = 0;
|
|
4011
|
+
let omittedCandidateCount = 0;
|
|
4012
|
+
const candidateSuggestions = [];
|
|
4013
|
+
const suggestedVarSets2 = [];
|
|
3680
4014
|
for (const edge of edges) {
|
|
3681
4015
|
const effective = parseObject(edge.evidence.effectiveResolution);
|
|
3682
4016
|
if (!["dynamic", "unresolved", "ambiguous"].includes(String(effective.status ?? "")))
|
|
@@ -3685,6 +4019,12 @@ function runtimeVariableDiagnostic(edges) {
|
|
|
3685
4019
|
if (!substitutions || typeof substitutions !== "object" || Array.isArray(substitutions)) continue;
|
|
3686
4020
|
for (const value of Object.values(substitutions))
|
|
3687
4021
|
for (const key of value.missing ?? []) missing.add(key);
|
|
4022
|
+
const exploration = parseObject(edge.evidence.dynamicTargetExploration);
|
|
4023
|
+
candidateCount += numeric(exploration.candidateCount);
|
|
4024
|
+
shownCandidateCount += numeric(exploration.shownCandidateCount);
|
|
4025
|
+
omittedCandidateCount += numeric(exploration.omittedCandidateCount);
|
|
4026
|
+
candidateSuggestions.push(...recordArray(edge.evidence.dynamicTargetCandidateSuggestions));
|
|
4027
|
+
suggestedVarSets2.push(...recordArray(exploration.suggestedVarSets));
|
|
3688
4028
|
}
|
|
3689
4029
|
const missingVariables = [...missing].sort();
|
|
3690
4030
|
if (missingVariables.length === 0) return void 0;
|
|
@@ -3693,25 +4033,34 @@ function runtimeVariableDiagnostic(edges) {
|
|
|
3693
4033
|
code: "trace_runtime_variables_missing",
|
|
3694
4034
|
message: `Runtime variables are required to resolve dynamic trace targets: ${missingVariables.join(", ")}`,
|
|
3695
4035
|
missingVariables,
|
|
3696
|
-
suggestions: missingVariables.map((key) => `--var ${key}=<value>`)
|
|
4036
|
+
suggestions: missingVariables.map((key) => `--var ${key}=<value>`),
|
|
4037
|
+
candidateCount,
|
|
4038
|
+
shownCandidateCount,
|
|
4039
|
+
omittedCandidateCount,
|
|
4040
|
+
candidateSuggestions: candidateSuggestions.slice(0, 5),
|
|
4041
|
+
suggestedVarSets: uniqueCliRows(suggestedVarSets2).slice(0, 5),
|
|
4042
|
+
copyableExamples: [
|
|
4043
|
+
...uniqueCliRows(suggestedVarSets2).slice(0, 3).flatMap((row) => typeof row.cli === "string" ? [row.cli] : []),
|
|
4044
|
+
...candidateCount > 0 ? ["--dynamic-mode candidates --max-dynamic-candidates 20"] : []
|
|
4045
|
+
]
|
|
3697
4046
|
};
|
|
3698
4047
|
}
|
|
3699
4048
|
function edgeTarget(row, evidence) {
|
|
3700
4049
|
const effective = parseObject(evidence.effectiveResolution);
|
|
3701
|
-
const targetServicePath =
|
|
3702
|
-
const targetOperationPath =
|
|
4050
|
+
const targetServicePath = stringValue5(effective.targetServicePath ?? evidence.targetServicePath);
|
|
4051
|
+
const targetOperationPath = stringValue5(effective.targetOperationPath ?? evidence.targetOperationPath);
|
|
3703
4052
|
if (targetServicePath && targetOperationPath) return `${targetServicePath}${targetOperationPath}`;
|
|
3704
4053
|
const runtimeCandidate = evidence.runtimeResolvedCandidate;
|
|
3705
4054
|
if (runtimeCandidate?.servicePath && runtimeCandidate.operationPath) return `${runtimeCandidate.servicePath}${runtimeCandidate.operationPath}`;
|
|
3706
|
-
const servicePath =
|
|
3707
|
-
const operationPath =
|
|
3708
|
-
const targetOperation =
|
|
3709
|
-
const targetRepo =
|
|
4055
|
+
const servicePath = stringValue5(evidence.servicePath);
|
|
4056
|
+
const operationPath = stringValue5(evidence.operationPath);
|
|
4057
|
+
const targetOperation = stringValue5(evidence.targetOperation);
|
|
4058
|
+
const targetRepo = stringValue5(evidence.targetRepo) ?? "";
|
|
3710
4059
|
if (row.edge_type === "HANDLER_RUNS_DB_QUERY") return `Entity: ${row.to_id || "unknown"}`;
|
|
3711
|
-
if (row.edge_type === "HANDLER_RUNS_REMOTE_QUERY") return
|
|
4060
|
+
if (row.edge_type === "HANDLER_RUNS_REMOTE_QUERY") return stringValue5(evidence.remoteQueryTarget) ?? `Remote query: ${row.to_id || "unknown"}`;
|
|
3712
4061
|
if (row.edge_type === "HANDLER_CALLS_EXTERNAL_HTTP") {
|
|
3713
4062
|
const target = parseObject(evidence.externalTarget);
|
|
3714
|
-
return
|
|
4063
|
+
return stringValue5(target.label) ?? `External endpoint: ${row.to_id || "unknown"}`;
|
|
3715
4064
|
}
|
|
3716
4065
|
if (servicePath && operationPath) return `${servicePath}${operationPath}`;
|
|
3717
4066
|
return targetOperation ? `${targetRepo}:${targetOperation}` : row.to_id;
|
|
@@ -3750,12 +4099,12 @@ function withEffectiveResolution(evidence, row, unresolvedReason, resolution) {
|
|
|
3750
4099
|
return { ...rest, effectiveResolution: current, linker: { status: current.status, confidence: current.confidence, reason: unresolvedReason, edgeType: current.edgeType } };
|
|
3751
4100
|
}
|
|
3752
4101
|
function resolveRuntimeOperation(db, evidence, workspaceId) {
|
|
3753
|
-
const servicePath =
|
|
3754
|
-
const rawOperationPath =
|
|
4102
|
+
const servicePath = stringValue5(evidence.servicePath);
|
|
4103
|
+
const rawOperationPath = stringValue5(evidence.operationPath);
|
|
3755
4104
|
const normalized = normalizeODataOperationInvocationPath(rawOperationPath);
|
|
3756
|
-
const operationPath = normalized?.wasInvocation ? normalized.normalizedOperationPath :
|
|
3757
|
-
const alias =
|
|
3758
|
-
const destination =
|
|
4105
|
+
const operationPath = normalized?.wasInvocation ? normalized.normalizedOperationPath : stringValue5(evidence.normalizedOperationPath) ?? rawOperationPath;
|
|
4106
|
+
const alias = stringValue5(evidence.serviceAliasExpr ?? evidence.serviceAlias);
|
|
4107
|
+
const destination = stringValue5(evidence.destination);
|
|
3759
4108
|
return resolveOperation(db, { servicePath, operationPath, alias, destination, hasExplicitOverride: true, isDynamic: true }, workspaceId);
|
|
3760
4109
|
}
|
|
3761
4110
|
function evidenceWithRuntimeVariables(evidence, vars) {
|
|
@@ -3767,6 +4116,22 @@ function evidenceWithRuntimeVariables(evidence, vars) {
|
|
|
3767
4116
|
if (Object.keys(vars ?? {}).length > 0) next.runtimeVariablesApplied = true;
|
|
3768
4117
|
return next;
|
|
3769
4118
|
}
|
|
4119
|
+
function evidenceWithDynamicAnalysis(evidence, analysis) {
|
|
4120
|
+
return {
|
|
4121
|
+
...evidence,
|
|
4122
|
+
dynamicTargetExploration: {
|
|
4123
|
+
mode: analysis.mode,
|
|
4124
|
+
missingVariables: analysis.missingVariables,
|
|
4125
|
+
candidateCount: analysis.candidateCount,
|
|
4126
|
+
shownCandidateCount: analysis.shownCandidateCount,
|
|
4127
|
+
omittedCandidateCount: analysis.omittedCandidateCount,
|
|
4128
|
+
suggestedVarSets: analysis.suggestedVarSets
|
|
4129
|
+
},
|
|
4130
|
+
dynamicTargetCandidates: analysis.candidates,
|
|
4131
|
+
dynamicTargetCandidateSuggestions: analysis.shownCandidates,
|
|
4132
|
+
dynamicTargetInference: analysis.inference
|
|
4133
|
+
};
|
|
4134
|
+
}
|
|
3770
4135
|
function runtimeSubstitutions(evidence, vars) {
|
|
3771
4136
|
const substitutions = {};
|
|
3772
4137
|
for (const key of ["servicePath", "operationPath", "serviceAliasExpr", "serviceAlias", "destination"]) {
|
|
@@ -3776,7 +4141,7 @@ function runtimeSubstitutions(evidence, vars) {
|
|
|
3776
4141
|
return substitutions;
|
|
3777
4142
|
}
|
|
3778
4143
|
function substitutionValue(evidence, key) {
|
|
3779
|
-
const value =
|
|
4144
|
+
const value = stringValue5(evidence[key]);
|
|
3780
4145
|
if (key !== "operationPath") return value;
|
|
3781
4146
|
const normalized = normalizeODataOperationInvocationPath(value);
|
|
3782
4147
|
return normalized?.wasInvocation ? normalized.normalizedOperationPath : value;
|
|
@@ -3787,6 +4152,29 @@ function isRemoteRuntimeCandidate(row, evidence, vars) {
|
|
|
3787
4152
|
if (!["DYNAMIC_EDGE_CANDIDATE", "UNRESOLVED_EDGE", "REMOTE_CALL_RESOLVES_TO_OPERATION"].includes(row.edge_type)) return false;
|
|
3788
4153
|
return ["servicePath", "operationPath", "serviceAliasExpr", "serviceAlias", "destination"].some((key) => hasRuntimeVariable(evidence[key], vars));
|
|
3789
4154
|
}
|
|
4155
|
+
function inferredTarget(analysis) {
|
|
4156
|
+
if (analysis?.inference.status !== "resolved") return void 0;
|
|
4157
|
+
const id = Number(analysis.inference.candidateOperationId);
|
|
4158
|
+
const candidate = analysis.candidates.find((item) => item.candidateOperationId === id);
|
|
4159
|
+
if (!candidate) return void 0;
|
|
4160
|
+
return targetFromCandidate(candidate);
|
|
4161
|
+
}
|
|
4162
|
+
function targetFromCandidate(candidate) {
|
|
4163
|
+
return {
|
|
4164
|
+
operationId: candidate.candidateOperationId,
|
|
4165
|
+
repoName: candidate.repoName,
|
|
4166
|
+
serviceName: "",
|
|
4167
|
+
qualifiedName: "",
|
|
4168
|
+
servicePath: candidate.servicePath,
|
|
4169
|
+
operationPath: candidate.operationPath,
|
|
4170
|
+
operationName: candidate.operationName,
|
|
4171
|
+
packageName: candidate.packageName,
|
|
4172
|
+
score: candidate.score,
|
|
4173
|
+
reasons: candidate.reasons,
|
|
4174
|
+
sourceFile: "",
|
|
4175
|
+
sourceLine: 0
|
|
4176
|
+
};
|
|
4177
|
+
}
|
|
3790
4178
|
function hasRuntimeVariable(value, vars) {
|
|
3791
4179
|
return typeof value === "string" && extractPlaceholders(value).some((key) => Object.hasOwn(vars, key));
|
|
3792
4180
|
}
|
|
@@ -3798,9 +4186,56 @@ function runtimeUnresolvedReason(resolution) {
|
|
|
3798
4186
|
function parseObject(value) {
|
|
3799
4187
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
3800
4188
|
}
|
|
3801
|
-
function
|
|
4189
|
+
function stringValue5(value) {
|
|
3802
4190
|
return typeof value === "string" ? value : void 0;
|
|
3803
4191
|
}
|
|
4192
|
+
function numeric(value) {
|
|
4193
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
4194
|
+
}
|
|
4195
|
+
function recordArray(value) {
|
|
4196
|
+
return Array.isArray(value) ? value.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
|
|
4197
|
+
}
|
|
4198
|
+
function uniqueCliRows(rows2) {
|
|
4199
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4200
|
+
return rows2.filter((row) => {
|
|
4201
|
+
const cli = typeof row.cli === "string" ? row.cli : JSON.stringify(row);
|
|
4202
|
+
if (seen.has(cli)) return false;
|
|
4203
|
+
seen.add(cli);
|
|
4204
|
+
return true;
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
function positiveCandidateCap(value) {
|
|
4208
|
+
return Number.isFinite(value) && Number(value) > 0 ? Math.floor(Number(value)) : 5;
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
// src/trace/dynamic-branches.ts
|
|
4212
|
+
function dynamicCandidateBranches(depth, call, evidence) {
|
|
4213
|
+
const exploration = objectRecord(evidence.dynamicTargetExploration);
|
|
4214
|
+
return recordArray2(evidence.dynamicTargetCandidateSuggestions).map((candidate) => ({
|
|
4215
|
+
step: depth,
|
|
4216
|
+
type: "dynamic_candidate_branch",
|
|
4217
|
+
from: `${call.repoName}:${call.source_file}:${call.source_line}`,
|
|
4218
|
+
to: `${String(candidate.servicePath ?? "")}${String(candidate.operationPath ?? "")}`,
|
|
4219
|
+
evidence: {
|
|
4220
|
+
...candidate,
|
|
4221
|
+
exploratory: true,
|
|
4222
|
+
dynamicMode: String(exploration.mode ?? "candidates"),
|
|
4223
|
+
selected: false,
|
|
4224
|
+
omittedCandidateCount: numericValue(exploration.omittedCandidateCount)
|
|
4225
|
+
},
|
|
4226
|
+
confidence: numericValue(candidate.score),
|
|
4227
|
+
unresolvedReason: "Exploratory dynamic target candidate; provide runtime variables to select it"
|
|
4228
|
+
}));
|
|
4229
|
+
}
|
|
4230
|
+
function objectRecord(value) {
|
|
4231
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
4232
|
+
}
|
|
4233
|
+
function recordArray2(value) {
|
|
4234
|
+
return Array.isArray(value) ? value.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
|
|
4235
|
+
}
|
|
4236
|
+
function numericValue(value) {
|
|
4237
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
4238
|
+
}
|
|
3804
4239
|
|
|
3805
4240
|
// src/trace/trace-engine.ts
|
|
3806
4241
|
function normalizeOperation(value) {
|
|
@@ -4269,7 +4704,11 @@ function trace(db, start, options) {
|
|
|
4269
4704
|
String(row.evidence_json || "{}")
|
|
4270
4705
|
);
|
|
4271
4706
|
const rawEvidence = baseTraceEvidence(row, call, persistedEvidence, contextual.evidence);
|
|
4272
|
-
const effective = runtimeResolution(db, row, rawEvidence,
|
|
4707
|
+
const effective = runtimeResolution(db, row, rawEvidence, {
|
|
4708
|
+
vars: options.vars,
|
|
4709
|
+
dynamicMode: options.dynamicMode ?? "strict",
|
|
4710
|
+
maxDynamicCandidates: options.maxDynamicCandidates
|
|
4711
|
+
}, workspaceIdForCall(db, String(call.id)), contextual.unresolvedReason);
|
|
4273
4712
|
const evidence = effective.evidence;
|
|
4274
4713
|
const effectiveRow = effective.row;
|
|
4275
4714
|
const targetNode = `${effectiveRow.to_kind}:${effectiveRow.to_id}`;
|
|
@@ -4289,6 +4728,8 @@ function trace(db, start, options) {
|
|
|
4289
4728
|
confidence: Number(effectiveRow.confidence ?? call.confidence),
|
|
4290
4729
|
unresolvedReason: effective.unresolvedReason
|
|
4291
4730
|
});
|
|
4731
|
+
if ((options.dynamicMode ?? "strict") === "candidates")
|
|
4732
|
+
edges.push(...dynamicCandidateBranches(current.depth, call, evidence));
|
|
4292
4733
|
if (effectiveRow.to_kind === "operation") {
|
|
4293
4734
|
const implementation = implementationScope(db, effectiveRow.to_id);
|
|
4294
4735
|
const contextSelection = contextImplementationMethodId(implementation.edge, current.repoId, evidence, hintOptions);
|
|
@@ -4371,4 +4812,4 @@ export {
|
|
|
4371
4812
|
parseVars,
|
|
4372
4813
|
trace
|
|
4373
4814
|
};
|
|
4374
|
-
//# sourceMappingURL=chunk-
|
|
4815
|
+
//# sourceMappingURL=chunk-YZJKE5UX.js.map
|