@saptools/service-flow 0.1.64 → 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/CHANGELOG.md +6 -0
- package/dist/{chunk-TBH33OYC.js → chunk-OONNRIDL.js} +2 -1
- package/dist/cli.js +78 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/parsers/symbol-parser.ts +106 -1
- /package/dist/{chunk-TBH33OYC.js.map → chunk-OONNRIDL.js.map} +0 -0
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,10 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
import type { ExecutableSymbolFact, SymbolCallFact } from '../types.js';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
classifyOutboundCallsInSource,
|
|
7
|
+
containsSupportedOutboundCall,
|
|
8
|
+
} from './outbound-call-parser.js';
|
|
6
9
|
import type { RepositorySourceContext } from './ts-project.js';
|
|
7
10
|
import { normalizePath } from '../utils/path-utils.js';
|
|
8
11
|
|
|
@@ -38,6 +41,76 @@ function exportDeclarations(source: ts.SourceFile): Map<string, string> {
|
|
|
38
41
|
function isRelativeImport(value: string | undefined): boolean {
|
|
39
42
|
return Boolean(value?.startsWith('.'));
|
|
40
43
|
}
|
|
44
|
+
type HandlerReferenceRelation =
|
|
45
|
+
| 'relative_import'
|
|
46
|
+
| 'package_import'
|
|
47
|
+
| 'indexed_local_symbol'
|
|
48
|
+
| 'relative_import_namespace_member';
|
|
49
|
+
interface HandlerReferenceTarget {
|
|
50
|
+
calleeExpression: string;
|
|
51
|
+
calleeLocalName: string;
|
|
52
|
+
importSource?: string;
|
|
53
|
+
relation: HandlerReferenceRelation;
|
|
54
|
+
wrapperFunction?: string;
|
|
55
|
+
}
|
|
56
|
+
function directHandlerReferenceTarget(
|
|
57
|
+
expression: ts.Expression,
|
|
58
|
+
source: ts.SourceFile,
|
|
59
|
+
imports: Map<string, string>,
|
|
60
|
+
namespaceImports: Set<string>,
|
|
61
|
+
): HandlerReferenceTarget | undefined {
|
|
62
|
+
if (ts.isIdentifier(expression)) {
|
|
63
|
+
const importSource = imports.get(expression.text);
|
|
64
|
+
return {
|
|
65
|
+
calleeExpression: expression.text,
|
|
66
|
+
calleeLocalName: expression.text,
|
|
67
|
+
importSource,
|
|
68
|
+
relation: importSource
|
|
69
|
+
? isRelativeImport(importSource) ? 'relative_import' : 'package_import'
|
|
70
|
+
: 'indexed_local_symbol',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (!ts.isPropertyAccessExpression(expression) || expression.questionDotToken
|
|
74
|
+
|| !ts.isIdentifier(expression.expression) || !ts.isIdentifier(expression.name))
|
|
75
|
+
return undefined;
|
|
76
|
+
const objectName = expression.expression.text;
|
|
77
|
+
const memberName = expression.name.text;
|
|
78
|
+
const importSource = imports.get(objectName);
|
|
79
|
+
if (namespaceImports.has(objectName)) return {
|
|
80
|
+
calleeExpression: expression.getText(source),
|
|
81
|
+
calleeLocalName: memberName,
|
|
82
|
+
importSource,
|
|
83
|
+
relation: 'relative_import_namespace_member',
|
|
84
|
+
};
|
|
85
|
+
const qualifiedName = `${objectName}.${memberName}`;
|
|
86
|
+
return {
|
|
87
|
+
calleeExpression: qualifiedName,
|
|
88
|
+
calleeLocalName: qualifiedName,
|
|
89
|
+
importSource,
|
|
90
|
+
relation: importSource
|
|
91
|
+
? isRelativeImport(importSource) ? 'relative_import' : 'package_import'
|
|
92
|
+
: 'indexed_local_symbol',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function handlerReferenceTarget(
|
|
96
|
+
expression: ts.Expression,
|
|
97
|
+
source: ts.SourceFile,
|
|
98
|
+
imports: Map<string, string>,
|
|
99
|
+
namespaceImports: Set<string>,
|
|
100
|
+
): HandlerReferenceTarget | undefined {
|
|
101
|
+
const direct = directHandlerReferenceTarget(
|
|
102
|
+
expression, source, imports, namespaceImports,
|
|
103
|
+
);
|
|
104
|
+
if (direct) return direct;
|
|
105
|
+
if (!ts.isCallExpression(expression) || expression.questionDotToken
|
|
106
|
+
|| expression.arguments.length !== 1) return undefined;
|
|
107
|
+
const inner = directHandlerReferenceTarget(
|
|
108
|
+
expression.arguments[0], source, imports, namespaceImports,
|
|
109
|
+
);
|
|
110
|
+
return inner
|
|
111
|
+
? { ...inner, wrapperFunction: expression.expression.getText(source) }
|
|
112
|
+
: undefined;
|
|
113
|
+
}
|
|
41
114
|
function isObjectFunction(node: ts.Node): boolean {
|
|
42
115
|
return ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node);
|
|
43
116
|
}
|
|
@@ -185,6 +258,11 @@ export async function parseExecutableSymbols(
|
|
|
185
258
|
const calls: SymbolCallFact[] = [];
|
|
186
259
|
const imports = new Map<string, string>();
|
|
187
260
|
const namespaceImports = new Set<string>();
|
|
261
|
+
const eventSubscriptionOffsets = new Set(
|
|
262
|
+
classifyOutboundCallsInSource(source, sourceFile)
|
|
263
|
+
.filter((call) => call.fact.callType === 'async_subscribe')
|
|
264
|
+
.map((call) => call.node.getStart(source)),
|
|
265
|
+
);
|
|
188
266
|
const exportNames = exportDeclarations(source);
|
|
189
267
|
const objectExports = new Set<string>();
|
|
190
268
|
const exportedClasses = new Set<string>();
|
|
@@ -305,6 +383,33 @@ export async function parseExecutableSymbols(
|
|
|
305
383
|
const name = `event:${eventName}:${startLine}`;
|
|
306
384
|
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 } });
|
|
307
385
|
}
|
|
386
|
+
if (eventSubscriptionOffsets.has(node.getStart(source))) {
|
|
387
|
+
const startLine = lineOf(source, node.getStart(source));
|
|
388
|
+
const handlerArgument = node.arguments[1];
|
|
389
|
+
const target = handlerArgument
|
|
390
|
+
? handlerReferenceTarget(
|
|
391
|
+
handlerArgument, source, imports, namespaceImports,
|
|
392
|
+
)
|
|
393
|
+
: undefined;
|
|
394
|
+
const anchor = nearest(symbols, startLine);
|
|
395
|
+
if (target && anchor) calls.push({
|
|
396
|
+
callerQualifiedName: anchor.qualifiedName,
|
|
397
|
+
calleeExpression: target.calleeExpression,
|
|
398
|
+
calleeLocalName: target.calleeLocalName,
|
|
399
|
+
importSource: target.importSource,
|
|
400
|
+
sourceFile,
|
|
401
|
+
sourceLine: startLine,
|
|
402
|
+
evidence: {
|
|
403
|
+
relation: target.relation,
|
|
404
|
+
caller: anchor.qualifiedName,
|
|
405
|
+
targetName: target.calleeLocalName,
|
|
406
|
+
...(target.wrapperFunction
|
|
407
|
+
? { wrapperFunction: target.wrapperFunction }
|
|
408
|
+
: {}),
|
|
409
|
+
candidateStrategy: 'event_subscribe_handler_reference',
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
}
|
|
308
413
|
}
|
|
309
414
|
ts.forEachChild(node, visitEventRegistrationSymbols);
|
|
310
415
|
};
|
|
File without changes
|