@saptools/service-flow 0.1.67 → 0.1.68
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 +10 -0
- package/README.md +20 -8
- package/TECHNICAL-NOTE.md +27 -0
- package/dist/{chunk-ZQABU7MR.js → chunk-AEM4JY22.js} +12676 -6714
- package/dist/chunk-AEM4JY22.js.map +1 -0
- package/dist/cli.js +2325 -413
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +61 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/003-doctor-package-resolution.ts +68 -0
- package/src/cli/doctor.ts +25 -14
- package/src/db/000-call-fact-repository.ts +475 -342
- package/src/db/001-fact-lifecycle.ts +60 -30
- package/src/db/002-fact-json-inventory.ts +169 -0
- package/src/db/003-current-fact-semantics.ts +698 -0
- package/src/db/004-package-target-invalidation.ts +173 -0
- package/src/db/005-schema-structure.ts +201 -0
- package/src/db/006-relative-symbol-resolution.ts +443 -0
- package/src/db/007-package-fact-semantics.ts +573 -0
- package/src/db/008-relative-fact-semantics.ts +207 -0
- package/src/db/009-binding-fact-semantics.ts +347 -0
- package/src/db/010-package-symbol-surface-semantics.ts +320 -0
- package/src/db/011-symbol-call-semantics.ts +144 -0
- package/src/db/012-binding-reference-proof.ts +264 -0
- package/src/db/migrations.ts +16 -3
- package/src/db/repositories.ts +113 -6
- package/src/db/schema.ts +4 -2
- package/src/index.ts +12 -0
- package/src/indexer/repository-indexer.ts +72 -11
- package/src/indexer/workspace-indexer.ts +123 -32
- package/src/linker/003-package-import-symbol-resolver.ts +363 -131
- package/src/linker/004-event-subscription-handler-linker.ts +2 -0
- package/src/linker/005-odata-path-structure.ts +371 -0
- package/src/linker/cross-repo-linker.ts +2 -1
- package/src/linker/odata-path-normalizer.ts +273 -180
- package/src/linker/service-resolver.ts +197 -77
- package/src/parsers/002-symbol-import-bindings.ts +516 -0
- package/src/parsers/003-package-public-surface.ts +661 -0
- package/src/parsers/004-fact-identity.ts +108 -0
- package/src/parsers/005-event-subscription-facts.ts +281 -0
- package/src/parsers/006-binding-identity.ts +343 -0
- package/src/parsers/007-source-fact-reconciliation.ts +105 -0
- package/src/parsers/008-package-surface-publication.ts +82 -0
- package/src/parsers/009-symbol-call-facts.ts +528 -0
- package/src/parsers/010-package-public-surface-analysis.ts +352 -0
- package/src/parsers/011-binding-lexical-scope.ts +583 -0
- package/src/parsers/012-package-fact-contract.ts +306 -0
- package/src/parsers/013-executable-body-eligibility.ts +35 -0
- package/src/parsers/014-service-binding-helper-flow.ts +240 -0
- package/src/parsers/015-service-binding-collector.ts +693 -0
- package/src/parsers/016-local-symbol-reference.ts +261 -0
- package/src/parsers/017-symbol-derived-contexts.ts +268 -0
- package/src/parsers/018-package-commonjs-syntax.ts +142 -0
- package/src/parsers/019-binding-assignment-targets.ts +76 -0
- package/src/parsers/020-stable-local-value.ts +217 -0
- package/src/parsers/021-binding-visibility.ts +152 -0
- package/src/parsers/operation-path-analysis.ts +6 -1
- package/src/parsers/outbound-call-parser.ts +19 -6
- package/src/parsers/package-json-parser.ts +45 -3
- package/src/parsers/service-binding-parser-helpers.ts +86 -15
- package/src/parsers/service-binding-parser.ts +147 -597
- package/src/parsers/symbol-parser.ts +482 -353
- package/src/trace/002-trace-diagnostics.ts +36 -1
- package/src/trace/016-compact-projector.ts +16 -17
- package/src/trace/020-compact-field-projection.ts +48 -37
- package/src/trace/021-compact-decision-normalization.ts +105 -0
- package/src/trace/022-trace-fact-preflight.ts +21 -0
- package/src/trace/023-nested-event-scopes.ts +23 -0
- package/src/trace/024-compact-observation-decision.ts +76 -0
- package/src/trace/025-trace-implementation-scope.ts +123 -0
- package/src/trace/026-trace-start-scope.ts +335 -0
- package/src/trace/027-trace-scope-execution.ts +566 -0
- package/src/trace/028-trace-operation-execution.ts +336 -0
- package/src/trace/029-trace-start-implementation.ts +172 -0
- package/src/trace/trace-engine.ts +122 -624
- package/src/types.ts +56 -0
- package/src/utils/001-placeholders.ts +188 -10
- package/src/version.ts +1 -1
- package/dist/chunk-ZQABU7MR.js.map +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ExecutableSymbolFact } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const executableOwnerKinds = [
|
|
4
|
+
'event_registration',
|
|
5
|
+
'callback',
|
|
6
|
+
'method',
|
|
7
|
+
'object_method',
|
|
8
|
+
'function',
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
type ExecutableOwnerKind = typeof executableOwnerKinds[number];
|
|
12
|
+
|
|
13
|
+
export interface OwnerCandidate {
|
|
14
|
+
kind: string;
|
|
15
|
+
qualifiedName: string;
|
|
16
|
+
startOffset: number;
|
|
17
|
+
endOffset: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OwnerSelection<T extends OwnerCandidate> {
|
|
21
|
+
status: 'resolved' | 'none' | 'ambiguous';
|
|
22
|
+
owner?: T;
|
|
23
|
+
eligibleCount: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const kindRank = new Map<ExecutableOwnerKind, number>(
|
|
27
|
+
executableOwnerKinds.map((kind, index) => [kind, index]),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export function binaryCompare(left: string, right: string): number {
|
|
31
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function containsHalfOpen(
|
|
35
|
+
containerStart: number,
|
|
36
|
+
containerEnd: number,
|
|
37
|
+
childStart: number,
|
|
38
|
+
childEnd: number,
|
|
39
|
+
): boolean {
|
|
40
|
+
return containerStart <= childStart && containerEnd >= childEnd;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isExecutableOwnerKind(kind: string): kind is ExecutableOwnerKind {
|
|
44
|
+
return kindRank.has(kind as ExecutableOwnerKind);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ownerIdentity(candidate: OwnerCandidate): string {
|
|
48
|
+
return [
|
|
49
|
+
candidate.kind,
|
|
50
|
+
candidate.startOffset,
|
|
51
|
+
candidate.endOffset,
|
|
52
|
+
candidate.qualifiedName,
|
|
53
|
+
].join('\u0000');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function compareOwners(left: OwnerCandidate, right: OwnerCandidate): number {
|
|
57
|
+
return (left.endOffset - left.startOffset) - (right.endOffset - right.startOffset)
|
|
58
|
+
|| (kindRank.get(left.kind as ExecutableOwnerKind) ?? 99)
|
|
59
|
+
- (kindRank.get(right.kind as ExecutableOwnerKind) ?? 99)
|
|
60
|
+
|| left.startOffset - right.startOffset
|
|
61
|
+
|| left.endOffset - right.endOffset
|
|
62
|
+
|| binaryCompare(left.qualifiedName, right.qualifiedName);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function exactRegistration<T extends OwnerCandidate>(
|
|
66
|
+
candidates: T[],
|
|
67
|
+
callStart: number,
|
|
68
|
+
callEnd: number,
|
|
69
|
+
): T[] {
|
|
70
|
+
return candidates.filter((candidate) =>
|
|
71
|
+
candidate.kind === 'event_registration'
|
|
72
|
+
&& candidate.startOffset === callStart
|
|
73
|
+
&& candidate.endOffset === callEnd);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function selectCallOwner<T extends OwnerCandidate>(
|
|
77
|
+
candidates: readonly T[],
|
|
78
|
+
callStart: number,
|
|
79
|
+
callEnd: number,
|
|
80
|
+
preferExactRegistration = false,
|
|
81
|
+
): OwnerSelection<T> {
|
|
82
|
+
const contained = candidates.filter((candidate) =>
|
|
83
|
+
isExecutableOwnerKind(candidate.kind)
|
|
84
|
+
&& containsHalfOpen(
|
|
85
|
+
candidate.startOffset, candidate.endOffset, callStart, callEnd,
|
|
86
|
+
));
|
|
87
|
+
const eligible = preferExactRegistration
|
|
88
|
+
? exactRegistration(contained, callStart, callEnd)
|
|
89
|
+
: contained;
|
|
90
|
+
const pool = eligible;
|
|
91
|
+
if (pool.length === 0) return { status: 'none', eligibleCount: 0 };
|
|
92
|
+
const ordered = [...pool].sort(compareOwners);
|
|
93
|
+
const first = ordered[0];
|
|
94
|
+
if (!first) return { status: 'none', eligibleCount: 0 };
|
|
95
|
+
const duplicates = ordered.filter((candidate) =>
|
|
96
|
+
ownerIdentity(candidate) === ownerIdentity(first));
|
|
97
|
+
if (duplicates.length > 1)
|
|
98
|
+
return { status: 'ambiguous', eligibleCount: pool.length };
|
|
99
|
+
return { status: 'resolved', owner: first, eligibleCount: pool.length };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function executableSymbolCandidates(
|
|
103
|
+
symbols: readonly ExecutableSymbolFact[],
|
|
104
|
+
sourceFile: string,
|
|
105
|
+
): ExecutableSymbolFact[] {
|
|
106
|
+
return symbols.filter((symbol) =>
|
|
107
|
+
symbol.sourceFile === sourceFile && isExecutableOwnerKind(symbol.kind));
|
|
108
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type {
|
|
3
|
+
ExecutableSymbolFact,
|
|
4
|
+
HandlerReferenceStatus,
|
|
5
|
+
SymbolCallFact,
|
|
6
|
+
} from '../types.js';
|
|
7
|
+
import {
|
|
8
|
+
collectSymbolImportBindings,
|
|
9
|
+
symbolImportReference,
|
|
10
|
+
type SymbolImportBinding,
|
|
11
|
+
type SymbolImportReference,
|
|
12
|
+
} from './002-symbol-import-bindings.js';
|
|
13
|
+
import type { ClassifiedOutboundCall } from './outbound-call-parser.js';
|
|
14
|
+
import {
|
|
15
|
+
localSymbolTarget,
|
|
16
|
+
type LocalSymbolTargetIdentity,
|
|
17
|
+
} from './016-local-symbol-reference.js';
|
|
18
|
+
|
|
19
|
+
interface HandlerTarget {
|
|
20
|
+
calleeExpression: string;
|
|
21
|
+
calleeLocalName: string;
|
|
22
|
+
importSource?: string;
|
|
23
|
+
relation: string;
|
|
24
|
+
referenceShape: string;
|
|
25
|
+
wrapperFunction?: string;
|
|
26
|
+
importBinding?: SymbolImportReference;
|
|
27
|
+
localTargetIdentity?: LocalSymbolTargetIdentity;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface HandlerClassification {
|
|
31
|
+
status: HandlerReferenceStatus;
|
|
32
|
+
reason?: string;
|
|
33
|
+
referenceShape: string;
|
|
34
|
+
target?: HandlerTarget;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function lineOf(source: ts.SourceFile, node: ts.Node): number {
|
|
38
|
+
return source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function lineAt(source: ts.SourceFile, position: number): number {
|
|
42
|
+
return source.getLineAndCharacterOfPosition(position).line + 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function importRelation(binding: SymbolImportReference): string {
|
|
46
|
+
if (binding.moduleKind === 'package') return 'package_import';
|
|
47
|
+
return binding.referenceShape === 'namespace_member'
|
|
48
|
+
? 'relative_import_namespace_member'
|
|
49
|
+
: 'relative_import';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function directTarget(
|
|
53
|
+
expression: ts.Expression,
|
|
54
|
+
source: ts.SourceFile,
|
|
55
|
+
imports: readonly SymbolImportBinding[],
|
|
56
|
+
symbols: readonly ExecutableSymbolFact[],
|
|
57
|
+
): HandlerTarget | undefined {
|
|
58
|
+
const imported = symbolImportReference(expression, imports);
|
|
59
|
+
if (imported) return importedTarget(expression, source, imported);
|
|
60
|
+
if (ts.isIdentifier(expression)) {
|
|
61
|
+
const exact = localSymbolTarget(expression, source, symbols);
|
|
62
|
+
return {
|
|
63
|
+
calleeExpression: expression.text,
|
|
64
|
+
calleeLocalName: expression.text,
|
|
65
|
+
relation: exact
|
|
66
|
+
? 'indexed_local_symbol'
|
|
67
|
+
: 'indexed_local_symbol_unproven',
|
|
68
|
+
referenceShape: 'identifier',
|
|
69
|
+
localTargetIdentity: exact,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return propertyTarget(expression, source, symbols);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function importedTarget(
|
|
76
|
+
expression: ts.Expression,
|
|
77
|
+
source: ts.SourceFile,
|
|
78
|
+
binding: SymbolImportReference,
|
|
79
|
+
): HandlerTarget {
|
|
80
|
+
return {
|
|
81
|
+
calleeExpression: expression.getText(source),
|
|
82
|
+
calleeLocalName: binding.requestedPublicName,
|
|
83
|
+
importSource: binding.rawModuleSpecifier,
|
|
84
|
+
relation: importRelation(binding),
|
|
85
|
+
referenceShape: binding.referenceShape,
|
|
86
|
+
importBinding: binding,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function propertyTarget(
|
|
91
|
+
expression: ts.Expression,
|
|
92
|
+
source: ts.SourceFile,
|
|
93
|
+
symbols: readonly ExecutableSymbolFact[],
|
|
94
|
+
): HandlerTarget | undefined {
|
|
95
|
+
if (!ts.isPropertyAccessExpression(expression) || expression.questionDotToken
|
|
96
|
+
|| !ts.isIdentifier(expression.expression)) return undefined;
|
|
97
|
+
const exact = localSymbolTarget(expression, source, symbols);
|
|
98
|
+
return {
|
|
99
|
+
calleeExpression: expression.getText(source),
|
|
100
|
+
calleeLocalName: `${expression.expression.text}.${expression.name.text}`,
|
|
101
|
+
relation: exact
|
|
102
|
+
? 'indexed_local_symbol'
|
|
103
|
+
: 'indexed_local_symbol_unproven',
|
|
104
|
+
referenceShape: 'static_member',
|
|
105
|
+
localTargetIdentity: exact,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function unsupportedClassification(
|
|
110
|
+
status: Exclude<HandlerReferenceStatus, 'role_required'>,
|
|
111
|
+
reason: string,
|
|
112
|
+
referenceShape: string,
|
|
113
|
+
): HandlerClassification {
|
|
114
|
+
return { status, reason, referenceShape };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function classifyHandlerReference(
|
|
118
|
+
expression: ts.Expression | undefined,
|
|
119
|
+
source: ts.SourceFile,
|
|
120
|
+
imports = collectSymbolImportBindings(source),
|
|
121
|
+
symbols: readonly ExecutableSymbolFact[] = [],
|
|
122
|
+
): HandlerClassification {
|
|
123
|
+
if (!expression)
|
|
124
|
+
return unsupportedClassification(
|
|
125
|
+
'missing_argument', 'handler_argument_missing', 'missing',
|
|
126
|
+
);
|
|
127
|
+
if (ts.isArrowFunction(expression) || ts.isFunctionExpression(expression))
|
|
128
|
+
return unsupportedClassification(
|
|
129
|
+
'unsupported_inline', 'inline_handler_body_not_indexed', 'inline_callback',
|
|
130
|
+
);
|
|
131
|
+
const direct = directTarget(expression, source, imports, symbols);
|
|
132
|
+
if (direct)
|
|
133
|
+
return { status: 'role_required', referenceShape: direct.referenceShape, target: direct };
|
|
134
|
+
if (!ts.isCallExpression(expression))
|
|
135
|
+
return unsupportedClassification(
|
|
136
|
+
'unsupported_reference_shape', 'handler_reference_shape_unsupported',
|
|
137
|
+
'unsupported_expression',
|
|
138
|
+
);
|
|
139
|
+
return classifyWrapperReference(expression, source, imports, symbols);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function classifyWrapperReference(
|
|
143
|
+
expression: ts.CallExpression,
|
|
144
|
+
source: ts.SourceFile,
|
|
145
|
+
imports: readonly SymbolImportBinding[],
|
|
146
|
+
symbols: readonly ExecutableSymbolFact[],
|
|
147
|
+
): HandlerClassification {
|
|
148
|
+
if (expression.questionDotToken || expression.arguments.length !== 1)
|
|
149
|
+
return unsupportedClassification(
|
|
150
|
+
'unsupported_wrapper', 'wrapper_requires_one_reference', 'wrapper_call',
|
|
151
|
+
);
|
|
152
|
+
const inner = expression.arguments[0];
|
|
153
|
+
const target = inner
|
|
154
|
+
? directTarget(inner, source, imports, symbols)
|
|
155
|
+
: undefined;
|
|
156
|
+
if (!target)
|
|
157
|
+
return unsupportedClassification(
|
|
158
|
+
'unsupported_wrapper', 'wrapper_reference_shape_unsupported', 'wrapper_call',
|
|
159
|
+
);
|
|
160
|
+
return {
|
|
161
|
+
status: 'role_required',
|
|
162
|
+
referenceShape: `wrapped_${target.referenceShape}`,
|
|
163
|
+
target: { ...target, wrapperFunction: expression.expression.getText(source) },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function eventSymbol(
|
|
168
|
+
source: ts.SourceFile,
|
|
169
|
+
classified: ClassifiedOutboundCall,
|
|
170
|
+
): ExecutableSymbolFact {
|
|
171
|
+
const node = classified.node;
|
|
172
|
+
const eventName = classified.fact.eventNameExpr ?? '';
|
|
173
|
+
const line = lineOf(source, node);
|
|
174
|
+
const safeName = eventName.replace(/[^A-Za-z0-9_$-]/g, '_');
|
|
175
|
+
const name = `event:${safeName}:${line}`;
|
|
176
|
+
const receiver = classified.fact.evidence?.receiver;
|
|
177
|
+
return {
|
|
178
|
+
kind: 'event_registration',
|
|
179
|
+
localName: name,
|
|
180
|
+
qualifiedName: `module:${classified.fact.sourceFile}#${name}`,
|
|
181
|
+
sourceFile: classified.fact.sourceFile,
|
|
182
|
+
startLine: line,
|
|
183
|
+
endLine: lineAt(source, node.getEnd() - 1),
|
|
184
|
+
startOffset: node.getStart(source),
|
|
185
|
+
endOffset: node.getEnd(),
|
|
186
|
+
exported: false,
|
|
187
|
+
importExportEvidence: {
|
|
188
|
+
source: 'synthetic_event_registration',
|
|
189
|
+
eventName,
|
|
190
|
+
registrationLine: line,
|
|
191
|
+
receiver: typeof receiver === 'string' ? receiver : undefined,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function eventCall(
|
|
197
|
+
classified: ClassifiedOutboundCall,
|
|
198
|
+
owner: ExecutableSymbolFact,
|
|
199
|
+
classification: HandlerClassification,
|
|
200
|
+
): SymbolCallFact | undefined {
|
|
201
|
+
const target = classification.target;
|
|
202
|
+
if (!target) return undefined;
|
|
203
|
+
return {
|
|
204
|
+
callerQualifiedName: owner.qualifiedName,
|
|
205
|
+
calleeExpression: target.calleeExpression,
|
|
206
|
+
calleeLocalName: target.calleeLocalName,
|
|
207
|
+
importSource: target.importSource,
|
|
208
|
+
sourceFile: classified.fact.sourceFile,
|
|
209
|
+
sourceLine: classified.fact.sourceLine,
|
|
210
|
+
callSiteStartOffset: classified.fact.callSiteStartOffset,
|
|
211
|
+
callSiteEndOffset: classified.fact.callSiteEndOffset,
|
|
212
|
+
callRole: 'event_subscribe_handler',
|
|
213
|
+
evidence: {
|
|
214
|
+
relation: target.relation,
|
|
215
|
+
caller: owner.qualifiedName,
|
|
216
|
+
targetName: target.calleeLocalName,
|
|
217
|
+
referenceShape: classification.referenceShape,
|
|
218
|
+
...(target.importBinding ? { importBinding: target.importBinding } : {}),
|
|
219
|
+
...(target.localTargetIdentity
|
|
220
|
+
? { localTargetIdentity: target.localTargetIdentity }
|
|
221
|
+
: {}),
|
|
222
|
+
...(target.wrapperFunction ? { wrapperFunction: target.wrapperFunction } : {}),
|
|
223
|
+
factOrigin: 'event_subscribe_handler_reference',
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function enrichSubscription(
|
|
229
|
+
source: ts.SourceFile,
|
|
230
|
+
classified: ClassifiedOutboundCall,
|
|
231
|
+
symbols: readonly ExecutableSymbolFact[],
|
|
232
|
+
): { classified: ClassifiedOutboundCall; classification: HandlerClassification } {
|
|
233
|
+
const classification = classifyHandlerReference(
|
|
234
|
+
classified.node.arguments[1], source, undefined, symbols,
|
|
235
|
+
);
|
|
236
|
+
const evidence = {
|
|
237
|
+
...(classified.fact.evidence ?? {}),
|
|
238
|
+
handlerReferenceStatus: classification.status,
|
|
239
|
+
...(classification.reason
|
|
240
|
+
? { handlerReferenceReason: classification.reason }
|
|
241
|
+
: {}),
|
|
242
|
+
handlerReferenceShape: classification.referenceShape,
|
|
243
|
+
};
|
|
244
|
+
return {
|
|
245
|
+
classified: { ...classified, fact: { ...classified.fact, evidence } },
|
|
246
|
+
classification,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function reconcileEventSubscriptions(
|
|
251
|
+
source: ts.SourceFile,
|
|
252
|
+
classifications: readonly ClassifiedOutboundCall[],
|
|
253
|
+
symbols: readonly ExecutableSymbolFact[],
|
|
254
|
+
calls: readonly SymbolCallFact[],
|
|
255
|
+
): {
|
|
256
|
+
classifications: ClassifiedOutboundCall[];
|
|
257
|
+
symbols: ExecutableSymbolFact[];
|
|
258
|
+
calls: SymbolCallFact[];
|
|
259
|
+
} {
|
|
260
|
+
const subscriptions = classifications.filter(
|
|
261
|
+
(item) => item.fact.callType === 'async_subscribe',
|
|
262
|
+
).map((item) => enrichSubscription(source, item, symbols));
|
|
263
|
+
const eventSymbols = subscriptions.map((item) => eventSymbol(source, item.classified));
|
|
264
|
+
const eventCalls = subscriptions.flatMap((item, index) => {
|
|
265
|
+
const owner = eventSymbols[index];
|
|
266
|
+
const call = owner
|
|
267
|
+
? eventCall(item.classified, owner, item.classification)
|
|
268
|
+
: undefined;
|
|
269
|
+
return call ? [call] : [];
|
|
270
|
+
});
|
|
271
|
+
return {
|
|
272
|
+
classifications: classifications.map((item) =>
|
|
273
|
+
subscriptions.find((candidate) => candidate.classified.node === item.node)
|
|
274
|
+
?.classified ?? item),
|
|
275
|
+
symbols: [...symbols.filter((item) => item.kind !== 'event_registration'), ...eventSymbols],
|
|
276
|
+
calls: [
|
|
277
|
+
...calls.filter((item) => item.callRole !== 'event_subscribe_handler'),
|
|
278
|
+
...eventCalls,
|
|
279
|
+
],
|
|
280
|
+
};
|
|
281
|
+
}
|