@saptools/service-flow 0.1.37 → 0.1.39

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.
Files changed (49) hide show
  1. package/dist/{chunk-WE3A6TOJ.js → chunk-SAZ5OK7R.js} +50 -9
  2. package/dist/chunk-SAZ5OK7R.js.map +1 -0
  3. package/dist/cli.js +4 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/package.json +3 -2
  7. package/src/cli.ts +865 -0
  8. package/src/config/defaults.ts +14 -0
  9. package/src/config/workspace-config.ts +61 -0
  10. package/src/db/connection.ts +131 -0
  11. package/src/db/migrations.ts +81 -0
  12. package/src/db/repositories.ts +353 -0
  13. package/src/db/schema.ts +24 -0
  14. package/src/discovery/classify-repository.ts +50 -0
  15. package/src/discovery/discover-repositories.ts +58 -0
  16. package/src/index.ts +13 -0
  17. package/src/indexer/incremental-index.ts +25 -0
  18. package/src/indexer/repository-indexer.ts +137 -0
  19. package/src/indexer/workspace-indexer.ts +29 -0
  20. package/src/linker/cross-repo-linker.ts +406 -0
  21. package/src/linker/dynamic-edge-resolver.ts +45 -0
  22. package/src/linker/external-http-target.ts +38 -0
  23. package/src/linker/helper-package-linker.ts +57 -0
  24. package/src/linker/odata-path-normalizer.ts +236 -0
  25. package/src/linker/operation-decorator-normalizer.ts +47 -0
  26. package/src/linker/remote-query-target.ts +39 -0
  27. package/src/linker/service-resolver.ts +223 -0
  28. package/src/output/json-output.ts +7 -0
  29. package/src/output/mermaid-output.ts +16 -0
  30. package/src/output/table-output.ts +21 -0
  31. package/src/parsers/cds-parser.ts +147 -0
  32. package/src/parsers/decorator-parser.ts +76 -0
  33. package/src/parsers/generated-constants-parser.ts +23 -0
  34. package/src/parsers/handler-registration-parser.ts +177 -0
  35. package/src/parsers/outbound-call-parser.ts +441 -0
  36. package/src/parsers/package-json-parser.ts +63 -0
  37. package/src/parsers/service-binding-parser.ts +826 -0
  38. package/src/parsers/symbol-parser.ts +328 -0
  39. package/src/parsers/ts-project.ts +13 -0
  40. package/src/trace/selectors.ts +20 -0
  41. package/src/trace/trace-engine.ts +647 -0
  42. package/src/trace/traversal.ts +4 -0
  43. package/src/types.ts +186 -0
  44. package/src/utils/diagnostics.ts +10 -0
  45. package/src/utils/hashing.ts +10 -0
  46. package/src/utils/path-utils.ts +13 -0
  47. package/src/utils/redaction.ts +20 -0
  48. package/src/version.ts +4 -0
  49. package/dist/chunk-WE3A6TOJ.js.map +0 -1
