@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +20 -8
  3. package/TECHNICAL-NOTE.md +27 -0
  4. package/dist/{chunk-ZQABU7MR.js → chunk-AEM4JY22.js} +12676 -6714
  5. package/dist/chunk-AEM4JY22.js.map +1 -0
  6. package/dist/cli.js +2325 -413
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.ts +61 -2
  9. package/dist/index.js +1 -1
  10. package/package.json +1 -1
  11. package/src/cli/003-doctor-package-resolution.ts +68 -0
  12. package/src/cli/doctor.ts +25 -14
  13. package/src/db/000-call-fact-repository.ts +475 -342
  14. package/src/db/001-fact-lifecycle.ts +60 -30
  15. package/src/db/002-fact-json-inventory.ts +169 -0
  16. package/src/db/003-current-fact-semantics.ts +698 -0
  17. package/src/db/004-package-target-invalidation.ts +173 -0
  18. package/src/db/005-schema-structure.ts +201 -0
  19. package/src/db/006-relative-symbol-resolution.ts +443 -0
  20. package/src/db/007-package-fact-semantics.ts +573 -0
  21. package/src/db/008-relative-fact-semantics.ts +207 -0
  22. package/src/db/009-binding-fact-semantics.ts +347 -0
  23. package/src/db/010-package-symbol-surface-semantics.ts +320 -0
  24. package/src/db/011-symbol-call-semantics.ts +144 -0
  25. package/src/db/012-binding-reference-proof.ts +264 -0
  26. package/src/db/migrations.ts +16 -3
  27. package/src/db/repositories.ts +113 -6
  28. package/src/db/schema.ts +4 -2
  29. package/src/index.ts +12 -0
  30. package/src/indexer/repository-indexer.ts +72 -11
  31. package/src/indexer/workspace-indexer.ts +123 -32
  32. package/src/linker/003-package-import-symbol-resolver.ts +363 -131
  33. package/src/linker/004-event-subscription-handler-linker.ts +2 -0
  34. package/src/linker/005-odata-path-structure.ts +371 -0
  35. package/src/linker/cross-repo-linker.ts +2 -1
  36. package/src/linker/odata-path-normalizer.ts +273 -180
  37. package/src/linker/service-resolver.ts +197 -77
  38. package/src/parsers/002-symbol-import-bindings.ts +516 -0
  39. package/src/parsers/003-package-public-surface.ts +661 -0
  40. package/src/parsers/004-fact-identity.ts +108 -0
  41. package/src/parsers/005-event-subscription-facts.ts +281 -0
  42. package/src/parsers/006-binding-identity.ts +343 -0
  43. package/src/parsers/007-source-fact-reconciliation.ts +105 -0
  44. package/src/parsers/008-package-surface-publication.ts +82 -0
  45. package/src/parsers/009-symbol-call-facts.ts +528 -0
  46. package/src/parsers/010-package-public-surface-analysis.ts +352 -0
  47. package/src/parsers/011-binding-lexical-scope.ts +583 -0
  48. package/src/parsers/012-package-fact-contract.ts +306 -0
  49. package/src/parsers/013-executable-body-eligibility.ts +35 -0
  50. package/src/parsers/014-service-binding-helper-flow.ts +240 -0
  51. package/src/parsers/015-service-binding-collector.ts +693 -0
  52. package/src/parsers/016-local-symbol-reference.ts +261 -0
  53. package/src/parsers/017-symbol-derived-contexts.ts +268 -0
  54. package/src/parsers/018-package-commonjs-syntax.ts +142 -0
  55. package/src/parsers/019-binding-assignment-targets.ts +76 -0
  56. package/src/parsers/020-stable-local-value.ts +217 -0
  57. package/src/parsers/021-binding-visibility.ts +152 -0
  58. package/src/parsers/operation-path-analysis.ts +6 -1
  59. package/src/parsers/outbound-call-parser.ts +19 -6
  60. package/src/parsers/package-json-parser.ts +45 -3
  61. package/src/parsers/service-binding-parser-helpers.ts +86 -15
  62. package/src/parsers/service-binding-parser.ts +147 -597
  63. package/src/parsers/symbol-parser.ts +482 -353
  64. package/src/trace/002-trace-diagnostics.ts +36 -1
  65. package/src/trace/016-compact-projector.ts +16 -17
  66. package/src/trace/020-compact-field-projection.ts +48 -37
  67. package/src/trace/021-compact-decision-normalization.ts +105 -0
  68. package/src/trace/022-trace-fact-preflight.ts +21 -0
  69. package/src/trace/023-nested-event-scopes.ts +23 -0
  70. package/src/trace/024-compact-observation-decision.ts +76 -0
  71. package/src/trace/025-trace-implementation-scope.ts +123 -0
  72. package/src/trace/026-trace-start-scope.ts +335 -0
  73. package/src/trace/027-trace-scope-execution.ts +566 -0
  74. package/src/trace/028-trace-operation-execution.ts +336 -0
  75. package/src/trace/029-trace-start-implementation.ts +172 -0
  76. package/src/trace/trace-engine.ts +122 -624
  77. package/src/types.ts +56 -0
  78. package/src/utils/001-placeholders.ts +188 -10
  79. package/src/version.ts +1 -1
  80. package/dist/chunk-ZQABU7MR.js.map +0 -1
