@saptools/service-flow 0.1.27 → 0.1.29

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/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  parsePackageJson,
13
13
  parseServiceBindings,
14
14
  trace
15
- } from "./chunk-VDJTNOEI.js";
15
+ } from "./chunk-GOE6ZK6I.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.27",
191
+ version: "0.1.29",
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: {
@@ -789,6 +789,41 @@ function bindingLocalName(name, initializer) {
789
789
  if (initializer && ts.isIdentifier(initializer)) return initializer.text;
790
790
  return void 0;
791
791
  }
792
+ function objectPatternAliases(pattern, parameter, source, lineNode) {
793
+ return pattern.elements.flatMap((element) => {
794
+ if (element.dotDotDotToken || ts.isObjectBindingPattern(element.name) || ts.isArrayBindingPattern(element.name)) return [];
795
+ const property = element.propertyName ? nameOf(element.propertyName) : nameOf(element.name);
796
+ if (!property) return [];
797
+ const local = bindingLocalName(element.name, element.initializer);
798
+ return local ? [{ parameter, property, local, kind: "object_parameter_destructure", line: lineOf(source, lineNode.getStart(source)) }] : [];
799
+ });
800
+ }
801
+ function parameterPropertyAliases(fn, source) {
802
+ const parameterNames = new Set(fn.parameters.flatMap((param) => ts.isIdentifier(param.name) ? [param.name.text] : []));
803
+ if (!fn.body || parameterNames.size === 0) return [];
804
+ const aliases = [];
805
+ const addFromAssignment = (left, right, node) => {
806
+ if (!ts.isObjectLiteralExpression(left) || !ts.isIdentifier(right) || !parameterNames.has(right.text)) return;
807
+ for (const prop of left.properties) {
808
+ if (!ts.isPropertyAssignment(prop)) continue;
809
+ const property = nameOf(prop.name);
810
+ if (property && ts.isIdentifier(prop.initializer)) aliases.push({ parameter: right.text, property, local: prop.initializer.text, kind: "object_parameter_destructure", line: lineOf(source, node.getStart(source)) });
811
+ }
812
+ };
813
+ const visit = (node) => {
814
+ if (ts.isVariableDeclaration(node) && ts.isObjectBindingPattern(node.name) && node.initializer && ts.isIdentifier(node.initializer) && parameterNames.has(node.initializer.text)) aliases.push(...objectPatternAliases(node.name, node.initializer.text, source, node));
815
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) addFromAssignment(ts.isParenthesizedExpression(node.left) ? node.left.expression : node.left, node.right, node);
816
+ ts.forEachChild(node, visit);
817
+ };
818
+ visit(fn.body);
819
+ const seen = /* @__PURE__ */ new Set();
820
+ return aliases.filter((alias) => {
821
+ const key = `${alias.parameter}.${alias.property}:${alias.local}`;
822
+ if (seen.has(key)) return false;
823
+ seen.add(key);
824
+ return true;
825
+ });
826
+ }
792
827
  function parameterBindings(params) {
793
828
  return params.flatMap((param, index) => {
794
829
  if (ts.isIdentifier(param.name)) return [{ index, kind: "identifier", name: param.name.text }];
@@ -826,7 +861,8 @@ async function parseExecutableSymbols(repoPath, filePath) {
826
861
  const bindings = isFunctionLike(node) ? parameterBindings(node.parameters) : void 0;
827
862
  const params = bindings?.flatMap((binding) => binding.kind === "identifier" ? [binding.name] : []);
828
863
  const sourceEvidence = evidence ?? (classMemberExported ? { source: "exported_class_member", exportedClass: parentRoot, memberKind: (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static) !== 0 ? "static_method" : "class_method", parameters: params } : declaredExportName ? { exportedName: declaredExportName, source: "export_declaration" } : objectExported ? { exportedName: qualifiedName, source: "exported_object_literal" } : void 0);
829
- const parameterEvidence = bindings && bindings.length > 0 ? { parameters: params, parameterBindings: bindings } : {};
864
+ const aliases = isFunctionLike(node) ? parameterPropertyAliases(node, source) : [];
865
+ const parameterEvidence = { ...bindings && bindings.length > 0 ? { parameters: params, parameterBindings: bindings } : {}, ...aliases.length > 0 ? { parameterPropertyAliases: aliases } : {} };
830
866
  symbols.push({ kind, localName: kind === "object_method" ? qualifiedName : localName, exportedName: effectiveExportedName, qualifiedName, sourceFile, startLine: lineOf(source, node.getStart(source)), endLine: lineOf(source, node.getEnd()), startOffset: node.getStart(source), endOffset: node.getEnd(), exported: exported(node) || Boolean(effectiveExportedName), importExportEvidence: sourceEvidence ? { ...sourceEvidence, ...parameterEvidence } : bindings && bindings.length > 0 ? parameterEvidence : void 0 });
831
867
  };
832
868
  const addAliasSymbol = (objectName, propertyName, node, targetImportSource) => {
@@ -1400,7 +1436,11 @@ function contextualBindingPropagationQuality(db) {
1400
1436
  AND EXISTS (SELECT 1 FROM symbol_calls sc WHERE sc.status='resolved' AND sc.source_file=c.source_file)
1401
1437
  ) GROUP BY contextualStatus ORDER BY count DESC,contextualStatus`).all();
1402
1438
  const resolvedContextual = db.prepare(`SELECT COUNT(*) count FROM outbound_calls c JOIN graph_edges e ON e.from_kind='call' AND e.from_id=CAST(c.id AS TEXT) WHERE c.call_type='remote_action' AND e.status='resolved' AND c.service_binding_id IS NOT NULL`).get();
1403
- return { severity: Number(missingMetadata.count ?? 0) + Number(destructuredUnmapped.count ?? 0) + opportunities.length > 0 ? "warning" : "info", code: "strict_contextual_binding_propagation_quality", message: "Contextual service-client propagation opportunities for trace-time helper resolution", localSymbolCallsWithServiceClientArguments: Number(serviceClientCalls.count ?? 0), calleeSymbolsMissingParameterMetadata: Number(missingMetadata.count ?? 0), destructuredObjectParametersPossiblyUnmapped: Number(destructuredUnmapped.count ?? 0), contextualHelperSendsResolvedDuringPersistedLink: Number(resolvedContextual.count ?? 0), traceTimeContextualOpportunities: opportunities.length, traceTimeContextualOpportunityBreakdown: statusRows.length > 0 ? statusRows : [{ contextualStatus: "no_contextual_opportunity", count: 0 }], examples: opportunities };
1439
+ const totalOpportunities = statusRows.reduce((sum, row) => sum + Number(row.count ?? 0), 0);
1440
+ const actionableStatuses = /* @__PURE__ */ new Set(["alias_without_matching_cds_requires", "cds_requires_present_but_persisted_resolution_unresolved", "trace_time_contextual_binding_candidate"]);
1441
+ const actionableOpportunityCount = statusRows.reduce((sum, row) => actionableStatuses.has(String(row.contextualStatus)) ? sum + Number(row.count ?? 0) : sum, 0);
1442
+ const severity = Number(missingMetadata.count ?? 0) + Number(destructuredUnmapped.count ?? 0) + actionableOpportunityCount > 0 ? "warning" : "info";
1443
+ return { severity, code: "strict_contextual_binding_propagation_quality", message: "Contextual service-client propagation opportunities for trace-time helper resolution", localSymbolCallsWithServiceClientArguments: Number(serviceClientCalls.count ?? 0), calleeSymbolsMissingParameterMetadata: Number(missingMetadata.count ?? 0), destructuredObjectParametersPossiblyUnmapped: Number(destructuredUnmapped.count ?? 0), contextualHelperSendsResolvedDuringPersistedLink: Number(resolvedContextual.count ?? 0), traceTimeContextualOpportunities: totalOpportunities, traceTimeContextualOpportunityBreakdown: statusRows.length > 0 ? statusRows : [{ contextualStatus: "no_contextual_opportunity", count: 0 }], exampleCount: opportunities.length, examples: opportunities };
1404
1444
  }
1405
1445
  function nestedThisReceiverQuality(db) {
1406
1446
  const aggregate = db.prepare(`SELECT COUNT(*) total,