@saptools/service-flow 0.1.63 → 0.1.65

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
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  classifyODataPathIntent,
4
+ classifyOutboundCallsInSource,
4
5
  clearRepoFacts,
5
6
  containsSupportedOutboundCall,
6
7
  discoverRepositories,
@@ -35,7 +36,7 @@ import {
35
36
  trace,
36
37
  upsertRepository,
37
38
  upsertWorkspace
38
- } from "./chunk-EGBTHN7J.js";
39
+ } from "./chunk-OONNRIDL.js";
39
40
 
40
41
  // src/cli.ts
41
42
  import { Command } from "commander";
@@ -237,7 +238,7 @@ function migrate(db) {
237
238
  // package.json
238
239
  var package_default = {
239
240
  name: "@saptools/service-flow",
240
- version: "0.1.63",
241
+ version: "0.1.65",
241
242
  description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
242
243
  type: "module",
243
244
  publishConfig: {
@@ -490,6 +491,52 @@ function exportDeclarations(source) {
490
491
  function isRelativeImport(value) {
491
492
  return Boolean(value?.startsWith("."));
492
493
  }
494
+ function directHandlerReferenceTarget(expression, source, imports, namespaceImports) {
495
+ if (ts.isIdentifier(expression)) {
496
+ const importSource2 = imports.get(expression.text);
497
+ return {
498
+ calleeExpression: expression.text,
499
+ calleeLocalName: expression.text,
500
+ importSource: importSource2,
501
+ relation: importSource2 ? isRelativeImport(importSource2) ? "relative_import" : "package_import" : "indexed_local_symbol"
502
+ };
503
+ }
504
+ if (!ts.isPropertyAccessExpression(expression) || expression.questionDotToken || !ts.isIdentifier(expression.expression) || !ts.isIdentifier(expression.name))
505
+ return void 0;
506
+ const objectName = expression.expression.text;
507
+ const memberName = expression.name.text;
508
+ const importSource = imports.get(objectName);
509
+ if (namespaceImports.has(objectName)) return {
510
+ calleeExpression: expression.getText(source),
511
+ calleeLocalName: memberName,
512
+ importSource,
513
+ relation: "relative_import_namespace_member"
514
+ };
515
+ const qualifiedName = `${objectName}.${memberName}`;
516
+ return {
517
+ calleeExpression: qualifiedName,
518
+ calleeLocalName: qualifiedName,
519
+ importSource,
520
+ relation: importSource ? isRelativeImport(importSource) ? "relative_import" : "package_import" : "indexed_local_symbol"
521
+ };
522
+ }
523
+ function handlerReferenceTarget(expression, source, imports, namespaceImports) {
524
+ const direct = directHandlerReferenceTarget(
525
+ expression,
526
+ source,
527
+ imports,
528
+ namespaceImports
529
+ );
530
+ if (direct) return direct;
531
+ if (!ts.isCallExpression(expression) || expression.questionDotToken || expression.arguments.length !== 1) return void 0;
532
+ const inner = directHandlerReferenceTarget(
533
+ expression.arguments[0],
534
+ source,
535
+ imports,
536
+ namespaceImports
537
+ );
538
+ return inner ? { ...inner, wrapperFunction: expression.expression.getText(source) } : void 0;
539
+ }
493
540
  function isObjectFunction(node) {
494
541
  return ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node);
495
542
  }
@@ -655,6 +702,9 @@ async function parseExecutableSymbols(repoPath, filePath, context) {
655
702
  const calls = [];
656
703
  const imports = /* @__PURE__ */ new Map();
657
704
  const namespaceImports = /* @__PURE__ */ new Set();
705
+ const eventSubscriptionOffsets = new Set(
706
+ classifyOutboundCallsInSource(source, sourceFile).filter((call) => call.fact.callType === "async_subscribe").map((call) => call.node.getStart(source))
707
+ );
658
708
  const exportNames = exportDeclarations(source);
659
709
  const objectExports = /* @__PURE__ */ new Set();
660
710
  const exportedClasses = /* @__PURE__ */ new Set();
@@ -771,6 +821,32 @@ async function parseExecutableSymbols(repoPath, filePath, context) {
771
821
  const name = `event:${eventName}:${startLine}`;
772
822
  symbols.push({ kind: "event_registration", localName: name, qualifiedName: `module:${sourceFile}#${name}`, sourceFile, startLine, endLine: lineOf(source, node.getEnd()), startOffset: node.getStart(source), endOffset: node.getEnd(), exported: false, importExportEvidence: { source: "synthetic_event_registration", eventName: eventArg.text, registrationLine: startLine, receiver } });
773
823
  }
824
+ if (eventSubscriptionOffsets.has(node.getStart(source))) {
825
+ const startLine = lineOf(source, node.getStart(source));
826
+ const handlerArgument = node.arguments[1];
827
+ const target = handlerArgument ? handlerReferenceTarget(
828
+ handlerArgument,
829
+ source,
830
+ imports,
831
+ namespaceImports
832
+ ) : void 0;
833
+ const anchor = nearest(symbols, startLine);
834
+ if (target && anchor) calls.push({
835
+ callerQualifiedName: anchor.qualifiedName,
836
+ calleeExpression: target.calleeExpression,
837
+ calleeLocalName: target.calleeLocalName,
838
+ importSource: target.importSource,
839
+ sourceFile,
840
+ sourceLine: startLine,
841
+ evidence: {
842
+ relation: target.relation,
843
+ caller: anchor.qualifiedName,
844
+ targetName: target.calleeLocalName,
845
+ ...target.wrapperFunction ? { wrapperFunction: target.wrapperFunction } : {},
846
+ candidateStrategy: "event_subscribe_handler_reference"
847
+ }
848
+ });
849
+ }
774
850
  }
775
851
  ts.forEachChild(node, visitEventRegistrationSymbols);
776
852
  };