@@ -0,0 +1,528 @@
1
+ import ts from 'typescript';
2
+ import type {
3
+ ExecutableSymbolFact,
4
+ SymbolCallFact,
5
+ } from '../types.js';
6
+ import {
7
+ executableSymbolCandidates,
8
+ selectCallOwner,
9
+ } from './004-fact-identity.js';
10
+ import {
11
+ derivedMemberImportReference,
12
+ identifierMatchesDeclaration,
13
+ symbolImportReference,
14
+ type SymbolImportBinding,
15
+ type SymbolImportReference,
16
+ } from './002-symbol-import-bindings.js';
17
+ import {
18
+ localSymbolTarget,
19
+ type LocalSymbolTargetIdentity,
20
+ } from './016-local-symbol-reference.js';
21
+
22
+ export interface SymbolCallProxy {
23
+ importSource: string;
24
+ importBinding: SymbolImportReference;
25
+ factory: string;
26
+ variableName: string;
27
+ declarationStartOffset: number;
28
+ declarationEndOffset: number;
29
+ }
30
+
31
+ export interface SymbolClassInstance {
32
+ className: string;
33
+ importSource?: string;
34
+ importBinding?: SymbolImportReference;
35
+ propertyName?: string;
36
+ declarationStartOffset: number;
37
+ declarationEndOffset: number;
38
+ containerStartOffset?: number;
39
+ containerEndOffset?: number;
40
+ }
41
+
42
+ interface CalleeName {
43
+ expression: string;
44
+ local?: string;
45
+ member?: string;
46
+ receiver?: string;
47
+ }
48
+
49
+ interface CallCollection {
50
+ source: ts.SourceFile;
51
+ sourceFile: string;
52
+ symbols: readonly ExecutableSymbolFact[];
53
+ imports: ReadonlyMap<string, string>;
54
+ importBindings: readonly SymbolImportBinding[];
55
+ proxies: ReadonlyMap<string, readonly SymbolCallProxy[]>;
56
+ instances: ReadonlyMap<string, readonly SymbolClassInstance[]>;
57
+ }
58
+
59
+ interface CallContext {
60
+ proxy?: SymbolCallProxy;
61
+ instance?: SymbolClassInstance;
62
+ reference?: SymbolImportReference;
63
+ localTarget?: LocalSymbolTargetIdentity;
64
+ }
65
+
66
+ const commonTerminalMembers = new Set([
67
+ 'push', 'includes', 'find', 'findIndex', 'map', 'filter', 'reduce',
68
+ 'forEach', 'some', 'every', 'toUpperCase', 'toLowerCase', 'trim',
69
+ 'split', 'join', 'get', 'set', 'has',
70
+ ]);
71
+ const loggerMembers = new Set([
72
+ 'trace', 'debug', 'info', 'warn', 'error', 'fatal', 'log',
73
+ ]);
74
+ const globalObjects = new Set([
75
+ 'JSON', 'Object', 'Array', 'String', 'Number', 'Boolean', 'Math', 'Date',
76
+ 'Promise', 'Reflect',
77
+ ]);
78
+ const capDslRoots = new Set([
79
+ 'SELECT', 'INSERT', 'UPSERT', 'UPDATE', 'DELETE',
80
+ ]);
81
+ const requestHelpers = new Set([
82
+ 'reject', 'error', 'info', 'warn', 'notify',
83
+ ]);
84
+ const transportMembers = new Set([
85
+ 'emit', 'publish', 'send', 'on',
86
+ ]);
87
+ const cdsFrameworkPrefixes = [
88
+ 'cds.connect.', 'cds.services.', 'cds.parse.',
89
+ ];
90
+
91
+ export function symbolCallName(expr: ts.Expression): CalleeName {
92
+ if (ts.isIdentifier(expr)) return { expression: expr.text, local: expr.text };
93
+ if (!ts.isPropertyAccessExpression(expr))
94
+ return { expression: expr.getText() };
95
+ const left = expr.expression.getText();
96
+ return {
97
+ expression: expr.getText(),
98
+ local: left === 'this' ? undefined : left.split('.')[0],
99
+ member: expr.name.text,
100
+ receiver: left,
101
+ };
102
+ }
103
+
104
+ function hasSetMember(
105
+ value: string | undefined,
106
+ values: ReadonlySet<string>,
107
+ ): boolean {
108
+ return value ? values.has(value) : false;
109
+ }
110
+
111
+ function cdsFrameworkCall(callee: CalleeName): boolean {
112
+ return callee.expression === 'cds.run'
113
+ || cdsFrameworkPrefixes.some((prefix) =>
114
+ callee.expression.startsWith(prefix));
115
+ }
116
+
117
+ function requestHelperCall(callee: CalleeName): boolean {
118
+ return callee.local === 'req'
119
+ && hasSetMember(callee.member, requestHelpers);
120
+ }
121
+
122
+ function ignoredFrameworkCall(callee: CalleeName): boolean {
123
+ const checks = [
124
+ hasSetMember(callee.local, capDslRoots),
125
+ cdsFrameworkCall(callee),
126
+ requestHelperCall(callee),
127
+ hasSetMember(callee.member, transportMembers),
128
+ hasSetMember(callee.local, globalObjects),
129
+ callee.expression.startsWith('new Date().'),
130
+ ];
131
+ return checks.some(Boolean);
132
+ }
133
+
134
+ function argumentEvidence(
135
+ args: ts.NodeArray<ts.Expression>,
136
+ ): Array<Record<string, unknown>> {
137
+ return args.map((arg) => {
138
+ if (ts.isIdentifier(arg)) return { kind: 'identifier', name: arg.text };
139
+ if (ts.isArrayLiteralExpression(arg)) return {
140
+ kind: 'array_literal',
141
+ elements: arg.elements.flatMap((item, index) =>
142
+ ts.isIdentifier(item)
143
+ ? [{ index, kind: 'identifier', name: item.text }]
144
+ : []),
145
+ };
146
+ if (ts.isObjectLiteralExpression(arg))
147
+ return objectArgumentEvidence(arg);
148
+ return { kind: 'unsupported', expression: arg.getText() };
149
+ });
150
+ }
151
+
152
+ function objectArgumentEvidence(
153
+ argument: ts.ObjectLiteralExpression,
154
+ ): Record<string, unknown> {
155
+ const properties = argument.properties.flatMap((property) => {
156
+ if (ts.isShorthandPropertyAssignment(property))
157
+ return [{
158
+ kind: 'shorthand',
159
+ property: property.name.text,
160
+ argument: property.name.text,
161
+ }];
162
+ if (!ts.isPropertyAssignment(property)
163
+ || !ts.isIdentifier(property.initializer)) return [];
164
+ const name = propertyName(property.name);
165
+ return name ? [{
166
+ kind: 'property_assignment',
167
+ property: name,
168
+ argument: property.initializer.text,
169
+ }] : [];
170
+ });
171
+ return { kind: 'object_literal', properties };
172
+ }
173
+
174
+ function propertyName(name: ts.PropertyName): string | undefined {
175
+ return ts.isIdentifier(name) || ts.isStringLiteralLike(name)
176
+ || ts.isNumericLiteral(name) ? name.text : undefined;
177
+ }
178
+
179
+ function exactCaller(
180
+ collection: CallCollection,
181
+ node: ts.CallExpression,
182
+ ): ExecutableSymbolFact | undefined {
183
+ const selected = selectCallOwner(
184
+ executableSymbolCandidates(collection.symbols, collection.sourceFile),
185
+ node.getStart(collection.source),
186
+ node.getEnd(),
187
+ );
188
+ if (selected.status === 'ambiguous')
189
+ throw new Error('invalid_prepared_repository_snapshot:symbol_call_owner_ambiguous');
190
+ return selected.owner;
191
+ }
192
+
193
+ function relation(
194
+ reference: SymbolImportReference | undefined,
195
+ instance: SymbolClassInstance | undefined,
196
+ proxy: SymbolCallProxy | undefined,
197
+ localTarget: LocalSymbolTargetIdentity | undefined,
198
+ provenThisMethod: boolean,
199
+ ): string {
200
+ const derived = derivedRelation(instance, proxy);
201
+ if (derived) return derived;
202
+ if (reference?.moduleKind === 'package') return 'package_import';
203
+ if (reference?.referenceShape === 'namespace_member')
204
+ return 'relative_import_namespace_member';
205
+ if (reference) return 'relative_import';
206
+ if (localTarget) return 'indexed_local_symbol';
207
+ return provenThisMethod ? 'indexed_this_method' : 'indexed_local_symbol';
208
+ }
209
+
210
+ function derivedRelation(
211
+ instance: SymbolClassInstance | undefined,
212
+ proxy: SymbolCallProxy | undefined,
213
+ ): string | undefined {
214
+ if (instance?.importBinding?.moduleKind === 'package')
215
+ return 'package_import_derived_member';
216
+ if (instance) return 'class_instance_method';
217
+ if (proxy?.importBinding.moduleKind === 'package')
218
+ return 'package_import_derived_member';
219
+ return proxy ? 'relative_import_proxy_member' : undefined;
220
+ }
221
+
222
+ function targetName(
223
+ callee: CalleeName,
224
+ reference: SymbolImportReference | undefined,
225
+ instance: SymbolClassInstance | undefined,
226
+ proxy: SymbolCallProxy | undefined,
227
+ ): string | undefined {
228
+ if (instance && callee.member)
229
+ return `${instance.className}.${callee.member}`;
230
+ if (proxy && callee.member) return callee.member;
231
+ if (callee.receiver === 'this') return callee.member;
232
+ if (reference) return reference.requestedPublicName;
233
+ return callee.member && callee.local
234
+ ? `${callee.local}.${callee.member}`
235
+ : callee.local;
236
+ }
237
+
238
+ function callFact(
239
+ collection: CallCollection,
240
+ node: ts.CallExpression,
241
+ ): SymbolCallFact | undefined {
242
+ const caller = exactCaller(collection, node);
243
+ if (!caller) return undefined;
244
+ const callee = symbolCallName(node.expression);
245
+ const proxy = callProxy(collection, node);
246
+ const instance = callInstance(collection, node, callee);
247
+ const reference = callImportReference(
248
+ collection, node.expression, callee, instance, proxy,
249
+ );
250
+ const localTarget = reference || instance || proxy
251
+ ? undefined
252
+ : localSymbolTarget(node.expression, collection.source, collection.symbols);
253
+ return retainedCallFact(collection, node, caller, callee, {
254
+ proxy, instance, reference, localTarget,
255
+ });
256
+ }
257
+
258
+ function expressionRoot(
259
+ expression: ts.Expression,
260
+ ): ts.Identifier | undefined {
261
+ let current = expression;
262
+ while (ts.isPropertyAccessExpression(current))
263
+ current = current.expression;
264
+ return ts.isIdentifier(current) ? current : undefined;
265
+ }
266
+
267
+ function exactDeclaredContext<T extends {
268
+ declarationStartOffset: number;
269
+ declarationEndOffset: number;
270
+ }>(
271
+ identifier: ts.Identifier,
272
+ candidates: readonly T[],
273
+ ): T | undefined {
274
+ const matches = candidates.filter((candidate) =>
275
+ identifierMatchesDeclaration(
276
+ identifier,
277
+ candidate.declarationStartOffset,
278
+ candidate.declarationEndOffset,
279
+ ));
280
+ return matches.length === 1 ? matches[0] : undefined;
281
+ }
282
+
283
+ function callProxy(
284
+ collection: CallCollection,
285
+ node: ts.CallExpression,
286
+ ): SymbolCallProxy | undefined {
287
+ const root = expressionRoot(node.expression);
288
+ if (!root) return undefined;
289
+ return exactDeclaredContext(
290
+ root, collection.proxies.get(root.text) ?? [],
291
+ );
292
+ }
293
+
294
+ function callInstance(
295
+ collection: CallCollection,
296
+ node: ts.CallExpression,
297
+ callee: CalleeName,
298
+ ): SymbolClassInstance | undefined {
299
+ const root = expressionRoot(node.expression);
300
+ if (root && root.text !== 'this') {
301
+ const exact = exactDeclaredContext(
302
+ root, collection.instances.get(root.text) ?? [],
303
+ );
304
+ if (exact) return exact;
305
+ }
306
+ return callee.receiver
307
+ ? thisPropertyInstance(collection, node, callee.receiver)
308
+ : undefined;
309
+ }
310
+
311
+ function enclosingClass(node: ts.Node): ts.ClassLikeDeclaration | undefined {
312
+ let current: ts.Node | undefined = node.parent;
313
+ while (current && !ts.isSourceFile(current)) {
314
+ if (ts.isClassLike(current)) return current;
315
+ current = current.parent;
316
+ }
317
+ return undefined;
318
+ }
319
+
320
+ function thisPropertyInstance(
321
+ collection: CallCollection,
322
+ node: ts.CallExpression,
323
+ receiver: string,
324
+ ): SymbolClassInstance | undefined {
325
+ if (!receiver.startsWith('this.')) return undefined;
326
+ const container = enclosingClass(node);
327
+ if (!container) return undefined;
328
+ const start = container.getStart(collection.source);
329
+ const end = container.getEnd();
330
+ const matches = (collection.instances.get(receiver) ?? []).filter(
331
+ (candidate) => candidate.containerStartOffset === start
332
+ && candidate.containerEndOffset === end,
333
+ );
334
+ return matches.length === 1 ? matches[0] : undefined;
335
+ }
336
+
337
+ function callImportReference(
338
+ collection: CallCollection,
339
+ expression: ts.Expression,
340
+ callee: CalleeName,
341
+ instance: SymbolClassInstance | undefined,
342
+ proxy: SymbolCallProxy | undefined,
343
+ ): SymbolImportReference | undefined {
344
+ const direct = symbolImportReference(expression, collection.importBindings);
345
+ if (direct || !callee.member) return direct;
346
+ const inherited = instance?.importBinding ?? proxy?.importBinding;
347
+ return inherited
348
+ ? derivedMemberImportReference(inherited, callee.member)
349
+ : undefined;
350
+ }
351
+
352
+ function retainedCallFact(
353
+ collection: CallCollection,
354
+ node: ts.CallExpression,
355
+ caller: ExecutableSymbolFact,
356
+ callee: CalleeName,
357
+ context: CallContext,
358
+ ): SymbolCallFact | undefined {
359
+ const target = targetName(
360
+ callee, context.reference, context.instance, context.proxy,
361
+ );
362
+ const className = caller.qualifiedName.includes('.')
363
+ ? caller.qualifiedName.split('.')[0] : undefined;
364
+ const thisTarget = callee.receiver === 'this' && className && callee.member
365
+ ? `${className}.${callee.member}` : undefined;
366
+ const resolvedTarget = thisTarget ?? target;
367
+ if (!resolvedTarget
368
+ || !shouldRetainCall(
369
+ collection, callee, resolvedTarget, context, Boolean(thisTarget),
370
+ ))
371
+ return undefined;
372
+ return createCallFact(
373
+ collection, node, caller, callee, resolvedTarget, thisTarget, context,
374
+ );
375
+ }
376
+
377
+ function shouldRetainCall(
378
+ collection: CallCollection,
379
+ callee: CalleeName,
380
+ target: string | undefined,
381
+ context: CallContext,
382
+ provenThisMethod: boolean,
383
+ ): boolean {
384
+ if (!target || ignoredCall(callee)) return false;
385
+ const callables = new Set(collection.symbols.flatMap((symbol) =>
386
+ [symbol.localName, symbol.qualifiedName]));
387
+ return provenThisMethod
388
+ || Boolean(context.localTarget)
389
+ || callables.has(target) && Boolean(context.instance)
390
+ || hasImportedContext(context);
391
+ }
392
+
393
+ function ignoredCall(callee: CalleeName): boolean {
394
+ return loggerCall(callee) || terminalCall(callee)
395
+ || ignoredFrameworkCall(callee);
396
+ }
397
+
398
+ function loggerCall(callee: CalleeName): boolean {
399
+ if (callee.local === 'logger') return true;
400
+ if (callee.receiver?.endsWith('.logger')) return true;
401
+ return callee.expression.startsWith('this.logger.')
402
+ && hasSetMember(callee.member, loggerMembers);
403
+ }
404
+
405
+ function terminalCall(callee: CalleeName): boolean {
406
+ return hasSetMember(callee.member, commonTerminalMembers)
407
+ || hasSetMember(callee.member, loggerMembers);
408
+ }
409
+
410
+ function hasImportedContext(context: CallContext): boolean {
411
+ return Boolean(context.reference ?? context.proxy ?? context.instance);
412
+ }
413
+
414
+ function createCallFact(
415
+ collection: CallCollection,
416
+ node: ts.CallExpression,
417
+ caller: ExecutableSymbolFact,
418
+ callee: CalleeName,
419
+ target: string,
420
+ thisTarget: string | undefined,
421
+ context: CallContext,
422
+ ): SymbolCallFact {
423
+ const importSource = context.instance?.importSource
424
+ ?? context.proxy?.importSource
425
+ ?? context.reference?.rawModuleSpecifier;
426
+ return {
427
+ callerQualifiedName: caller.qualifiedName,
428
+ calleeExpression: callee.expression,
429
+ calleeLocalName: target,
430
+ receiverLocalName: callee.member ? callee.local ?? callee.receiver : undefined,
431
+ importSource,
432
+ sourceFile: collection.sourceFile,
433
+ sourceLine: collection.source.getLineAndCharacterOfPosition(
434
+ node.getStart(collection.source),
435
+ ).line + 1,
436
+ callSiteStartOffset: node.getStart(collection.source),
437
+ callSiteEndOffset: node.getEnd(),
438
+ callRole: 'ordinary_call',
439
+ evidence: callEvidence(caller, callee, target, thisTarget, context, node),
440
+ };
441
+ }
442
+
443
+ function callEvidence(
444
+ caller: ExecutableSymbolFact,
445
+ callee: CalleeName,
446
+ target: string,
447
+ thisTarget: string | undefined,
448
+ context: CallContext,
449
+ node: ts.CallExpression,
450
+ ): Record<string, unknown> {
451
+ return {
452
+ relation: relation(
453
+ context.reference, context.instance, context.proxy,
454
+ context.localTarget, Boolean(thisTarget),
455
+ ),
456
+ caller: caller.qualifiedName,
457
+ targetName: target,
458
+ ...importReferenceEvidence(context),
459
+ ...instanceEvidence(context.instance, callee),
460
+ callArguments: argumentEvidence(node.arguments),
461
+ ...proxyEvidence(context.proxy),
462
+ ...(context.localTarget
463
+ ? { localTargetIdentity: context.localTarget }
464
+ : {}),
465
+ candidateStrategy: parserCandidateStrategy(context),
466
+ };
467
+ }
468
+
469
+ function importReferenceEvidence(
470
+ context: CallContext,
471
+ ): Record<string, unknown> {
472
+ if (!context.reference) return {};
473
+ const derived = context.instance ?? context.proxy;
474
+ return derived && context.reference.moduleKind === 'package'
475
+ ? { derivedImportBinding: context.reference }
476
+ : { importBinding: context.reference };
477
+ }
478
+
479
+ function instanceEvidence(
480
+ instance: SymbolClassInstance | undefined,
481
+ callee: CalleeName,
482
+ ): Record<string, unknown> {
483
+ if (!instance) return {};
484
+ return {
485
+ instanceVariable: instance.propertyName ?? callee.local,
486
+ className: instance.className,
487
+ methodName: callee.member,
488
+ classImportSource: instance.importSource,
489
+ classImportBinding: instance.importBinding,
490
+ };
491
+ }
492
+
493
+ function proxyEvidence(
494
+ proxy: SymbolCallProxy | undefined,
495
+ ): Record<string, unknown> {
496
+ if (!proxy) return {};
497
+ return {
498
+ proxyVariableName: proxy.variableName,
499
+ factory: proxy.factory,
500
+ factoryExpression: proxy.factory,
501
+ factoryImportSource: proxy.importSource,
502
+ factoryImportBinding: proxy.importBinding,
503
+ };
504
+ }
505
+
506
+ function parserCandidateStrategy(context: CallContext): string | undefined {
507
+ if (context.instance?.importSource)
508
+ return 'relative_import_class_instance_method';
509
+ if (context.instance) return 'same_file_class_instance_method';
510
+ return context.proxy
511
+ ? 'proxy_member_exact_export_or_unique_member'
512
+ : undefined;
513
+ }
514
+
515
+ export function collectSymbolCallFacts(
516
+ collection: CallCollection,
517
+ ): SymbolCallFact[] {
518
+ const calls: SymbolCallFact[] = [];
519
+ const visit = (node: ts.Node): void => {
520
+ if (ts.isCallExpression(node)) {
521
+ const call = callFact(collection, node);
522
+ if (call) calls.push(call);
523
+ }
524
+ ts.forEachChild(node, visit);
525
+ };
526
+ visit(collection.source);
527
+ return calls;
528
+ }