@saptools/service-flow 0.1.49 → 0.1.51
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 +14 -0
- package/README.md +10 -2
- package/TECHNICAL-NOTE.md +6 -1
- package/dist/{chunk-XOROZHW4.js → chunk-YZJKE5UX.js} +757 -122
- package/dist/chunk-YZJKE5UX.js.map +1 -0
- package/dist/cli.js +107 -22
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +20 -10
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/doctor.ts +59 -1
- package/src/cli.ts +22 -0
- package/src/db/migrations.ts +4 -1
- package/src/db/repositories.ts +2 -1
- package/src/db/schema.ts +1 -1
- package/src/index.ts +1 -1
- package/src/linker/cross-repo-linker.ts +111 -6
- package/src/linker/operation-decorator-normalizer.ts +3 -3
- package/src/linker/service-resolver.ts +15 -4
- package/src/output/table-output.ts +29 -2
- package/src/parsers/decorator-parser.ts +128 -22
- package/src/trace/dynamic-branches.ts +45 -0
- package/src/trace/dynamic-targets.ts +424 -0
- package/src/trace/evidence.ts +117 -7
- package/src/trace/selectors.ts +36 -0
- package/src/trace/trace-engine.ts +12 -31
- package/src/types.ts +24 -0
- package/dist/chunk-XOROZHW4.js.map +0 -1
package/src/trace/evidence.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { Db } from '../db/connection.js';
|
|
|
2
2
|
import { extractPlaceholders, substituteVariables, type RuntimeSubstitution } from '../linker/dynamic-edge-resolver.js';
|
|
3
3
|
import { normalizeODataOperationInvocationPath } from '../linker/odata-path-normalizer.js';
|
|
4
4
|
import { resolveOperation, type OperationTarget } from '../linker/service-resolver.js';
|
|
5
|
+
import type { DynamicMode } from '../types.js';
|
|
6
|
+
import { analyzeDynamicTargetCandidates, type DynamicTargetAnalysis, type DynamicTargetCandidate } from './dynamic-targets.js';
|
|
5
7
|
|
|
6
8
|
export interface TraceGraphRow extends Record<string, unknown> {
|
|
7
9
|
id: number;
|
|
@@ -50,27 +52,49 @@ export function runtimeResolution(
|
|
|
50
52
|
db: Db,
|
|
51
53
|
row: TraceGraphRow,
|
|
52
54
|
evidence: Record<string, unknown>,
|
|
53
|
-
|
|
55
|
+
options: { vars?: Record<string, string>; dynamicMode?: DynamicMode; maxDynamicCandidates?: number },
|
|
54
56
|
workspaceId: number | undefined,
|
|
55
57
|
contextualUnresolvedReason?: string,
|
|
56
58
|
): { row: TraceGraphRow; evidence: Record<string, unknown>; target?: OperationTarget; unresolvedReason?: string } {
|
|
57
|
-
const substituted = evidenceWithRuntimeVariables(evidence, vars);
|
|
58
|
-
|
|
59
|
+
const substituted = evidenceWithRuntimeVariables(evidence, options.vars);
|
|
60
|
+
const dynamicMode = options.dynamicMode ?? 'strict';
|
|
61
|
+
const analysis = analyzeDynamicTargetCandidates(
|
|
62
|
+
db,
|
|
63
|
+
substituted,
|
|
64
|
+
workspaceId,
|
|
65
|
+
dynamicMode,
|
|
66
|
+
positiveCandidateCap(options.maxDynamicCandidates),
|
|
67
|
+
);
|
|
68
|
+
const enriched = analysis ? evidenceWithDynamicAnalysis(substituted, analysis) : substituted;
|
|
69
|
+
if (dynamicMode === 'infer') {
|
|
70
|
+
const inferred = inferredTarget(analysis);
|
|
71
|
+
if (inferred) {
|
|
72
|
+
const resolvedRow = { ...row, status: 'resolved', to_kind: 'operation', to_id: String(inferred.operationId), unresolved_reason: undefined, confidence: inferred.score };
|
|
73
|
+
const resolution = { status: 'resolved' as const, target: inferred, candidates: [], reasons: inferred.reasons };
|
|
74
|
+
return { row: resolvedRow, evidence: withEffectiveResolution(enriched, resolvedRow, undefined, resolution), target: inferred };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (!isRemoteRuntimeCandidate(row, evidence, options.vars)) {
|
|
59
78
|
const unresolvedReason = contextualUnresolvedReason ?? row.unresolved_reason;
|
|
60
|
-
const withSections = withEffectiveResolution(
|
|
79
|
+
const withSections = withEffectiveResolution(enriched, row, unresolvedReason);
|
|
61
80
|
return { row, evidence: withSections, unresolvedReason };
|
|
62
81
|
}
|
|
63
|
-
const resolution = resolveRuntimeOperation(db,
|
|
82
|
+
const resolution = resolveRuntimeOperation(db, enriched, workspaceId);
|
|
64
83
|
if (resolution.target) {
|
|
65
84
|
const resolvedRow = { ...row, status: 'resolved', to_kind: 'operation', to_id: String(resolution.target.operationId), unresolved_reason: undefined, confidence: Math.max(0, Math.min(1, resolution.target.score)) };
|
|
66
|
-
return { row: resolvedRow, evidence: withEffectiveResolution(
|
|
85
|
+
return { row: resolvedRow, evidence: withEffectiveResolution(enriched, resolvedRow, undefined, resolution), target: resolution.target };
|
|
67
86
|
}
|
|
68
87
|
const unresolvedReason = runtimeUnresolvedReason(resolution);
|
|
69
|
-
return { row, evidence: withEffectiveResolution(
|
|
88
|
+
return { row, evidence: withEffectiveResolution(enriched, row, unresolvedReason, resolution), unresolvedReason };
|
|
70
89
|
}
|
|
71
90
|
|
|
72
91
|
export function runtimeVariableDiagnostic(edges: Array<{ evidence: Record<string, unknown> }>): Record<string, unknown> | undefined {
|
|
73
92
|
const missing = new Set<string>();
|
|
93
|
+
let candidateCount = 0;
|
|
94
|
+
let shownCandidateCount = 0;
|
|
95
|
+
let omittedCandidateCount = 0;
|
|
96
|
+
const candidateSuggestions: Record<string, unknown>[] = [];
|
|
97
|
+
const suggestedVarSets: Record<string, unknown>[] = [];
|
|
74
98
|
for (const edge of edges) {
|
|
75
99
|
const effective = parseObject(edge.evidence.effectiveResolution);
|
|
76
100
|
if (!['dynamic', 'unresolved', 'ambiguous'].includes(String(effective.status ?? '')))
|
|
@@ -79,6 +103,12 @@ export function runtimeVariableDiagnostic(edges: Array<{ evidence: Record<string
|
|
|
79
103
|
if (!substitutions || typeof substitutions !== 'object' || Array.isArray(substitutions)) continue;
|
|
80
104
|
for (const value of Object.values(substitutions as Record<string, RuntimeSubstitution>))
|
|
81
105
|
for (const key of value.missing ?? []) missing.add(key);
|
|
106
|
+
const exploration = parseObject(edge.evidence.dynamicTargetExploration);
|
|
107
|
+
candidateCount += numeric(exploration.candidateCount);
|
|
108
|
+
shownCandidateCount += numeric(exploration.shownCandidateCount);
|
|
109
|
+
omittedCandidateCount += numeric(exploration.omittedCandidateCount);
|
|
110
|
+
candidateSuggestions.push(...recordArray(edge.evidence.dynamicTargetCandidateSuggestions));
|
|
111
|
+
suggestedVarSets.push(...recordArray(exploration.suggestedVarSets));
|
|
82
112
|
}
|
|
83
113
|
const missingVariables = [...missing].sort();
|
|
84
114
|
if (missingVariables.length === 0) return undefined;
|
|
@@ -88,6 +118,16 @@ export function runtimeVariableDiagnostic(edges: Array<{ evidence: Record<string
|
|
|
88
118
|
message: `Runtime variables are required to resolve dynamic trace targets: ${missingVariables.join(', ')}`,
|
|
89
119
|
missingVariables,
|
|
90
120
|
suggestions: missingVariables.map((key) => `--var ${key}=<value>`),
|
|
121
|
+
candidateCount,
|
|
122
|
+
shownCandidateCount,
|
|
123
|
+
omittedCandidateCount,
|
|
124
|
+
candidateSuggestions: candidateSuggestions.slice(0, 5),
|
|
125
|
+
suggestedVarSets: uniqueCliRows(suggestedVarSets).slice(0, 5),
|
|
126
|
+
copyableExamples: [
|
|
127
|
+
...uniqueCliRows(suggestedVarSets).slice(0, 3).flatMap((row) =>
|
|
128
|
+
typeof row.cli === 'string' ? [row.cli] : []),
|
|
129
|
+
...(candidateCount > 0 ? ['--dynamic-mode candidates --max-dynamic-candidates 20'] : []),
|
|
130
|
+
],
|
|
91
131
|
};
|
|
92
132
|
}
|
|
93
133
|
|
|
@@ -180,6 +220,26 @@ function evidenceWithRuntimeVariables(evidence: Record<string, unknown>, vars: R
|
|
|
180
220
|
return next;
|
|
181
221
|
}
|
|
182
222
|
|
|
223
|
+
function evidenceWithDynamicAnalysis(
|
|
224
|
+
evidence: Record<string, unknown>,
|
|
225
|
+
analysis: DynamicTargetAnalysis,
|
|
226
|
+
): Record<string, unknown> {
|
|
227
|
+
return {
|
|
228
|
+
...evidence,
|
|
229
|
+
dynamicTargetExploration: {
|
|
230
|
+
mode: analysis.mode,
|
|
231
|
+
missingVariables: analysis.missingVariables,
|
|
232
|
+
candidateCount: analysis.candidateCount,
|
|
233
|
+
shownCandidateCount: analysis.shownCandidateCount,
|
|
234
|
+
omittedCandidateCount: analysis.omittedCandidateCount,
|
|
235
|
+
suggestedVarSets: analysis.suggestedVarSets,
|
|
236
|
+
},
|
|
237
|
+
dynamicTargetCandidates: analysis.candidates,
|
|
238
|
+
dynamicTargetCandidateSuggestions: analysis.shownCandidates,
|
|
239
|
+
dynamicTargetInference: analysis.inference,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
183
243
|
function runtimeSubstitutions(evidence: Record<string, unknown>, vars: Record<string, string>): Record<string, RuntimeSubstitution> {
|
|
184
244
|
const substitutions: Record<string, RuntimeSubstitution> = {};
|
|
185
245
|
for (const key of ['servicePath', 'operationPath', 'serviceAliasExpr', 'serviceAlias', 'destination']) {
|
|
@@ -206,6 +266,31 @@ function isRemoteRuntimeCandidate(row: TraceGraphRow, evidence: Record<string, u
|
|
|
206
266
|
return ['servicePath', 'operationPath', 'serviceAliasExpr', 'serviceAlias', 'destination'].some((key) => hasRuntimeVariable(evidence[key], vars));
|
|
207
267
|
}
|
|
208
268
|
|
|
269
|
+
function inferredTarget(analysis: DynamicTargetAnalysis | undefined): OperationTarget | undefined {
|
|
270
|
+
if (analysis?.inference.status !== 'resolved') return undefined;
|
|
271
|
+
const id = Number(analysis.inference.candidateOperationId);
|
|
272
|
+
const candidate = analysis.candidates.find((item) => item.candidateOperationId === id);
|
|
273
|
+
if (!candidate) return undefined;
|
|
274
|
+
return targetFromCandidate(candidate);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function targetFromCandidate(candidate: DynamicTargetCandidate): OperationTarget {
|
|
278
|
+
return {
|
|
279
|
+
operationId: candidate.candidateOperationId,
|
|
280
|
+
repoName: candidate.repoName,
|
|
281
|
+
serviceName: '',
|
|
282
|
+
qualifiedName: '',
|
|
283
|
+
servicePath: candidate.servicePath,
|
|
284
|
+
operationPath: candidate.operationPath,
|
|
285
|
+
operationName: candidate.operationName,
|
|
286
|
+
packageName: candidate.packageName,
|
|
287
|
+
score: candidate.score,
|
|
288
|
+
reasons: candidate.reasons,
|
|
289
|
+
sourceFile: '',
|
|
290
|
+
sourceLine: 0,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
209
294
|
function hasRuntimeVariable(value: unknown, vars: Record<string, string>): boolean {
|
|
210
295
|
return typeof value === 'string' && extractPlaceholders(value).some((key) => Object.hasOwn(vars, key));
|
|
211
296
|
}
|
|
@@ -223,3 +308,28 @@ function parseObject(value: unknown): Record<string, unknown> {
|
|
|
223
308
|
function stringValue(value: unknown): string | undefined {
|
|
224
309
|
return typeof value === 'string' ? value : undefined;
|
|
225
310
|
}
|
|
311
|
+
|
|
312
|
+
function numeric(value: unknown): number {
|
|
313
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function recordArray(value: unknown): Record<string, unknown>[] {
|
|
317
|
+
return Array.isArray(value)
|
|
318
|
+
? value.filter((item): item is Record<string, unknown> =>
|
|
319
|
+
Boolean(item && typeof item === 'object' && !Array.isArray(item)))
|
|
320
|
+
: [];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function uniqueCliRows(rows: Record<string, unknown>[]): Record<string, unknown>[] {
|
|
324
|
+
const seen = new Set<string>();
|
|
325
|
+
return rows.filter((row) => {
|
|
326
|
+
const cli = typeof row.cli === 'string' ? row.cli : JSON.stringify(row);
|
|
327
|
+
if (seen.has(cli)) return false;
|
|
328
|
+
seen.add(cli);
|
|
329
|
+
return true;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function positiveCandidateCap(value: number | undefined): number {
|
|
334
|
+
return Number.isFinite(value) && Number(value) > 0 ? Math.floor(Number(value)) : 5;
|
|
335
|
+
}
|
package/src/trace/selectors.ts
CHANGED
|
@@ -18,3 +18,39 @@ export function startLabel(start: TraceStart): string {
|
|
|
18
18
|
.filter(Boolean)
|
|
19
19
|
.join(' ');
|
|
20
20
|
}
|
|
21
|
+
export function ambiguousStartDiagnostic(
|
|
22
|
+
requested: string,
|
|
23
|
+
candidates: Array<Record<string, unknown>>,
|
|
24
|
+
message: string,
|
|
25
|
+
): Record<string, unknown> {
|
|
26
|
+
const serviceSuggestions = [...new Set(candidates
|
|
27
|
+
.flatMap((row) => typeof row.servicePath === 'string'
|
|
28
|
+
? [`--service ${row.servicePath}`]
|
|
29
|
+
: []))].sort();
|
|
30
|
+
return {
|
|
31
|
+
severity: 'warning',
|
|
32
|
+
code: 'trace_start_ambiguous',
|
|
33
|
+
message,
|
|
34
|
+
normalizedSelectorValue: requested,
|
|
35
|
+
resolutionStage: 'operation',
|
|
36
|
+
resolutionStatus: 'ambiguous_operation',
|
|
37
|
+
candidates,
|
|
38
|
+
serviceSuggestions,
|
|
39
|
+
selectorSuggestions: fullSelectorSuggestions(candidates),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function fullSelectorSuggestions(
|
|
43
|
+
candidates: Array<Record<string, unknown>>,
|
|
44
|
+
): string[] {
|
|
45
|
+
const includeRepo = new Set(candidates.map((row) => row.repoName)).size > 1;
|
|
46
|
+
return [...new Set(candidates.flatMap((row) => {
|
|
47
|
+
if (typeof row.servicePath !== 'string'
|
|
48
|
+
|| typeof row.operationPath !== 'string') return [];
|
|
49
|
+
const repoSelector = includeRepo && typeof row.repoName === 'string'
|
|
50
|
+
? `--repo ${row.repoName} `
|
|
51
|
+
: '';
|
|
52
|
+
return [
|
|
53
|
+
`${repoSelector}--service ${row.servicePath} --path ${row.operationPath}`,
|
|
54
|
+
];
|
|
55
|
+
}))].sort();
|
|
56
|
+
}
|
|
@@ -2,9 +2,11 @@ import type { Db } from '../db/connection.js';
|
|
|
2
2
|
import { extractPlaceholders } from '../linker/dynamic-edge-resolver.js';
|
|
3
3
|
import { normalizeODataOperationInvocationPath } from '../linker/odata-path-normalizer.js';
|
|
4
4
|
import { resolveOperation } from '../linker/service-resolver.js';
|
|
5
|
-
import type { ImplementationHint, TraceEdge, TraceResult, TraceStart } from '../types.js';
|
|
5
|
+
import type { ImplementationHint, TraceEdge, TraceOptions, TraceResult, TraceStart } from '../types.js';
|
|
6
6
|
import { baseTraceEvidence, edgeTarget, runtimeResolution, runtimeVariableDiagnostic, type TraceGraphRow } from './evidence.js';
|
|
7
|
+
import { dynamicCandidateBranches } from './dynamic-branches.js';
|
|
7
8
|
import { implementationHintDiagnostic, selectImplementation, type ImplementationSelection } from './implementation-hints.js';
|
|
9
|
+
import { ambiguousStartDiagnostic } from './selectors.js';
|
|
8
10
|
interface RepoRef {
|
|
9
11
|
id: number;
|
|
10
12
|
name: string;
|
|
@@ -120,26 +122,6 @@ function implementationRejectionReasons(evidence: Record<string, unknown>): stri
|
|
|
120
122
|
: []);
|
|
121
123
|
return [...new Set(reasons)].sort();
|
|
122
124
|
}
|
|
123
|
-
function ambiguousStartDiagnostic(
|
|
124
|
-
requested: string,
|
|
125
|
-
candidates: Array<Record<string, unknown>>,
|
|
126
|
-
message: string,
|
|
127
|
-
): Record<string, unknown> {
|
|
128
|
-
const serviceSuggestions = [...new Set(candidates
|
|
129
|
-
.flatMap((row) => typeof row.servicePath === 'string'
|
|
130
|
-
? [`--service ${row.servicePath}`]
|
|
131
|
-
: []))].sort();
|
|
132
|
-
return {
|
|
133
|
-
severity: 'warning',
|
|
134
|
-
code: 'trace_start_ambiguous',
|
|
135
|
-
message,
|
|
136
|
-
normalizedSelectorValue: requested,
|
|
137
|
-
resolutionStage: 'operation',
|
|
138
|
-
resolutionStatus: 'ambiguous_operation',
|
|
139
|
-
candidates,
|
|
140
|
-
serviceSuggestions,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
125
|
function sourceFilesForStart(
|
|
144
126
|
db: Db,
|
|
145
127
|
repoId: number | undefined,
|
|
@@ -326,6 +308,7 @@ function operationNode(db: Db, operationId: string): Record<string, unknown> | u
|
|
|
326
308
|
if (!row) return undefined;
|
|
327
309
|
return { id: `operation:${operationId}`, kind: 'operation', label: `${String(row.repoName)}:${String(row.servicePath)}${String(row.operationPath)}`, ...row };
|
|
328
310
|
}
|
|
311
|
+
|
|
329
312
|
function workspaceIdForCall(db: Db, callId: string): number | undefined {
|
|
330
313
|
return (db.prepare('SELECT r.workspace_id workspaceId FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id WHERE c.id=?').get(callId) as { workspaceId?: number } | undefined)?.workspaceId;
|
|
331
314
|
}
|
|
@@ -505,15 +488,7 @@ function contextualRuntimeResolution(db: Db, call: CallRow, binding: ContextBind
|
|
|
505
488
|
export function trace(
|
|
506
489
|
db: Db,
|
|
507
490
|
start: TraceStart,
|
|
508
|
-
options:
|
|
509
|
-
depth: number;
|
|
510
|
-
vars?: Record<string, string>;
|
|
511
|
-
includeExternal?: boolean;
|
|
512
|
-
includeDb?: boolean;
|
|
513
|
-
includeAsync?: boolean;
|
|
514
|
-
implementationRepo?: string;
|
|
515
|
-
implementationHints?: ImplementationHint[];
|
|
516
|
-
},
|
|
491
|
+
options: TraceOptions,
|
|
517
492
|
): TraceResult {
|
|
518
493
|
const hintOptions = { implementationRepo: options.implementationRepo, implementationHints: options.implementationHints };
|
|
519
494
|
const scope = startScope(db, start, hintOptions);
|
|
@@ -614,7 +589,11 @@ export function trace(
|
|
|
614
589
|
String(row.evidence_json || '{}'),
|
|
615
590
|
) as Record<string, unknown>;
|
|
616
591
|
const rawEvidence = baseTraceEvidence(row as TraceGraphRow, call, persistedEvidence, contextual.evidence);
|
|
617
|
-
const effective = runtimeResolution(db, row as TraceGraphRow, rawEvidence,
|
|
592
|
+
const effective = runtimeResolution(db, row as TraceGraphRow, rawEvidence, {
|
|
593
|
+
vars: options.vars,
|
|
594
|
+
dynamicMode: options.dynamicMode ?? 'strict',
|
|
595
|
+
maxDynamicCandidates: options.maxDynamicCandidates,
|
|
596
|
+
}, workspaceIdForCall(db, String(call.id)), contextual.unresolvedReason);
|
|
618
597
|
const evidence = effective.evidence;
|
|
619
598
|
const effectiveRow = effective.row;
|
|
620
599
|
const targetNode = `${effectiveRow.to_kind}:${effectiveRow.to_id}`;
|
|
@@ -634,6 +613,8 @@ export function trace(
|
|
|
634
613
|
confidence: Number(effectiveRow.confidence ?? call.confidence),
|
|
635
614
|
unresolvedReason: effective.unresolvedReason,
|
|
636
615
|
});
|
|
616
|
+
if ((options.dynamicMode ?? 'strict') === 'candidates')
|
|
617
|
+
edges.push(...dynamicCandidateBranches(current.depth, call, evidence));
|
|
637
618
|
if (effectiveRow.to_kind === 'operation') {
|
|
638
619
|
const implementation = implementationScope(db, effectiveRow.to_id);
|
|
639
620
|
const contextSelection = contextImplementationMethodId(implementation.edge, current.repoId, evidence, hintOptions);
|
package/src/types.ts
CHANGED
|
@@ -101,6 +101,18 @@ export interface HandlerMethodFact {
|
|
|
101
101
|
decoratorKind: string;
|
|
102
102
|
decoratorValue?: string;
|
|
103
103
|
decoratorRawExpression: string;
|
|
104
|
+
decoratorResolution: {
|
|
105
|
+
rawExpression: string;
|
|
106
|
+
resolvedValue?: string;
|
|
107
|
+
resolutionKind:
|
|
108
|
+
| 'literal'
|
|
109
|
+
| 'const_identifier'
|
|
110
|
+
| 'enum_member'
|
|
111
|
+
| 'const_object_property'
|
|
112
|
+
| 'generated_constant_name'
|
|
113
|
+
| 'unresolved';
|
|
114
|
+
unresolvedReason?: string;
|
|
115
|
+
};
|
|
104
116
|
sourceFile: string;
|
|
105
117
|
sourceLine: number;
|
|
106
118
|
}
|
|
@@ -187,6 +199,18 @@ export interface ImplementationHint {
|
|
|
187
199
|
candidateFamily?: string;
|
|
188
200
|
implementationRepo: string;
|
|
189
201
|
}
|
|
202
|
+
export type DynamicMode = 'strict' | 'candidates' | 'infer';
|
|
203
|
+
export interface TraceOptions {
|
|
204
|
+
depth: number;
|
|
205
|
+
vars?: Record<string, string>;
|
|
206
|
+
includeExternal?: boolean;
|
|
207
|
+
includeDb?: boolean;
|
|
208
|
+
includeAsync?: boolean;
|
|
209
|
+
implementationRepo?: string;
|
|
210
|
+
implementationHints?: ImplementationHint[];
|
|
211
|
+
dynamicMode?: DynamicMode;
|
|
212
|
+
maxDynamicCandidates?: number;
|
|
213
|
+
}
|
|
190
214
|
export interface TraceEdge {
|
|
191
215
|
step: number;
|
|
192
216
|
type: string;
|