@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,443 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
2
|
+
import type { SymbolCallFact } from '../types.js';
|
|
3
|
+
import type { Db } from './connection.js';
|
|
4
|
+
|
|
5
|
+
export interface RelativeSymbolCallResolution {
|
|
6
|
+
id: number | null;
|
|
7
|
+
status: 'resolved' | 'ambiguous' | 'unresolved';
|
|
8
|
+
reason: string | null;
|
|
9
|
+
strategy: string;
|
|
10
|
+
candidateCount: number;
|
|
11
|
+
eligibleCandidateCount: number;
|
|
12
|
+
candidateSetComplete: boolean;
|
|
13
|
+
resolvedModulePath?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface TargetRow {
|
|
17
|
+
id: number;
|
|
18
|
+
kind: string;
|
|
19
|
+
sourceFile: string;
|
|
20
|
+
exported: boolean;
|
|
21
|
+
evidenceJson: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface RelativeModuleScope {
|
|
25
|
+
paths: Set<string>;
|
|
26
|
+
ambiguous: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface MappingTarget {
|
|
30
|
+
specifier?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface MappedExecutableRows {
|
|
34
|
+
rows: TargetRow[];
|
|
35
|
+
ambiguous: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const stripExtension = (value: string): string =>
|
|
39
|
+
value.replace(/\.(?:ts|tsx|js|jsx|cds)$/, '');
|
|
40
|
+
|
|
41
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
42
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
43
|
+
? value as Record<string, unknown>
|
|
44
|
+
: undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parsedRecord(value: string | null): Record<string, unknown> | undefined {
|
|
48
|
+
if (value === null) return undefined;
|
|
49
|
+
try {
|
|
50
|
+
return record(JSON.parse(value) as unknown);
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function targetRows(rows: Array<Record<string, unknown>>): TargetRow[] {
|
|
57
|
+
return rows.flatMap((row) =>
|
|
58
|
+
typeof row.id === 'number' && typeof row.kind === 'string'
|
|
59
|
+
&& typeof row.sourceFile === 'string'
|
|
60
|
+
&& typeof row.exported === 'number'
|
|
61
|
+
? [{
|
|
62
|
+
id: row.id,
|
|
63
|
+
kind: row.kind,
|
|
64
|
+
sourceFile: row.sourceFile,
|
|
65
|
+
exported: row.exported === 1,
|
|
66
|
+
evidenceJson: typeof row.evidenceJson === 'string'
|
|
67
|
+
? row.evidenceJson
|
|
68
|
+
: null,
|
|
69
|
+
}]
|
|
70
|
+
: []);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function relativeTargets(sourceFile: string, specifier: string): Set<string> {
|
|
74
|
+
const joined = stripExtension(posix.normalize(
|
|
75
|
+
posix.join(posix.dirname(sourceFile), specifier),
|
|
76
|
+
));
|
|
77
|
+
return new Set([joined, `${joined}/index`]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function relativeModuleScope(
|
|
81
|
+
db: Db,
|
|
82
|
+
repoId: number,
|
|
83
|
+
sourceFile: string,
|
|
84
|
+
specifier: string,
|
|
85
|
+
): RelativeModuleScope {
|
|
86
|
+
const requested = relativeTargets(sourceFile, specifier);
|
|
87
|
+
const rows = db.prepare(`SELECT relative_path relativePath FROM files
|
|
88
|
+
WHERE repo_id=? ORDER BY relative_path COLLATE BINARY`).all(repoId);
|
|
89
|
+
const matched = rows.flatMap((row) =>
|
|
90
|
+
typeof row.relativePath === 'string'
|
|
91
|
+
&& requested.has(stripExtension(row.relativePath))
|
|
92
|
+
? [stripExtension(row.relativePath)] : []);
|
|
93
|
+
return {
|
|
94
|
+
paths: new Set(matched),
|
|
95
|
+
ambiguous: matched.length > 1,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function scopedRows(
|
|
100
|
+
rows: readonly TargetRow[],
|
|
101
|
+
paths: ReadonlySet<string>,
|
|
102
|
+
): TargetRow[] {
|
|
103
|
+
return rows.filter((row) => paths.has(stripExtension(row.sourceFile)));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function bodyEligible(row: TargetRow): boolean {
|
|
107
|
+
const evidence = parsedRecord(row.evidenceJson);
|
|
108
|
+
const direct = record(evidence?.executableBodyEligibility);
|
|
109
|
+
const surface = record(evidence?.packagePublicSurface);
|
|
110
|
+
const packageBody = record(surface?.bodyEligibility);
|
|
111
|
+
const body = direct ?? packageBody;
|
|
112
|
+
return body?.eligible === true && body.reason === 'body_present';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function relativeBinding(
|
|
116
|
+
call: SymbolCallFact,
|
|
117
|
+
): Record<string, unknown> | undefined {
|
|
118
|
+
const binding = record(call.evidence.importBinding);
|
|
119
|
+
return binding?.moduleKind === 'relative'
|
|
120
|
+
&& binding.rawModuleSpecifier === call.importSource
|
|
121
|
+
? binding
|
|
122
|
+
: undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function importedHandlerMember(
|
|
126
|
+
call: SymbolCallFact,
|
|
127
|
+
binding = relativeBinding(call),
|
|
128
|
+
): boolean {
|
|
129
|
+
return call.callRole === 'event_subscribe_handler'
|
|
130
|
+
&& binding?.referenceShape === 'static_member'
|
|
131
|
+
&& typeof binding.importedName === 'string'
|
|
132
|
+
&& binding.requestedPublicName === call.calleeLocalName;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function handlerMemberPubliclyCarried(
|
|
136
|
+
row: TargetRow,
|
|
137
|
+
binding: Record<string, unknown>,
|
|
138
|
+
): boolean {
|
|
139
|
+
if (row.exported) return true;
|
|
140
|
+
const evidence = parsedRecord(row.evidenceJson);
|
|
141
|
+
return evidence?.source === 'exported_class_instance_member'
|
|
142
|
+
&& evidence.exportedClass === binding.importedName;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function requiresPublicClassMember(
|
|
146
|
+
relation: unknown,
|
|
147
|
+
call: SymbolCallFact,
|
|
148
|
+
binding: Record<string, unknown>,
|
|
149
|
+
): boolean {
|
|
150
|
+
return relation === 'class_instance_method'
|
|
151
|
+
|| importedHandlerMember(call, binding);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function resolved(
|
|
155
|
+
row: TargetRow,
|
|
156
|
+
strategy: string,
|
|
157
|
+
candidateCount: number,
|
|
158
|
+
resolvedModulePath: string,
|
|
159
|
+
): RelativeSymbolCallResolution {
|
|
160
|
+
return {
|
|
161
|
+
id: row.id,
|
|
162
|
+
status: 'resolved',
|
|
163
|
+
reason: null,
|
|
164
|
+
strategy,
|
|
165
|
+
candidateCount,
|
|
166
|
+
eligibleCandidateCount: 1,
|
|
167
|
+
candidateSetComplete: true,
|
|
168
|
+
resolvedModulePath,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function unresolved(
|
|
173
|
+
strategy: string,
|
|
174
|
+
reason: string,
|
|
175
|
+
candidateCount: number,
|
|
176
|
+
eligibleCandidateCount = 0,
|
|
177
|
+
candidateSetComplete = true,
|
|
178
|
+
): RelativeSymbolCallResolution {
|
|
179
|
+
return {
|
|
180
|
+
id: null,
|
|
181
|
+
status: 'unresolved',
|
|
182
|
+
reason,
|
|
183
|
+
strategy,
|
|
184
|
+
candidateCount,
|
|
185
|
+
eligibleCandidateCount,
|
|
186
|
+
candidateSetComplete,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function ambiguous(
|
|
191
|
+
strategy: string,
|
|
192
|
+
reason: string,
|
|
193
|
+
candidateCount: number,
|
|
194
|
+
eligibleCandidateCount: number,
|
|
195
|
+
): RelativeSymbolCallResolution {
|
|
196
|
+
return {
|
|
197
|
+
id: null,
|
|
198
|
+
status: 'ambiguous',
|
|
199
|
+
reason,
|
|
200
|
+
strategy,
|
|
201
|
+
candidateCount,
|
|
202
|
+
eligibleCandidateCount,
|
|
203
|
+
candidateSetComplete: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function uniqueRows(rows: readonly TargetRow[]): TargetRow[] {
|
|
208
|
+
return [...new Map(rows.map((row) => [row.id, row])).values()]
|
|
209
|
+
.sort((left, right) => left.id - right.id);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function executableResolution(
|
|
213
|
+
rows: readonly TargetRow[],
|
|
214
|
+
scoped: readonly TargetRow[],
|
|
215
|
+
modulePath: string | undefined,
|
|
216
|
+
strategy: string,
|
|
217
|
+
ambiguousReason: string,
|
|
218
|
+
): RelativeSymbolCallResolution {
|
|
219
|
+
const eligible = uniqueRows(scoped.filter(bodyEligible));
|
|
220
|
+
if (eligible.length === 1 && eligible[0] && modulePath)
|
|
221
|
+
return resolved(eligible[0], strategy, rows.length, modulePath);
|
|
222
|
+
if (eligible.length > 1) return ambiguous(
|
|
223
|
+
strategy, ambiguousReason, rows.length, eligible.length,
|
|
224
|
+
);
|
|
225
|
+
return unresolved(
|
|
226
|
+
strategy,
|
|
227
|
+
scoped.length > 0
|
|
228
|
+
? 'relative_import_requested_module_has_no_executable_body'
|
|
229
|
+
: 'relative_import_requested_module_has_no_target',
|
|
230
|
+
rows.length,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function qualifiedRows(
|
|
235
|
+
db: Db,
|
|
236
|
+
repoId: number,
|
|
237
|
+
call: SymbolCallFact,
|
|
238
|
+
): TargetRow[] {
|
|
239
|
+
return targetRows(db.prepare(`SELECT id,kind,source_file sourceFile,
|
|
240
|
+
exported,evidence_json evidenceJson FROM symbols
|
|
241
|
+
WHERE repo_id=? AND source_file<>? AND qualified_name=?
|
|
242
|
+
ORDER BY id`).all(repoId, call.sourceFile, call.calleeLocalName));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function exportedRows(
|
|
246
|
+
db: Db,
|
|
247
|
+
repoId: number,
|
|
248
|
+
call: SymbolCallFact,
|
|
249
|
+
): TargetRow[] {
|
|
250
|
+
return targetRows(db.prepare(`SELECT id,kind,source_file sourceFile,
|
|
251
|
+
exported,evidence_json evidenceJson FROM symbols
|
|
252
|
+
WHERE repo_id=? AND source_file<>? AND exported=1
|
|
253
|
+
AND (exported_name=? OR name=? OR qualified_name=?)
|
|
254
|
+
ORDER BY id`).all(
|
|
255
|
+
repoId, call.sourceFile,
|
|
256
|
+
call.calleeLocalName, call.calleeLocalName, call.calleeLocalName,
|
|
257
|
+
));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function proxyRows(
|
|
261
|
+
db: Db,
|
|
262
|
+
repoId: number,
|
|
263
|
+
call: SymbolCallFact,
|
|
264
|
+
): TargetRow[] {
|
|
265
|
+
return targetRows(db.prepare(`SELECT id,kind,source_file sourceFile,
|
|
266
|
+
exported,evidence_json evidenceJson FROM symbols
|
|
267
|
+
WHERE repo_id=? AND source_file<>?
|
|
268
|
+
AND (exported_name=? OR name=? OR qualified_name=?)
|
|
269
|
+
ORDER BY id`).all(
|
|
270
|
+
repoId, call.sourceFile,
|
|
271
|
+
call.calleeLocalName, call.calleeLocalName, call.calleeLocalName,
|
|
272
|
+
));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function mappingTarget(row: TargetRow): MappingTarget | undefined {
|
|
276
|
+
const evidence = parsedRecord(row.evidenceJson);
|
|
277
|
+
if (row.kind !== 'object_alias'
|
|
278
|
+
|| evidence?.source !== 'exported_object_shorthand') return undefined;
|
|
279
|
+
const specifier = evidence.targetImportSource;
|
|
280
|
+
return typeof specifier === 'string' ? { specifier } : {};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function mappingTargetScope(
|
|
284
|
+
db: Db,
|
|
285
|
+
repoId: number,
|
|
286
|
+
row: TargetRow,
|
|
287
|
+
): RelativeModuleScope | undefined {
|
|
288
|
+
const target = mappingTarget(row);
|
|
289
|
+
if (!target) return undefined;
|
|
290
|
+
return target.specifier
|
|
291
|
+
? relativeModuleScope(db, repoId, row.sourceFile, target.specifier)
|
|
292
|
+
: { paths: new Set([stripExtension(row.sourceFile)]), ambiguous: false };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function mappedExecutableRows(
|
|
296
|
+
db: Db,
|
|
297
|
+
repoId: number,
|
|
298
|
+
rows: readonly TargetRow[],
|
|
299
|
+
aliases: readonly TargetRow[],
|
|
300
|
+
): MappedExecutableRows {
|
|
301
|
+
const scopes = aliases.flatMap((alias) => {
|
|
302
|
+
const scope = mappingTargetScope(db, repoId, alias);
|
|
303
|
+
return scope ? [scope] : [];
|
|
304
|
+
});
|
|
305
|
+
if (scopes.some((scope) => scope.ambiguous))
|
|
306
|
+
return { rows: [], ambiguous: true };
|
|
307
|
+
const mapped = scopes.flatMap((scope) => {
|
|
308
|
+
return rows.filter((row) =>
|
|
309
|
+
row.kind !== 'object_alias' && bodyEligible(row)
|
|
310
|
+
&& scope.paths.has(stripExtension(row.sourceFile)));
|
|
311
|
+
});
|
|
312
|
+
return { rows: uniqueRows(mapped), ambiguous: false };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function proxyResolution(
|
|
316
|
+
db: Db,
|
|
317
|
+
repoId: number,
|
|
318
|
+
rows: readonly TargetRow[],
|
|
319
|
+
scope: RelativeModuleScope,
|
|
320
|
+
): RelativeSymbolCallResolution {
|
|
321
|
+
const scoped = scopedRows(rows, scope.paths);
|
|
322
|
+
const aliases = scoped.filter((row) => mappingTarget(row));
|
|
323
|
+
const mapped = mappedExecutableRows(db, repoId, rows, aliases);
|
|
324
|
+
if (mapped.ambiguous) return unresolved(
|
|
325
|
+
'proxy_member_exported_object_map',
|
|
326
|
+
'relative_import_module_resolution_ambiguous',
|
|
327
|
+
rows.length, 0, false,
|
|
328
|
+
);
|
|
329
|
+
const direct = scoped.filter((row) =>
|
|
330
|
+
row.kind !== 'object_alias' && row.exported && bodyEligible(row));
|
|
331
|
+
const eligible = uniqueRows([
|
|
332
|
+
...direct,
|
|
333
|
+
...mapped.rows,
|
|
334
|
+
]);
|
|
335
|
+
const modulePath = [...scope.paths][0];
|
|
336
|
+
const strategy = aliases.length > 0
|
|
337
|
+
? 'proxy_member_exported_object_map'
|
|
338
|
+
: 'relative_import_path_disambiguated';
|
|
339
|
+
if (eligible.length === 1 && eligible[0] && modulePath)
|
|
340
|
+
return resolved(eligible[0], strategy, rows.length, modulePath);
|
|
341
|
+
if (eligible.length > 1) return ambiguous(
|
|
342
|
+
'proxy_member_no_global_name_fallback',
|
|
343
|
+
'multiple_proxy_targets_in_requested_module',
|
|
344
|
+
rows.length,
|
|
345
|
+
eligible.length,
|
|
346
|
+
);
|
|
347
|
+
return unresolved(
|
|
348
|
+
'proxy_member_no_global_name_fallback',
|
|
349
|
+
scoped.length > 0
|
|
350
|
+
? 'relative_import_requested_module_has_no_executable_body'
|
|
351
|
+
: 'relative_import_requested_module_has_no_target',
|
|
352
|
+
rows.length,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function resolutionRows(
|
|
357
|
+
db: Db,
|
|
358
|
+
repoId: number,
|
|
359
|
+
call: SymbolCallFact,
|
|
360
|
+
relation: unknown,
|
|
361
|
+
): TargetRow[] {
|
|
362
|
+
if (relation === 'class_instance_method' || isAccessorCall(relation, call)
|
|
363
|
+
|| importedHandlerMember(call))
|
|
364
|
+
return qualifiedRows(
|
|
365
|
+
db, repoId, call,
|
|
366
|
+
);
|
|
367
|
+
if (relation === 'relative_import_proxy_member') return proxyRows(
|
|
368
|
+
db, repoId, call,
|
|
369
|
+
);
|
|
370
|
+
return exportedRows(db, repoId, call);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function isAccessorCall(relation: unknown, call: SymbolCallFact): boolean {
|
|
374
|
+
return relation === 'relative_import'
|
|
375
|
+
&& call.calleeExpression.includes('().')
|
|
376
|
+
&& String(call.calleeLocalName).includes('.');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function strategyFor(
|
|
380
|
+
relation: unknown,
|
|
381
|
+
call: SymbolCallFact,
|
|
382
|
+
candidateCount: number,
|
|
383
|
+
): string {
|
|
384
|
+
if (relation === 'class_instance_method')
|
|
385
|
+
return 'relative_import_class_instance_method';
|
|
386
|
+
if (relation === 'relative_import_namespace_member')
|
|
387
|
+
return 'relative_import_namespace_member';
|
|
388
|
+
if (isAccessorCall(relation, call))
|
|
389
|
+
return 'relative_import_static_accessor_instance_method';
|
|
390
|
+
return candidateCount > 1
|
|
391
|
+
? 'relative_import_path_disambiguated'
|
|
392
|
+
: 'relative_import_exported_exact';
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function ambiguousReasonFor(
|
|
396
|
+
relation: unknown,
|
|
397
|
+
call: SymbolCallFact,
|
|
398
|
+
): string {
|
|
399
|
+
if (relation === 'class_instance_method')
|
|
400
|
+
return 'multiple_relative_class_targets_in_requested_module';
|
|
401
|
+
if (relation === 'relative_import_namespace_member')
|
|
402
|
+
return 'multiple_namespace_targets_in_requested_module';
|
|
403
|
+
return isAccessorCall(relation, call)
|
|
404
|
+
? 'multiple_accessor_targets_in_requested_module'
|
|
405
|
+
: 'multiple_exported_targets_in_requested_module';
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function resolveRelativeSymbolCall(
|
|
409
|
+
db: Db,
|
|
410
|
+
repoId: number,
|
|
411
|
+
call: SymbolCallFact,
|
|
412
|
+
relation: unknown,
|
|
413
|
+
): RelativeSymbolCallResolution | undefined {
|
|
414
|
+
if (!call.importSource?.startsWith('.')) return undefined;
|
|
415
|
+
const rows = resolutionRows(db, repoId, call, relation);
|
|
416
|
+
const binding = relativeBinding(call);
|
|
417
|
+
const strategy = strategyFor(relation, call, rows.length);
|
|
418
|
+
if (!binding) return unresolved(
|
|
419
|
+
strategy, 'relative_import_provenance_missing', rows.length,
|
|
420
|
+
);
|
|
421
|
+
if (binding.typeOnly === true) return unresolved(
|
|
422
|
+
strategy, 'relative_import_type_only', rows.length,
|
|
423
|
+
);
|
|
424
|
+
const moduleScope = relativeModuleScope(
|
|
425
|
+
db, repoId, call.sourceFile, call.importSource,
|
|
426
|
+
);
|
|
427
|
+
if (moduleScope.ambiguous) return unresolved(
|
|
428
|
+
strategy, 'relative_import_module_resolution_ambiguous',
|
|
429
|
+
rows.length, 0, false,
|
|
430
|
+
);
|
|
431
|
+
if (relation === 'relative_import_proxy_member')
|
|
432
|
+
return proxyResolution(db, repoId, rows, moduleScope);
|
|
433
|
+
const moduleRows = scopedRows(rows, moduleScope.paths);
|
|
434
|
+
const scoped = binding && requiresPublicClassMember(
|
|
435
|
+
relation, call, binding,
|
|
436
|
+
)
|
|
437
|
+
? moduleRows.filter((row) => handlerMemberPubliclyCarried(row, binding))
|
|
438
|
+
: moduleRows;
|
|
439
|
+
return executableResolution(
|
|
440
|
+
rows, scoped, [...moduleScope.paths][0],
|
|
441
|
+
strategy, ambiguousReasonFor(relation, call),
|
|
442
|
+
);
|
|
443
|
+
}
|