@saptools/service-flow 0.1.26 → 0.1.27

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.27
4
+
5
+ - Enrich trace-time contextual service-client bindings from package-level CAP `cds.requires` aliases so helper-internal sends resolve through require-derived service paths and destinations.
6
+ - Preserve contextual binding attempt evidence for unresolved helper sends, including effective service path, destination, candidate counts, and resolution reasons.
7
+ - Refine strict doctor diagnostics for contextual helper sends to distinguish resolved contextual opportunities, missing `cds.requires` rows, require-backed unresolved sends, remaining runtime variables, and workspaces with no contextual opportunity.
8
+ - Expand neutral CAP coverage for positional helper arguments, destructured object helper parameters, renamed properties, late assignments, table output, Mermaid output, and require-derived target operations.
9
+
3
10
  ## 0.1.26
4
11
 
5
12
  - Resolve helper-internal remote sends contextually in trace output when service clients are passed through positional arguments or one-level destructured object parameters.
@@ -2266,21 +2266,33 @@ function receiverFromEvidence(value) {
2266
2266
  const evidence = parseEvidence(value);
2267
2267
  return typeof evidence.receiver === "string" ? evidence.receiver : void 0;
2268
2268
  }
2269
+ function hasDynamicPlaceholder(value) {
2270
+ return extractPlaceholders(value).length > 0;
2271
+ }
2272
+ function enrichBinding(row) {
2273
+ const effectiveServicePath = row.servicePathExpr && !hasDynamicPlaceholder(row.servicePathExpr) ? row.servicePathExpr : !row.servicePathExpr ? row.requireServicePath : void 0;
2274
+ const effectiveDestination = row.destinationExpr && !hasDynamicPlaceholder(row.destinationExpr) ? row.destinationExpr : !row.destinationExpr ? row.requireDestination : void 0;
2275
+ return { ...row, effectiveServicePath, effectiveDestination };
2276
+ }
2269
2277
  function knownBindingsForCalls(db, calls) {
2270
2278
  const map = /* @__PURE__ */ new Map();
2271
2279
  for (const call of calls) {
2272
2280
  const receiver = receiverFromEvidence(call.evidence_json);
2273
2281
  const bindingId = Number(call.service_binding_id ?? 0);
2274
2282
  if (!receiver || !bindingId) continue;
2275
- const row = db.prepare("SELECT id,alias,alias_expr aliasExpr,destination_expr destinationExpr,service_path_expr servicePathExpr,source_file sourceFile,source_line sourceLine FROM service_bindings WHERE id=?").get(bindingId);
2276
- if (row) map.set(receiver, { ...row, bindingId, source: "local_service_binding", calleeReceiver: receiver });
2283
+ const row = db.prepare(`SELECT b.id,b.alias,b.alias_expr aliasExpr,b.destination_expr destinationExpr,b.service_path_expr servicePathExpr,b.source_file sourceFile,b.source_line sourceLine,req.service_path requireServicePath,req.destination requireDestination
2284
+ FROM service_bindings b LEFT JOIN cds_requires req ON req.repo_id=b.repo_id AND req.alias=b.alias
2285
+ WHERE b.id=?`).get(bindingId);
2286
+ if (row) map.set(receiver, enrichBinding({ ...row, bindingId, source: "local_service_binding", calleeReceiver: receiver }));
2277
2287
  }
2278
2288
  return map;
2279
2289
  }
2280
2290
  function knownBindingsForScope(db, repoId, symbolIds, files) {
2281
2291
  const map = /* @__PURE__ */ new Map();
2282
2292
  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);
2293
+ const rows2 = db.prepare(`SELECT b.id,b.variable_name variableName,b.alias,b.alias_expr aliasExpr,b.destination_expr destinationExpr,b.service_path_expr servicePathExpr,b.source_file sourceFile,b.source_line sourceLine,req.service_path requireServicePath,req.destination requireDestination
2294
+ FROM service_bindings b LEFT JOIN cds_requires req ON req.repo_id=b.repo_id AND req.alias=b.alias
2295
+ WHERE b.repo_id=?`).all(repoId);
2284
2296
  for (const row of rows2) {
2285
2297
  if (!row.variableName) continue;
2286
2298
  if (files && !files.has(String(row.sourceFile))) continue;
@@ -2288,7 +2300,7 @@ function knownBindingsForScope(db, repoId, symbolIds, files) {
2288
2300
  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
2301
  if (!owner) continue;
2290
2302
  }
2291
- map.set(row.variableName, { ...row, bindingId: Number(row.id), source: "local_service_binding", calleeReceiver: row.variableName });
2303
+ map.set(row.variableName, enrichBinding({ ...row, bindingId: Number(row.id), source: "local_service_binding", calleeReceiver: row.variableName }));
2292
2304
  }
2293
2305
  return map;
2294
2306
  }
