@saptools/service-flow 0.1.51 → 0.1.52
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 +7 -0
- package/README.md +10 -5
- package/TECHNICAL-NOTE.md +8 -4
- package/dist/{chunk-YZJKE5UX.js → chunk-PTLDSHRC.js} +2412 -553
- package/dist/chunk-PTLDSHRC.js.map +1 -0
- package/dist/cli.js +280 -506
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +45 -7
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/000-clean.ts +82 -0
- package/src/cli.ts +75 -57
- package/src/db/connection.ts +1 -1
- package/src/db/migrations.ts +5 -3
- package/src/db/repositories.ts +120 -22
- package/src/db/schema.ts +7 -2
- package/src/indexer/repository-indexer.ts +57 -29
- package/src/indexer/workspace-indexer.ts +84 -6
- package/src/linker/cross-repo-linker.ts +13 -1
- package/src/output/table-output.ts +29 -3
- package/src/parsers/cds-parser.ts +8 -2
- package/src/parsers/decorator-parser.ts +382 -48
- package/src/parsers/handler-registration-parser.ts +38 -21
- package/src/parsers/imported-wrapper-parser.ts +18 -5
- package/src/parsers/outbound-call-parser.ts +25 -8
- package/src/parsers/package-json-parser.ts +36 -11
- package/src/parsers/service-binding-parser-helpers.ts +8 -1
- package/src/parsers/service-binding-parser.ts +6 -3
- package/src/parsers/symbol-parser.ts +13 -3
- package/src/parsers/ts-project.ts +54 -0
- package/src/trace/000-dynamic-target-types.ts +84 -0
- package/src/trace/001-dynamic-identity.ts +280 -0
- package/src/trace/002-trace-diagnostics.ts +54 -0
- package/src/trace/003-dynamic-references.ts +82 -0
- package/src/trace/dynamic-targets.ts +459 -229
- package/src/trace/evidence.ts +305 -46
- package/src/trace/selectors.ts +483 -0
- package/src/trace/trace-engine.ts +90 -83
- package/src/types.ts +27 -1
- package/dist/chunk-YZJKE5UX.js.map +0 -1
|
@@ -4,6 +4,7 @@ import { classifyODataPathIntent } from '../linker/odata-path-normalizer.js';
|
|
|
4
4
|
import type { OutboundCallFact } from '../types.js';
|
|
5
5
|
import { normalizePath } from '../utils/path-utils.js';
|
|
6
6
|
import { importsFor, lineOf, readSource, type ImportBinding } from './service-binding-parser-helpers.js';
|
|
7
|
+
import type { RepositorySourceContext } from './ts-project.js';
|
|
7
8
|
import {
|
|
8
9
|
analyzeOperationPath,
|
|
9
10
|
operationPathExpression,
|
|
@@ -25,6 +26,7 @@ export async function parseImportedWrapperCalls(
|
|
|
25
26
|
filePath: string,
|
|
26
27
|
source: ts.SourceFile,
|
|
27
28
|
serviceBindings: Set<string>,
|
|
29
|
+
context?: RepositorySourceContext,
|
|
28
30
|
): Promise<OutboundCallFact[]> {
|
|
29
31
|
const imports = await importsFor(repoPath, filePath, source);
|
|
30
32
|
const importedByLocal = new Map(imports.filter((item) => item.sourceFile).map((item) => [item.localName, item]));
|
|
@@ -35,7 +37,7 @@ export async function parseImportedWrapperCalls(
|
|
|
35
37
|
if (!ts.isIdentifier(call.expression)) continue;
|
|
36
38
|
const imported = importedByLocal.get(call.expression.text);
|
|
37
39
|
if (!imported?.sourceFile) continue;
|
|
38
|
-
const spec = await loadWrapperSpec(repoPath, imported, cache, 0);
|
|
40
|
+
const spec = await loadWrapperSpec(repoPath, imported, cache, 0, context);
|
|
39
41
|
const fact = spec ? wrapperCallFact(source, filePath, call, spec, serviceBindings) : undefined;
|
|
40
42
|
if (fact) out.push(fact);
|
|
41
43
|
}
|
|
@@ -57,12 +59,15 @@ async function loadWrapperSpec(
|
|
|
57
59
|
imported: ImportBinding,
|
|
58
60
|
cache: Map<string, Promise<WrapperSpec | undefined>>,
|
|
59
61
|
depth: number,
|
|
62
|
+
context?: RepositorySourceContext,
|
|
60
63
|
): Promise<WrapperSpec | undefined> {
|
|
61
64
|
if (!imported.sourceFile || depth > 5) return undefined;
|
|
62
65
|
const key = `${imported.sourceFile}#${imported.exportedName}`;
|
|
63
66
|
const existing = cache.get(key);
|
|
64
67
|
if (existing) return existing;
|
|
65
|
-
const pending = inspectWrapper(
|
|
68
|
+
const pending = inspectWrapper(
|
|
69
|
+
repoPath, imported.sourceFile, imported.exportedName, cache, depth, context,
|
|
70
|
+
);
|
|
66
71
|
cache.set(key, pending);
|
|
67
72
|
return pending;
|
|
68
73
|
}
|
|
@@ -73,14 +78,19 @@ async function inspectWrapper(
|
|
|
73
78
|
exportedName: string,
|
|
74
79
|
cache: Map<string, Promise<WrapperSpec | undefined>>,
|
|
75
80
|
depth: number,
|
|
81
|
+
context?: RepositorySourceContext,
|
|
76
82
|
): Promise<WrapperSpec | undefined> {
|
|
77
|
-
const source = await readSource(
|
|
83
|
+
const source = await readSource(
|
|
84
|
+
path.join(repoPath, sourceFile), context, sourceFile,
|
|
85
|
+
);
|
|
78
86
|
if (!source) return undefined;
|
|
79
87
|
const named = findFunction(source, exportedName);
|
|
80
88
|
if (!named) return undefined;
|
|
81
89
|
const direct = directSendSpec(source, sourceFile, named.name, named.fn);
|
|
82
90
|
if (direct) return direct;
|
|
83
|
-
return nestedSendSpec(
|
|
91
|
+
return nestedSendSpec(
|
|
92
|
+
repoPath, sourceFile, source, named.name, named.fn, cache, depth, context,
|
|
93
|
+
);
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
function directSendSpec(source: ts.SourceFile, sourceFile: string, name: string, fn: ts.FunctionLikeDeclaration): WrapperSpec | undefined {
|
|
@@ -113,6 +123,7 @@ async function nestedSendSpec(
|
|
|
113
123
|
fn: ts.FunctionLikeDeclaration,
|
|
114
124
|
cache: Map<string, Promise<WrapperSpec | undefined>>,
|
|
115
125
|
depth: number,
|
|
126
|
+
context?: RepositorySourceContext,
|
|
116
127
|
): Promise<WrapperSpec | undefined> {
|
|
117
128
|
const imports = await importsFor(repoPath, sourceFile, source);
|
|
118
129
|
const byLocal = new Map(imports.filter((item) => item.sourceFile).map((item) => [item.localName, item]));
|
|
@@ -123,7 +134,9 @@ async function nestedSendSpec(
|
|
|
123
134
|
if (calls.length !== 1) return undefined;
|
|
124
135
|
const call = calls[0];
|
|
125
136
|
const imported = call && ts.isIdentifier(call.expression) ? byLocal.get(call.expression.text) : undefined;
|
|
126
|
-
const nested = imported
|
|
137
|
+
const nested = imported
|
|
138
|
+
? await loadWrapperSpec(repoPath, imported, cache, depth + 1, context)
|
|
139
|
+
: undefined;
|
|
127
140
|
if (!call || !nested) return undefined;
|
|
128
141
|
const params = parameterNames(fn);
|
|
129
142
|
const clientIndex = mappedParameterIndex(call.arguments[nested.clientIndex], params);
|
|
@@ -8,6 +8,7 @@ import { summarizeExpression } from '../utils/redaction.js';
|
|
|
8
8
|
import { classifyODataPathIntent } from '../linker/odata-path-normalizer.js';
|
|
9
9
|
import { parseServiceBindings } from './service-binding-parser.js';
|
|
10
10
|
import { parseImportedWrapperCalls } from './imported-wrapper-parser.js';
|
|
11
|
+
import type { RepositorySourceContext } from './ts-project.js';
|
|
11
12
|
import {
|
|
12
13
|
analyzeOperationPath,
|
|
13
14
|
operationPathExpression,
|
|
@@ -510,15 +511,31 @@ export function containsSupportedOutboundCall(node: ts.Node): boolean {
|
|
|
510
511
|
export async function parseOutboundCalls(
|
|
511
512
|
repoPath: string,
|
|
512
513
|
filePath: string,
|
|
514
|
+
context?: RepositorySourceContext,
|
|
513
515
|
): Promise<OutboundCallFact[]> {
|
|
514
|
-
const
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const
|
|
516
|
+
const snapshot = context?.get(filePath);
|
|
517
|
+
const text = snapshot?.text
|
|
518
|
+
?? await fs.readFile(path.join(repoPath, filePath), 'utf8');
|
|
519
|
+
const source = snapshot?.sourceFile() ?? ts.createSourceFile(
|
|
520
|
+
filePath, text, ts.ScriptTarget.Latest, true,
|
|
521
|
+
filePath.endsWith('.ts') ? ts.ScriptKind.TS : ts.ScriptKind.JS,
|
|
522
|
+
);
|
|
523
|
+
const bindingNames = new Set((await parseServiceBindings(
|
|
524
|
+
repoPath, filePath, context,
|
|
525
|
+
)).map((binding) => binding.variableName));
|
|
526
|
+
const importedWrappers = await parseImportedWrapperCalls(
|
|
527
|
+
repoPath, filePath, source, bindingNames, context,
|
|
528
|
+
);
|
|
529
|
+
return [...classifyOutboundCallsInSource(source, filePath).map((call) => call.fact), ...importedWrappers, ...parseLocalServiceCalls(text, filePath, source)];
|
|
530
|
+
}
|
|
531
|
+
function parseLocalServiceCalls(
|
|
532
|
+
text: string,
|
|
533
|
+
filePath: string,
|
|
534
|
+
source = ts.createSourceFile(
|
|
535
|
+
filePath, text, ts.ScriptTarget.Latest, true,
|
|
536
|
+
filePath.endsWith('.ts') ? ts.ScriptKind.TS : ts.ScriptKind.JS,
|
|
537
|
+
),
|
|
538
|
+
): OutboundCallFact[] {
|
|
522
539
|
const aliases = new Map<string, { service: string; lookup: string; chain: string[] }>();
|
|
523
540
|
const calls: OutboundCallFact[] = [];
|
|
524
541
|
const visit = (node: ts.Node): void => {
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import type { CdsRequire, PackageFacts } from '../types.js';
|
|
4
|
+
interface ParsePackageJsonOptions {
|
|
5
|
+
strict?: boolean;
|
|
6
|
+
allowMissing?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface PackageJsonSnapshot {
|
|
9
|
+
facts: PackageFacts;
|
|
10
|
+
rawText: string;
|
|
11
|
+
}
|
|
12
|
+
function emptyPackageFacts(): PackageFacts {
|
|
13
|
+
return { dependencies: {}, cdsRequires: [], scripts: {} };
|
|
14
|
+
}
|
|
4
15
|
function recordOfString(value: unknown): Record<string, string> {
|
|
5
16
|
if (!value || typeof value !== 'object') return {};
|
|
6
17
|
return Object.fromEntries(
|
|
@@ -41,23 +52,37 @@ function readRequires(cds: unknown): CdsRequire[] {
|
|
|
41
52
|
});
|
|
42
53
|
}
|
|
43
54
|
export async function parsePackageJson(
|
|
44
|
-
repoPath: string
|
|
55
|
+
repoPath: string,
|
|
56
|
+
options: ParsePackageJsonOptions = {},
|
|
45
57
|
): Promise<PackageFacts> {
|
|
58
|
+
return (await loadPackageJsonSnapshot(repoPath, options)).facts;
|
|
59
|
+
}
|
|
60
|
+
export async function loadPackageJsonSnapshot(
|
|
61
|
+
repoPath: string,
|
|
62
|
+
options: ParsePackageJsonOptions = {},
|
|
63
|
+
): Promise<PackageJsonSnapshot> {
|
|
46
64
|
try {
|
|
47
65
|
const raw = await fs.readFile(path.join(repoPath, 'package.json'), 'utf8');
|
|
48
66
|
const json = JSON.parse(raw) as Record<string, unknown>;
|
|
49
67
|
return {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
typeof json.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
rawText: raw,
|
|
69
|
+
facts: {
|
|
70
|
+
packageName: typeof json.name === 'string' ? json.name : undefined,
|
|
71
|
+
packageVersion:
|
|
72
|
+
typeof json.version === 'string' ? json.version : undefined,
|
|
73
|
+
dependencies: {
|
|
74
|
+
...recordOfString(json.dependencies),
|
|
75
|
+
...recordOfString(json.devDependencies),
|
|
76
|
+
},
|
|
77
|
+
cdsRequires: readRequires(json.cds),
|
|
78
|
+
scripts: recordOfString(json.scripts),
|
|
56
79
|
},
|
|
57
|
-
cdsRequires: readRequires(json.cds),
|
|
58
|
-
scripts: recordOfString(json.scripts)
|
|
59
80
|
};
|
|
60
|
-
} catch {
|
|
61
|
-
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const missing = typeof error === 'object' && error !== null
|
|
83
|
+
&& 'code' in error && error.code === 'ENOENT';
|
|
84
|
+
if (!options.strict || (options.allowMissing && missing))
|
|
85
|
+
return { facts: emptyPackageFacts(), rawText: '' };
|
|
86
|
+
throw error;
|
|
62
87
|
}
|
|
63
88
|
}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
import { normalizePath } from '../utils/path-utils.js';
|
|
5
|
+
import type { RepositorySourceContext } from './ts-project.js';
|
|
5
6
|
|
|
6
7
|
export interface HelperBinding {
|
|
7
8
|
exportedName: string;
|
|
@@ -123,7 +124,13 @@ export function findConnectInExpression(expr: ts.Expression): Omit<HelperBinding
|
|
|
123
124
|
return found;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
export async function readSource(
|
|
127
|
+
export async function readSource(
|
|
128
|
+
abs: string,
|
|
129
|
+
context?: RepositorySourceContext,
|
|
130
|
+
filePath?: string,
|
|
131
|
+
): Promise<ts.SourceFile | undefined> {
|
|
132
|
+
const snapshot = filePath ? context?.get(filePath) : undefined;
|
|
133
|
+
if (snapshot) return snapshot.sourceFile();
|
|
127
134
|
try {
|
|
128
135
|
const text = await fs.readFile(abs, 'utf8');
|
|
129
136
|
return ts.createSourceFile(abs, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type HelperBinding,
|
|
16
16
|
type ImportBinding,
|
|
17
17
|
} from './service-binding-parser-helpers.js';
|
|
18
|
+
import type { RepositorySourceContext } from './ts-project.js';
|
|
18
19
|
|
|
19
20
|
function collectLocalBindingFacts(
|
|
20
21
|
fn: ts.FunctionLikeDeclaration,
|
|
@@ -125,8 +126,9 @@ function exportedLocalNames(sf: ts.SourceFile): Map<string, string> {
|
|
|
125
126
|
async function helperBindings(
|
|
126
127
|
repoPath: string,
|
|
127
128
|
filePath: string,
|
|
129
|
+
context?: RepositorySourceContext,
|
|
128
130
|
): Promise<HelperBinding[]> {
|
|
129
|
-
const sf = await readSource(path.join(repoPath, filePath));
|
|
131
|
+
const sf = await readSource(path.join(repoPath, filePath), context, filePath);
|
|
130
132
|
if (!sf) return [];
|
|
131
133
|
const sourceFileAst = sf;
|
|
132
134
|
const out: HelperBinding[] = [];
|
|
@@ -195,8 +197,9 @@ async function helperBindings(
|
|
|
195
197
|
export async function parseServiceBindings(
|
|
196
198
|
repoPath: string,
|
|
197
199
|
filePath: string,
|
|
200
|
+
context?: RepositorySourceContext,
|
|
198
201
|
): Promise<ServiceBindingFact[]> {
|
|
199
|
-
const sf = await readSource(path.join(repoPath, filePath));
|
|
202
|
+
const sf = await readSource(path.join(repoPath, filePath), context, filePath);
|
|
200
203
|
if (!sf) return [];
|
|
201
204
|
const sourceFileAst = sf;
|
|
202
205
|
const out: ServiceBindingFact[] = [];
|
|
@@ -237,7 +240,7 @@ export async function parseServiceBindings(
|
|
|
237
240
|
if (!helperCache.has(imp.sourceFile))
|
|
238
241
|
helperCache.set(
|
|
239
242
|
imp.sourceFile,
|
|
240
|
-
await helperBindings(repoPath, imp.sourceFile),
|
|
243
|
+
await helperBindings(repoPath, imp.sourceFile, context),
|
|
241
244
|
);
|
|
242
245
|
return (helperCache.get(imp.sourceFile) ?? [])
|
|
243
246
|
.filter((h) => h.exportedName === imp.exportedName)
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
import type { ExecutableSymbolFact, SymbolCallFact } from '../types.js';
|
|
5
5
|
import { containsSupportedOutboundCall } from './outbound-call-parser.js';
|
|
6
|
+
import type { RepositorySourceContext } from './ts-project.js';
|
|
6
7
|
import { normalizePath } from '../utils/path-utils.js';
|
|
7
8
|
|
|
8
9
|
function lineOf(source: ts.SourceFile, pos: number): number {
|
|
@@ -167,9 +168,18 @@ function parameterBindings(params: ts.NodeArray<ts.ParameterDeclaration>): Param
|
|
|
167
168
|
return [];
|
|
168
169
|
});
|
|
169
170
|
}
|
|
170
|
-
export async function parseExecutableSymbols(
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
export async function parseExecutableSymbols(
|
|
172
|
+
repoPath: string,
|
|
173
|
+
filePath: string,
|
|
174
|
+
context?: RepositorySourceContext,
|
|
175
|
+
): Promise<{ symbols: ExecutableSymbolFact[]; calls: SymbolCallFact[] }> {
|
|
176
|
+
const snapshot = context?.get(filePath);
|
|
177
|
+
const text = snapshot?.text
|
|
178
|
+
?? await fs.readFile(path.join(repoPath, filePath), 'utf8');
|
|
179
|
+
const source = snapshot?.sourceFile() ?? ts.createSourceFile(
|
|
180
|
+
filePath, text, ts.ScriptTarget.Latest, true,
|
|
181
|
+
filePath.endsWith('.ts') ? ts.ScriptKind.TS : ts.ScriptKind.JS,
|
|
182
|
+
);
|
|
173
183
|
const sourceFile = normalizePath(filePath);
|
|
174
184
|
const symbols: ExecutableSymbolFact[] = [];
|
|
175
185
|
const calls: SymbolCallFact[] = [];
|
|
@@ -1,4 +1,58 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import ts from 'typescript';
|
|
4
|
+
import { normalizePath } from '../utils/path-utils.js';
|
|
5
|
+
|
|
6
|
+
export interface SourceContextInstrumentation {
|
|
7
|
+
onSourceRead?: (
|
|
8
|
+
repoPath: string,
|
|
9
|
+
filePath: string,
|
|
10
|
+
) => void | Promise<void>;
|
|
11
|
+
onAstCreated?: (repoPath: string, filePath: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SourceFileSnapshot {
|
|
15
|
+
repoPath: string;
|
|
16
|
+
filePath: string;
|
|
17
|
+
text: string;
|
|
18
|
+
sizeBytes: number;
|
|
19
|
+
sourceFile: () => ts.SourceFile;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RepositorySourceContext {
|
|
23
|
+
get: (filePath: string) => SourceFileSnapshot | undefined;
|
|
24
|
+
entries: () => SourceFileSnapshot[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function loadRepositorySourceContext(
|
|
28
|
+
repoPath: string,
|
|
29
|
+
filePaths: string[],
|
|
30
|
+
instrumentation?: SourceContextInstrumentation,
|
|
31
|
+
): Promise<RepositorySourceContext> {
|
|
32
|
+
const snapshots = new Map<string, SourceFileSnapshot>();
|
|
33
|
+
for (const inputPath of filePaths) {
|
|
34
|
+
const filePath = normalizePath(inputPath);
|
|
35
|
+
await instrumentation?.onSourceRead?.(repoPath, filePath);
|
|
36
|
+
const text = await fs.readFile(path.join(repoPath, filePath), 'utf8');
|
|
37
|
+
let ast: ts.SourceFile | undefined;
|
|
38
|
+
snapshots.set(filePath, {
|
|
39
|
+
repoPath,
|
|
40
|
+
filePath,
|
|
41
|
+
text,
|
|
42
|
+
sizeBytes: Buffer.byteLength(text),
|
|
43
|
+
sourceFile: () => {
|
|
44
|
+
if (ast) return ast;
|
|
45
|
+
instrumentation?.onAstCreated?.(repoPath, filePath);
|
|
46
|
+
ast = createSourceFile(filePath, text);
|
|
47
|
+
return ast;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
get: (filePath) => snapshots.get(normalizePath(filePath)),
|
|
53
|
+
entries: () => [...snapshots.values()],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
2
56
|
export function createSourceFile(
|
|
3
57
|
filePath: string,
|
|
4
58
|
text: string
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { DynamicMode } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export interface DynamicTemplates {
|
|
4
|
+
servicePath?: string;
|
|
5
|
+
operationPath?: string;
|
|
6
|
+
alias?: string;
|
|
7
|
+
destination?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DynamicVariableProvenance extends Record<string, unknown> {
|
|
11
|
+
sourceKind: string;
|
|
12
|
+
value: string;
|
|
13
|
+
rule: string;
|
|
14
|
+
template?: string;
|
|
15
|
+
matchedName?: string;
|
|
16
|
+
normalizedForm?: string;
|
|
17
|
+
sourceRepo?: string;
|
|
18
|
+
sourceFile?: string;
|
|
19
|
+
sourceLine?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DynamicVariableConflict {
|
|
23
|
+
key: string;
|
|
24
|
+
values: string[];
|
|
25
|
+
reason: string;
|
|
26
|
+
sources: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DynamicTargetCandidate {
|
|
30
|
+
candidateOperationId: number;
|
|
31
|
+
repoId?: number;
|
|
32
|
+
repoName: string;
|
|
33
|
+
packageName?: string;
|
|
34
|
+
serviceName: string;
|
|
35
|
+
qualifiedName: string;
|
|
36
|
+
servicePath: string;
|
|
37
|
+
operationPath: string;
|
|
38
|
+
operationName: string;
|
|
39
|
+
sourceFile: string;
|
|
40
|
+
sourceLine: number;
|
|
41
|
+
originalTemplates: DynamicTemplates;
|
|
42
|
+
effectiveValues: DynamicTemplates;
|
|
43
|
+
requiredVariables: string[];
|
|
44
|
+
requiredVariableSources: Record<string, string[]>;
|
|
45
|
+
suppliedVariables: Record<string, string>;
|
|
46
|
+
completeVariables: Record<string, string>;
|
|
47
|
+
derivedVariables: Record<string, string>;
|
|
48
|
+
derivedVariableSources: Record<string, DynamicVariableProvenance>;
|
|
49
|
+
derivationProvenance: Record<string, DynamicVariableProvenance[]>;
|
|
50
|
+
missingVariables: string[];
|
|
51
|
+
conflicts: DynamicVariableConflict[];
|
|
52
|
+
score: number;
|
|
53
|
+
explicitSignalStrength: number;
|
|
54
|
+
reasons: string[];
|
|
55
|
+
rejectedReasons: string[];
|
|
56
|
+
inferenceBlockReasons: string[];
|
|
57
|
+
viable: boolean;
|
|
58
|
+
rejected: boolean;
|
|
59
|
+
selected: boolean;
|
|
60
|
+
exploratory: boolean;
|
|
61
|
+
cli?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface DynamicTargetAnalysis {
|
|
65
|
+
mode: DynamicMode;
|
|
66
|
+
maxCandidates: number;
|
|
67
|
+
candidateCount: number;
|
|
68
|
+
viableCandidateCount: number;
|
|
69
|
+
rejectedCandidateCount: number;
|
|
70
|
+
shownCandidateCount: number;
|
|
71
|
+
omittedCandidateCount: number;
|
|
72
|
+
shownRejectedCandidateCount: number;
|
|
73
|
+
omittedRejectedCandidateCount: number;
|
|
74
|
+
missingVariables: string[];
|
|
75
|
+
requiredVariables: string[];
|
|
76
|
+
suppliedVariables: Record<string, string>;
|
|
77
|
+
appliedSuppliedVariables: Record<string, string>;
|
|
78
|
+
substitutedSignals: DynamicTemplates;
|
|
79
|
+
candidates: DynamicTargetCandidate[];
|
|
80
|
+
shownCandidates: DynamicTargetCandidate[];
|
|
81
|
+
rejectedCandidates: DynamicTargetCandidate[];
|
|
82
|
+
suggestedVarSets: Array<{ variables: Record<string, string>; cli: string }>;
|
|
83
|
+
inference: Record<string, unknown>;
|
|
84
|
+
}
|