@saptools/service-flow 0.1.24 → 0.1.26
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
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.26
|
|
4
|
+
|
|
5
|
+
- Resolve helper-internal remote sends contextually in trace output when service clients are passed through positional arguments or one-level destructured object parameters.
|
|
6
|
+
- Add auditable contextual binding evidence for propagated helper client receivers, including caller argument, caller object property, callee parameter, callee receiver, and propagation source.
|
|
7
|
+
- Keep nested `this.<property>.<method>()` symbol-call resolution conservative unless explicit same-file or relative-import helper instance evidence exists.
|
|
8
|
+
- Update strict doctor diagnostics for trace-time contextual propagation opportunities and nested `this` receiver quality signals.
|
|
9
|
+
|
|
10
|
+
## 0.1.25
|
|
11
|
+
|
|
12
|
+
- Make class-instance symbol-call indexing conservative so only same-file and relatively imported helper classes are traced, while built-in collection, date, URL, error, typed-array, promise, and abort-controller instances are ignored.
|
|
13
|
+
- Persist identifier and one-level destructured object parameter metadata for executable symbols, including class methods, so contextual service-client binding propagation can map helper arguments to callee receiver names.
|
|
14
|
+
- Propagate contextual service bindings through positional helper arguments and destructured object helper parameters with auditable caller and callee evidence.
|
|
15
|
+
- Refine unresolved operation diagnostics when indexed candidates exist but service context is absent or the resolution score is below threshold.
|
|
16
|
+
|
|
3
17
|
## 0.1.24
|
|
4
18
|
|
|
5
19
|
- Add conservative class instance method symbol-call resolution for same-file and relatively imported helper classes, with auditable class-instance evidence.
|
|
@@ -1767,13 +1767,21 @@ function insertCallEdge(db, workspaceId, call, vars, generation) {
|
|
|
1767
1767
|
}
|
|
1768
1768
|
const edgeType = callType === "local_db_query" ? "HANDLER_RUNS_DB_QUERY" : callType === "external_http" ? "HANDLER_CALLS_EXTERNAL_HTTP" : callType === "async_emit" ? "HANDLER_EMITS_EVENT" : callType === "async_subscribe" ? "EVENT_CONSUMED_BY_HANDLER" : resolution.status === "dynamic" ? "DYNAMIC_EDGE_CANDIDATE" : "UNRESOLVED_EDGE";
|
|
1769
1769
|
const status = edgeType === "DYNAMIC_EDGE_CANDIDATE" ? "dynamic" : resolution.status === "ambiguous" ? "ambiguous" : edgeType === "UNRESOLVED_EDGE" ? "unresolved" : "terminal";
|
|
1770
|
-
const unresolvedReason = status === "terminal" ? null : String(call.unresolved_reason ?? (resolution
|
|
1770
|
+
const unresolvedReason = status === "terminal" ? null : String(call.unresolved_reason ?? unresolvedOperationReason(resolution));
|
|
1771
1771
|
const targetKind = callType === "local_db_query" ? "db_entity" : callType.startsWith("async_") ? "event" : "external";
|
|
1772
1772
|
const targetId = callType === "local_db_query" ? String(call.query_entity ?? "unknown") : callType === "remote_action" ? op ? `Remote action: ${op}` : call.unresolved_reason === "dynamic_operation_path_identifier" ? "Remote action: dynamic path" : "Remote action: unknown path" : String(call.event_name_expr ?? op ?? call.id);
|
|
1773
1773
|
const graphLevelDynamic = edgeType === "DYNAMIC_EDGE_CANDIDATE" && resolution.status === "dynamic";
|
|
1774
1774
|
db.prepare("INSERT INTO graph_edges(workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,unresolved_reason,generation) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)").run(workspaceId, edgeType, status, "call", String(call.id), targetKind, targetId, Number(call.confidence ?? 0.2), JSON.stringify(evidence), graphLevelDynamic ? 1 : 0, unresolvedReason, generation);
|
|
1775
1775
|
return { status, callType };
|
|
1776
1776
|
}
|
|
1777
|
+
function unresolvedOperationReason(resolution) {
|
|
1778
|
+
if (resolution.status === "dynamic") return `Dynamic target requires runtime variable overrides: ${(resolution.reasons.length ? resolution.reasons : ["missing runtime variables"]).join(", ")}`;
|
|
1779
|
+
if (resolution.candidates.length === 0) return "No indexed target operation matched";
|
|
1780
|
+
if (resolution.reasons.includes("operation_path_only_has_no_strong_target_signal")) return "Operation candidates found but no strong service signal is available";
|
|
1781
|
+
if (resolution.reasons.includes("candidate_score_below_resolution_threshold")) return "Operation candidates found but resolution score is below threshold";
|
|
1782
|
+
if (resolution.status === "ambiguous") return "Ambiguous operation candidates require a strong service signal";
|
|
1783
|
+
return "Operation candidates found but resolution could not select a target";
|
|
1784
|
+
}
|
|
1777
1785
|
function parseJson(value) {
|
|
1778
1786
|
if (!value) return void 0;
|
|
1779
1787
|
try {
|
|
@@ -2269,6 +2277,21 @@ function knownBindingsForCalls(db, calls) {
|
|
|
2269
2277
|
}
|
|
2270
2278
|
return map;
|
|
2271
2279
|
}
|
|
2280
|
+
function knownBindingsForScope(db, repoId, symbolIds, files) {
|
|
2281
|
+
const map = /* @__PURE__ */ new Map();
|
|
2282
|
+
if (repoId === void 0) return map;
|
|
2283
|
+
const rows2 = db.prepare("SELECT id,variable_name variableName,alias,alias_expr aliasExpr,destination_expr destinationExpr,service_path_expr servicePathExpr,source_file sourceFile,source_line sourceLine FROM service_bindings WHERE repo_id=?").all(repoId);
|
|
2284
|
+
for (const row of rows2) {
|
|
2285
|
+
if (!row.variableName) continue;
|
|
2286
|
+
if (files && !files.has(String(row.sourceFile))) continue;
|
|
2287
|
+
if (symbolIds && symbolIds.size > 0) {
|
|
2288
|
+
const owner = db.prepare("SELECT id FROM symbols WHERE id IN (" + [...symbolIds].map(() => "?").join(",") + ") AND source_file=? AND start_line<=? AND end_line>=? LIMIT 1").get(...symbolIds, row.sourceFile, row.sourceLine, row.sourceLine);
|
|
2289
|
+
if (!owner) continue;
|
|
2290
|
+
}
|
|
2291
|
+
map.set(row.variableName, { ...row, bindingId: Number(row.id), source: "local_service_binding", calleeReceiver: row.variableName });
|
|
2292
|
+
}
|
|
2293
|
+
return map;
|
|
2294
|
+
}
|
|
2272
2295
|
function contextForSymbolCall(db, symbolCall, callerBindings) {
|
|
2273
2296
|
const next = /* @__PURE__ */ new Map();
|
|
2274
2297
|
if (callerBindings.size === 0) return next;
|
|
@@ -2276,19 +2299,23 @@ function contextForSymbolCall(db, symbolCall, callerBindings) {
|
|
|
2276
2299
|
const callee = db.prepare("SELECT evidence_json evidenceJson FROM symbols WHERE id=?").get(symbolCall.callee_symbol_id);
|
|
2277
2300
|
const calleeEvidence = parseEvidence(callee?.evidenceJson);
|
|
2278
2301
|
const params = Array.isArray(calleeEvidence.parameters) ? calleeEvidence.parameters.filter((item) => typeof item === "string") : [];
|
|
2302
|
+
const parameterBindings = Array.isArray(calleeEvidence.parameterBindings) ? calleeEvidence.parameterBindings.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
|
|
2279
2303
|
const args = Array.isArray(callEvidence2.callArguments) ? callEvidence2.callArguments : [];
|
|
2280
2304
|
args.forEach((arg, index) => {
|
|
2281
|
-
const
|
|
2282
|
-
|
|
2305
|
+
const paramBinding = parameterBindings.find((binding) => binding.index === index);
|
|
2306
|
+
const param = paramBinding?.kind === "identifier" && typeof paramBinding.name === "string" ? paramBinding.name : params[index];
|
|
2283
2307
|
if (arg.kind === "identifier" && typeof arg.name === "string") {
|
|
2284
2308
|
const binding = callerBindings.get(arg.name);
|
|
2285
|
-
if (binding) next.set(param, { ...binding, source: "local_symbol_argument", callerArgument: arg.name, calleeParameter: param, calleeReceiver: param });
|
|
2309
|
+
if (binding && param) next.set(param, { ...binding, source: "local_symbol_argument", callerArgument: arg.name, calleeParameter: param, calleeReceiver: param });
|
|
2286
2310
|
}
|
|
2287
2311
|
if (arg.kind === "object_literal" && Array.isArray(arg.properties)) {
|
|
2288
2312
|
for (const prop of arg.properties) {
|
|
2289
2313
|
if (typeof prop.property !== "string" || typeof prop.argument !== "string") continue;
|
|
2290
2314
|
const binding = callerBindings.get(prop.argument);
|
|
2291
|
-
if (binding)
|
|
2315
|
+
if (!binding) continue;
|
|
2316
|
+
const destructured = paramBinding?.kind === "object_pattern" && Array.isArray(paramBinding.properties) ? paramBinding.properties.find((item) => item.property === prop.property && typeof item.local === "string") : void 0;
|
|
2317
|
+
if (destructured && typeof destructured.local === "string") next.set(destructured.local, { ...binding, source: "local_symbol_destructured_object_argument", callerProperty: prop.property, callerArgument: prop.argument, calleeParameter: String(index), calleeReceiver: destructured.local });
|
|
2318
|
+
else if (param) next.set(`${param}.${prop.property}`, { ...binding, source: "local_symbol_object_argument", callerProperty: prop.property, callerArgument: prop.argument, calleeParameter: param, calleeReceiver: `${param}.${prop.property}` });
|
|
2292
2319
|
}
|
|
2293
2320
|
}
|
|
2294
2321
|
});
|
|
@@ -2361,7 +2388,8 @@ function trace(db, start, options) {
|
|
|
2361
2388
|
while (queue.length > 0) {
|
|
2362
2389
|
const current = queue.shift();
|
|
2363
2390
|
if (!current || current.depth > maxDepth) continue;
|
|
2364
|
-
const
|
|
2391
|
+
const contextKey = [...(current.context ?? /* @__PURE__ */ new Map()).keys()].sort().join(",");
|
|
2392
|
+
const key = `${current.repoId ?? "*"}:${[...current.symbolIds ?? /* @__PURE__ */ new Set(["*"])].sort().join(",")}:${[...current.files ?? /* @__PURE__ */ new Set(["*"])].sort().join(",")}:${contextKey}`;
|
|
2365
2393
|
if (seenScopes.has(key)) continue;
|
|
2366
2394
|
seenScopes.add(key);
|
|
2367
2395
|
const calls = db.prepare(
|
|
@@ -2370,7 +2398,7 @@ function trace(db, start, options) {
|
|
|
2370
2398
|
const filtered = calls.filter(
|
|
2371
2399
|
(c) => (!current.symbolIds || current.symbolIds.has(Number(c.source_symbol_id))) && (!current.files || current.files.has(String(c.source_file))) && includeCall(String(c.call_type), options)
|
|
2372
2400
|
);
|
|
2373
|
-
const callerBindings = new Map([...current.context ?? /* @__PURE__ */ new Map(), ...knownBindingsForCalls(db, filtered)]);
|
|
2401
|
+
const callerBindings = new Map([...current.context ?? /* @__PURE__ */ new Map(), ...knownBindingsForScope(db, current.repoId, current.symbolIds, current.files), ...knownBindingsForCalls(db, filtered)]);
|
|
2374
2402
|
if (current.symbolIds && current.symbolIds.size > 0 && current.depth < maxDepth) {
|
|
2375
2403
|
const symbolRows = db.prepare(`SELECT sc.*,s.repo_id calleeRepoId,s.source_file calleeFile FROM symbol_calls sc LEFT JOIN symbols s ON s.id=sc.callee_symbol_id WHERE sc.caller_symbol_id IN (${[...current.symbolIds].map(() => "?").join(",")}) ORDER BY sc.source_file,sc.source_line`).all(...current.symbolIds);
|
|
2376
2404
|
for (const symbolCall of symbolRows) {
|
|
@@ -2401,7 +2429,7 @@ function trace(db, start, options) {
|
|
|
2401
2429
|
line: call.source_line,
|
|
2402
2430
|
callType: call.call_type
|
|
2403
2431
|
});
|
|
2404
|
-
const contextual = contextualRuntimeResolution(db, call,
|
|
2432
|
+
const contextual = contextualRuntimeResolution(db, call, callerBindings.get(receiverFromEvidence(call.evidence_json) ?? ""), workspaceIdForCall(db, String(call.id)));
|
|
2405
2433
|
const graphRows = contextual.row ? [contextual.row] : graph.get(Number(call.id)) ?? [];
|
|
2406
2434
|
for (const row of graphRows) {
|
|
2407
2435
|
if (seenEdges.has(Number(row.id))) continue;
|
|
@@ -2503,4 +2531,4 @@ export {
|
|
|
2503
2531
|
linkWorkspace,
|
|
2504
2532
|
trace
|
|
2505
2533
|
};
|
|
2506
|
-
//# sourceMappingURL=chunk-
|
|
2534
|
+
//# sourceMappingURL=chunk-Q3J2QDNX.js.map
|