@@ -2324,10 +2336,13 @@ function contextForSymbolCall(db, symbolCall, callerBindings) {
2324
2336
  function contextualRuntimeResolution(db, call, binding, workspaceId) {
2325
2337
  if (!binding || String(call.call_type) !== "remote_action" || call.operation_path_expr === void 0 || call.operation_path_expr === null) return {};
2326
2338
  const op = normalizeODataOperationInvocationPathForTrace(String(call.operation_path_expr));
2327
- const resolution = resolveOperation(db, { servicePath: binding.servicePathExpr, operationPath: op, alias: binding.aliasExpr ?? binding.alias, destination: binding.destinationExpr, hasExplicitOverride: true, isDynamic: false }, workspaceId);
2328
- const evidence = { contextualServiceBindingSelected: true, contextualBinding: { source: binding.source, callerArgument: binding.callerArgument, callerProperty: binding.callerProperty, calleeParameter: binding.calleeParameter, calleeReceiver: binding.calleeReceiver, bindingSourceFile: binding.sourceFile, bindingSourceLine: binding.sourceLine }, operationPath: op, servicePath: binding.servicePathExpr, serviceAlias: binding.alias, serviceAliasExpr: binding.aliasExpr, destination: binding.destinationExpr, contextualResolutionStatus: resolution.status, candidates: resolution.candidates, resolutionReasons: resolution.reasons };
2329
- if (!resolution.target) return { evidence, unresolvedReason: resolution.status === "ambiguous" ? "Ambiguous contextual operation candidates" : "No contextual operation candidate matched" };
2330
- return { row: { id: -Number(call.id), edge_type: "REMOTE_CALL_RESOLVES_TO_OPERATION", from_id: String(call.id), to_kind: "operation", to_id: String(resolution.target.operationId), confidence: resolution.target.score, evidence_json: JSON.stringify(evidence), status: "resolved" }, evidence: { ...evidence, targetRepo: resolution.target.repoName, targetServicePath: resolution.target.servicePath, targetOperationPath: resolution.target.operationPath, targetOperation: resolution.target.operationName }, unresolvedReason: void 0 };
2339
+ const servicePath = binding.effectiveServicePath ?? binding.servicePathExpr ?? binding.requireServicePath;
2340
+ const destination = binding.effectiveDestination ?? binding.destinationExpr ?? binding.requireDestination;
2341
+ const resolution = resolveOperation(db, { servicePath, operationPath: op, alias: binding.aliasExpr ?? binding.alias, destination, hasExplicitOverride: true, isDynamic: false }, workspaceId);
2342
+ const evidence = { contextualServiceBindingAttempted: true, contextualBinding: { source: binding.source, callerArgument: binding.callerArgument, callerProperty: binding.callerProperty, calleeParameter: binding.calleeParameter, calleeReceiver: binding.calleeReceiver, bindingSourceFile: binding.sourceFile, bindingSourceLine: binding.sourceLine, alias: binding.alias, aliasExpr: binding.aliasExpr, requireServicePath: binding.requireServicePath, requireDestination: binding.requireDestination, effectiveServicePath: binding.effectiveServicePath, effectiveDestination: binding.effectiveDestination }, operationPath: op, servicePath, serviceAlias: binding.alias, serviceAliasExpr: binding.aliasExpr, destination, requireServicePath: binding.requireServicePath, requireDestination: binding.requireDestination, effectiveServicePath: binding.effectiveServicePath, effectiveDestination: binding.effectiveDestination, contextualResolutionStatus: resolution.status, contextualCandidateCount: resolution.candidates.length, candidates: resolution.candidates, contextualResolutionReasons: resolution.reasons, resolutionReasons: resolution.reasons };
2343
+ if (!resolution.target) return { evidence, unresolvedReason: resolution.status === "ambiguous" ? "Ambiguous contextual operation candidates" : resolution.status === "dynamic" ? `Dynamic contextual target is missing runtime variables: ${resolution.reasons.join(", ")}` : "No contextual operation candidate matched" };
2344
+ const resolvedEvidence = { ...evidence, contextualServiceBindingSelected: true, targetRepo: resolution.target.repoName, targetServicePath: resolution.target.servicePath, targetOperationPath: resolution.target.operationPath, targetOperation: resolution.target.operationName };
2345
+ return { row: { id: -Number(call.id), edge_type: "REMOTE_CALL_RESOLVES_TO_OPERATION", from_id: String(call.id), to_kind: "operation", to_id: String(resolution.target.operationId), confidence: resolution.target.score, evidence_json: JSON.stringify(resolvedEvidence), status: "resolved" }, evidence: resolvedEvidence, unresolvedReason: void 0 };
2331
2346
  }
2332
2347
  function normalizeODataOperationInvocationPathForTrace(raw) {
2333
2348
  return raw.startsWith("/") ? raw : `/${raw}`;
@@ -2434,9 +2449,10 @@ function trace(db, start, options) {
2434
2449
  for (const row of graphRows) {
2435
2450
  if (seenEdges.has(Number(row.id))) continue;
2436
2451
  seenEdges.add(Number(row.id));
2437
- const rawEvidence = contextual.evidence && row.id < 0 ? contextual.evidence : JSON.parse(
2452
+ const persistedEvidence = JSON.parse(
2438
2453
  String(row.evidence_json || "{}")
2439
2454
  );
2455
+ const rawEvidence = contextual.evidence ? { ...persistedEvidence, ...contextual.evidence } : persistedEvidence;
2440
2456
  const effective = runtimeResolution(db, row, rawEvidence, options.vars);
2441
2457
  const evidence = effective.evidence;
2442
2458
  const effectiveRow = effective.row;
@@ -2531,4 +2547,4 @@ export {
2531
2547
  linkWorkspace,
2532
2548
  trace
2533
2549
  };
2534
- //# sourceMappingURL=chunk-Q3J2QDNX.js.map
2550
+ //# sourceMappingURL=chunk-VDJTNOEI.js.map