@@ -879,9 +879,46 @@ function nameOfProperty(name) {
879
879
  function staticExpressionText(expr, initializers) {
880
880
  if (!expr) return void 0;
881
881
  if (isStringLike(expr)) return expr.text;
882
+ if (ts4.isTemplateExpression(expr)) return stripQuotes(expr.getText(expr.getSourceFile()));
882
883
  if (ts4.isIdentifier(expr) && initializers.has(expr.text)) return staticExpressionText(initializers.get(expr.text), initializers);
883
884
  return void 0;
884
885
  }
886
+ function staticPathExpression(expr, initializers) {
887
+ if (!expr) return { sourceKind: "dynamic" };
888
+ const rawExpression = expr.getText(expr.getSourceFile());
889
+ if (isStringLike(expr)) return { text: expr.text, sourceKind: "literal", rawExpression };
890
+ if (ts4.isTemplateExpression(expr)) return { text: stripQuotes(rawExpression), sourceKind: "template", rawExpression };
891
+ if (ts4.isIdentifier(expr) && initializers.has(expr.text)) {
892
+ const resolved2 = staticPathExpression(initializers.get(expr.text), initializers);
893
+ return resolved2.text ? { ...resolved2, sourceKind: "const", rawExpression, constName: expr.text } : { sourceKind: "dynamic", rawExpression };
894
+ }
895
+ return { sourceKind: "dynamic", rawExpression };
896
+ }
897
+ function operationPathFromStatic(text) {
898
+ return text ? `/${stripQuotes(text).replace(/^\//, "")}` : void 0;
899
+ }
900
+ function staticPathCandidates(source, identifier, initializers) {
901
+ const paths = [];
902
+ let hasDynamicAssignments = false;
903
+ const visit = (node) => {
904
+ if (ts4.isVariableDeclaration(node) && ts4.isIdentifier(node.name) && node.name.text === identifier && node.initializer) {
905
+ const resolved2 = staticPathExpression(node.initializer, initializers);
906
+ if (resolved2.text) paths.push(operationPathFromStatic(resolved2.text) ?? resolved2.text);
907
+ else hasDynamicAssignments = true;
908
+ }
909
+ if (ts4.isBinaryExpression(node) && node.operatorToken.kind === ts4.SyntaxKind.EqualsToken && ts4.isIdentifier(node.left) && node.left.text === identifier) {
910
+ const resolved2 = staticPathExpression(node.right, initializers);
911
+ if (resolved2.text) paths.push(operationPathFromStatic(resolved2.text) ?? resolved2.text);
912
+ else hasDynamicAssignments = true;
913
+ }
914
+ ts4.forEachChild(node, visit);
915
+ };
916
+ visit(source);
917
+ const candidatePaths = [...new Set(paths)];
918
+ if (candidatePaths.length === 0) return void 0;
919
+ const normalizedCandidateOperations = [...new Set(candidatePaths.map((candidate) => classifyODataPathIntent(candidate, "POST").topLevelOperationName).filter((value) => Boolean(value)))];
920
+ return { candidatePaths, normalizedCandidateOperations, candidateSourceKind: "static candidates observed", candidateIdentifier: identifier, hasDynamicAssignments };
921
+ }
885
922
  function destinationExpressionShape(expr) {
886
923
  if (!expr) return void 0;
887
924
  if (ts4.isIdentifier(expr)) return "identifier";
@@ -1072,16 +1109,17 @@ function classifyOutboundCallsInSource(source, filePath) {
1072
1109
  const query = objectPropertyText(objectArg, "query");
1073
1110
  const method = stripQuotes(objectPropertyText(objectArg, "method") ?? "POST");
1074
1111
  const pathExpr = propertyInitializer(objectArg, "path") ?? propertyInitializer(objectArg, "event");
1075
- const op = pathExpr ? staticExpressionText(pathExpr, initializers) ?? pathExpr.getText(source) : void 0;
1112
+ const resolvedPath = staticPathExpression(pathExpr, initializers);
1113
+ const op = pathExpr ? resolvedPath.text ?? pathExpr.getText(source) : void 0;
1076
1114
  const shorthandPath = objectPropertyIsShorthand(objectArg, "path");
1077
- const staticPath = staticExpressionText(pathExpr, initializers);
1078
- const literalPath = isStringLike(pathExpr) ? pathExpr.text : pathExpr && ts4.isTemplateExpression(pathExpr) ? pathExpr.getText(source) : void 0;
1079
- const operationPathExpr = staticPath ? `/${stripQuotes(staticPath).replace(/^\//, "")}` : literalPath ? `/${stripQuotes(literalPath).replace(/^\//, "")}` : void 0;
1115
+ const candidateEvidence2 = pathExpr && ts4.isIdentifier(pathExpr) && !resolvedPath.text ? staticPathCandidates(source, pathExpr.text, initializers) : void 0;
1116
+ const candidateOperationPath = candidateEvidence2 && !candidateEvidence2.hasDynamicAssignments && candidateEvidence2.normalizedCandidateOperations.length === 1 && candidateEvidence2.candidatePaths.length > 0 ? candidateEvidence2.candidatePaths[0] : void 0;
1117
+ const operationPathExpr = operationPathFromStatic(resolvedPath.text) ?? candidateOperationPath;
1080
1118
  const intent = classifyODataPathIntent(operationPathExpr, method);
1081
1119
  const entityCallTypes = { entity_mutation: "remote_entity_mutation", entity_delete: "remote_entity_delete", entity_media: "remote_entity_media", entity_candidate: "remote_entity_candidate" };
1082
1120
  const entityCallType = entityCallTypes[intent.kind];
1083
1121
  const isODataQueryRead = method.toUpperCase() === "GET" && ["entity_query", "entity_key_read", "entity_navigation_query"].includes(intent.kind);
1084
- add(node, { callType: query ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity: query ? extractQueryEntity(query) : isODataQueryRead ? intent.entitySegment : void 0, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || query ? 0.8 : 0.4, unresolvedReason: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 }, { receiver, classifier: "service_client_send_object", operationPathExpression: shorthandPath ? op : void 0, literalPathSource: shorthandPath && staticPath ? "same_scope_const_initializer" : void 0, odataPathIntent: operationPathExpr ? intent : void 0, parserWarning: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 });
1122
+ add(node, { callType: query ? "remote_query" : entityCallType ?? (isODataQueryRead ? "remote_query" : "remote_action"), serviceVariableName: receiver, method, operationPathExpr, queryEntity: query ? extractQueryEntity(query) : isODataQueryRead ? intent.entitySegment : void 0, payloadSummary: summarizeExpression(objectArg.getText(source)), confidence: op || query ? 0.8 : 0.4, unresolvedReason: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 }, { receiver, classifier: "service_client_send_object", operationPathExpression: shorthandPath ? op : void 0, rawPathExpression: resolvedPath.rawExpression, literalPathSource: resolvedPath.text ? resolvedPath.sourceKind === "const" ? "same_scope_const_initializer" : resolvedPath.sourceKind : void 0, odataPathIntent: operationPathExpr ? intent : void 0, staticPathCandidates: candidateEvidence2, parserWarning: !query && pathExpr && !operationPathExpr ? "dynamic_operation_path_identifier" : void 0 });
1085
1123
  }
1086
1124
  } else if (ts4.isCallExpression(expr) && ts4.isIdentifier(expr.expression) && wrapperSpecs.has(expr.expression.text) || ts4.isIdentifier(expr) && wrapperSpecs.has(expr.text)) {
1087
1125
  const wrapperName = ts4.isIdentifier(expr) ? expr.text : ts4.isCallExpression(expr) && ts4.isIdentifier(expr.expression) ? expr.expression.text : "";
@@ -1092,10 +1130,13 @@ function classifyOutboundCallsInSource(source, filePath) {
1092
1130
  const methodArg = spec?.methodIndex === void 0 ? void 0 : wrapperArgs[spec.methodIndex];
1093
1131
  const receiver = clientArg && ts4.isIdentifier(clientArg) ? clientArg.text : spec?.clientName;
1094
1132
  const method = stripQuotes(staticExpressionText(methodArg, initializers) ?? spec?.methodLiteral ?? "POST");
1095
- if (spec && receiver && isStringLike(pathArg)) {
1096
- add(node, { callType: "remote_action", serviceVariableName: receiver, method, operationPathExpr: `/${pathArg.text.replace(/^\//, "")}`, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.75 }, { receiver, classifier: "higher_order_wrapper_literal_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), literalPathSource: "wrapper_call_argument", literalCallerArgumentDetected: true });
1133
+ const resolvedPath = staticPathExpression(pathArg, initializers);
1134
+ const operationPathExpr = operationPathFromStatic(resolvedPath.text);
1135
+ const normalizedOperationPath = operationPathExpr ? classifyODataPathIntent(operationPathExpr, method).topLevelOperationName : void 0;
1136
+ if (spec && receiver && operationPathExpr) {
1137
+ add(node, { callType: "remote_action", serviceVariableName: receiver, method, operationPathExpr, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.75 }, { receiver, classifier: resolvedPath.sourceKind === "literal" ? "higher_order_wrapper_literal_path" : "higher_order_wrapper_static_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), wrapperPathSourceKind: resolvedPath.sourceKind, rawPathExpression: resolvedPath.rawExpression, normalizedOperationPath, literalPathSource: resolvedPath.sourceKind === "const" ? "same_scope_const_initializer" : `wrapper_call_${resolvedPath.sourceKind}`, literalCallerArgumentDetected: true });
1097
1138
  } else if (spec && receiver) {
1098
- add(node, { callType: "remote_action", serviceVariableName: receiver, method, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.45, unresolvedReason: "dynamic_operation_path_identifier" }, { receiver, classifier: "higher_order_wrapper_dynamic_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), parserWarning: "dynamic_operation_path_identifier" });
1139
+ add(node, { callType: "remote_action", serviceVariableName: receiver, method, payloadSummary: summarizeExpression(node.getText(source)), confidence: 0.45, unresolvedReason: "dynamic_operation_path_identifier" }, { receiver, classifier: "higher_order_wrapper_dynamic_path", wrapperFunction: wrapperName, wrapperDefinitionLine: spec.definitionLine, callerLine: lineOf3(source.text, node.getStart(source)), wrapperPathSourceKind: resolvedPath.sourceKind, rawPathExpression: resolvedPath.rawExpression, parserWarning: "dynamic_operation_path_identifier" });
1099
1140
  }
1100
1141
  } else if (ts4.isPropertyAccessExpression(expr) && ["emit", "publish", "on"].includes(expr.name.text)) {
1101
1142
  const receiver = receiverName(expr.expression);
@@ -3079,4 +3120,4 @@ export {
3079
3120
  linkWorkspace,
3080
3121
  trace
3081
3122
  };
3082
- //# sourceMappingURL=chunk-WE3A6TOJ.js.map
3123
+ //# sourceMappingURL=chunk-SAZ5OK7R.js.map