@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
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
|
+
import { reposByName } from '../db/repositories.js';
|
|
2
3
|
import { extractPlaceholders } from '../linker/dynamic-edge-resolver.js';
|
|
3
4
|
import { normalizeODataOperationInvocationPath } from '../linker/odata-path-normalizer.js';
|
|
4
5
|
import { resolveOperation } from '../linker/service-resolver.js';
|
|
5
6
|
import type { ImplementationHint, TraceEdge, TraceOptions, TraceResult, TraceStart } from '../types.js';
|
|
6
|
-
import { baseTraceEvidence, edgeTarget, runtimeResolution, runtimeVariableDiagnostic, type TraceGraphRow } from './evidence.js';
|
|
7
|
+
import { baseTraceEvidence, edgeTarget, runtimeNoCandidateDiagnostics, runtimeResolution, runtimeVariableDiagnostic, type TraceGraphRow } from './evidence.js';
|
|
7
8
|
import { dynamicCandidateBranches } from './dynamic-branches.js';
|
|
9
|
+
import {
|
|
10
|
+
loadTraceDiagnostics,
|
|
11
|
+
prependTraceDiagnostic,
|
|
12
|
+
} from './002-trace-diagnostics.js';
|
|
8
13
|
import { implementationHintDiagnostic, selectImplementation, type ImplementationSelection } from './implementation-hints.js';
|
|
9
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
ambiguousStartDiagnostic,
|
|
16
|
+
selectorNotFoundDiagnostic,
|
|
17
|
+
selectorRepoAmbiguousDiagnostic,
|
|
18
|
+
selectorRepoNotFoundDiagnostic,
|
|
19
|
+
sourceScopeForSelector,
|
|
20
|
+
} from './selectors.js';
|
|
10
21
|
interface RepoRef {
|
|
11
22
|
id: number;
|
|
12
23
|
name: string;
|
|
24
|
+
packageName?: string;
|
|
13
25
|
}
|
|
14
26
|
interface StartScope {
|
|
15
27
|
repo?: RepoRef;
|
|
@@ -78,13 +90,14 @@ function normalizeOperation(value: string | undefined): string | undefined {
|
|
|
78
90
|
function positiveDepth(value: number): number {
|
|
79
91
|
return Number.isFinite(value) && value > 0 ? Math.floor(value) : 25;
|
|
80
92
|
}
|
|
81
|
-
function operationStartScope(db: Db, repoId: number | undefined, start: TraceStart, hintOptions: ImplementationHintOptions): { files?: Set<string>; symbols?: Set<number>; repoId?: number; operationId?: string; diagnostics?: Array<Record<string, unknown>> } | undefined {
|
|
93
|
+
function operationStartScope(db: Db, repoId: number | undefined, start: TraceStart, hintOptions: ImplementationHintOptions, workspaceId?: number): { files?: Set<string>; symbols?: Set<number>; repoId?: number; operationId?: string; diagnostics?: Array<Record<string, unknown>> } | undefined {
|
|
82
94
|
const requested = normalizeOperation(start.operationPath ?? start.operation);
|
|
83
95
|
if (!requested) return undefined;
|
|
84
96
|
const rows = db.prepare(`SELECT o.id operationId,o.operation_name operationName,o.operation_path operationPath,o.source_file sourceFile,o.source_line sourceLine,s.service_path servicePath,r.id repoId,r.name repoName
|
|
85
97
|
FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id
|
|
86
|
-
WHERE (? IS NULL OR r.
|
|
87
|
-
|
|
98
|
+
WHERE (? IS NULL OR r.workspace_id=?) AND (? IS NULL OR r.id=?)
|
|
99
|
+
AND (? IS NULL OR s.service_path=?) AND (o.operation_name=? OR o.operation_path=? OR o.operation_path=?)
|
|
100
|
+
ORDER BY r.name,s.service_path,o.operation_name,o.id`).all(workspaceId, workspaceId, repoId, repoId, start.servicePath, start.servicePath, requested, requested, requested.startsWith('/') ? requested : `/${requested}`) as Array<Record<string, unknown>>;
|
|
88
101
|
if (rows.length === 0) return undefined;
|
|
89
102
|
const repoCount = new Set(rows.map((row) => String(row.repoName))).size;
|
|
90
103
|
const serviceCount = new Set(rows.map((row) => `${String(row.repoName)}:${String(row.servicePath)}`)).size;
|
|
@@ -126,60 +139,33 @@ function sourceFilesForStart(
|
|
|
126
139
|
db: Db,
|
|
127
140
|
repoId: number | undefined,
|
|
128
141
|
start: TraceStart,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
operation,
|
|
154
|
-
operation,
|
|
155
|
-
operation,
|
|
156
|
-
operation,
|
|
157
|
-
) as Array<{ sourceFile?: string; symbolId?: number }>;
|
|
158
|
-
if (rows.length > 0) return { files: new Set(rows.map((row) => row.sourceFile).filter(Boolean) as string[]), symbols: new Set(rows.map((row) => Number(row.symbolId)).filter(Boolean)), repoId };
|
|
159
|
-
if (start.servicePath && operation) {
|
|
160
|
-
const implRows = db.prepare(`SELECT DISTINCT hc.source_file sourceFile,sym.id symbolId
|
|
161
|
-
FROM cds_services s JOIN cds_operations o ON o.service_id=s.id
|
|
162
|
-
JOIN graph_edges e ON e.edge_type='OPERATION_IMPLEMENTED_BY_HANDLER' AND e.status='resolved' AND e.from_kind='operation' AND e.from_id=CAST(o.id AS TEXT)
|
|
163
|
-
JOIN handler_methods hm ON hm.id=CAST(e.to_id AS INTEGER)
|
|
164
|
-
JOIN handler_classes hc ON hc.id=hm.handler_class_id
|
|
165
|
-
LEFT JOIN symbols sym ON sym.repo_id=hc.repo_id AND sym.source_file=hc.source_file AND sym.name=hm.method_name
|
|
166
|
-
WHERE (? IS NULL OR s.repo_id=?) AND s.service_path=? AND (o.operation_path=? OR o.operation_name=?)`).all(repoId, repoId, start.servicePath, operation, operation) as Array<{ sourceFile?: string; symbolId?: number }>;
|
|
167
|
-
if (implRows.length > 0) return { files: new Set(implRows.map((row) => row.sourceFile).filter(Boolean) as string[]), symbols: new Set(implRows.map((row) => Number(row.symbolId)).filter(Boolean)), repoId };
|
|
168
|
-
}
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
|
-
function startScope(db: Db, start: TraceStart, hintOptions: ImplementationHintOptions): StartScope {
|
|
172
|
-
const repo = start.repo
|
|
173
|
-
? (db
|
|
174
|
-
.prepare(
|
|
175
|
-
'SELECT id,name FROM repositories WHERE name=? OR package_name=?',
|
|
176
|
-
)
|
|
177
|
-
.get(start.repo, start.repo) as RepoRef | undefined)
|
|
178
|
-
: undefined;
|
|
179
|
-
if (start.repo && !repo) return { repo, selectorMatched: false };
|
|
180
|
-
const operationScope = operationStartScope(db, repo?.id, start, hintOptions);
|
|
142
|
+
workspaceId: number | undefined,
|
|
143
|
+
): ReturnType<typeof sourceScopeForSelector> {
|
|
144
|
+
return sourceScopeForSelector(db, repoId, start, workspaceId);
|
|
145
|
+
}
|
|
146
|
+
function startScope(db: Db, start: TraceStart, hintOptions: ImplementationHintOptions, workspaceId?: number): StartScope {
|
|
147
|
+
const repos: RepoRef[] = start.repo
|
|
148
|
+
? reposByName(db, start.repo, workspaceId).map((row) => ({
|
|
149
|
+
id: row.id,
|
|
150
|
+
name: row.name,
|
|
151
|
+
packageName: row.package_name ?? undefined,
|
|
152
|
+
}))
|
|
153
|
+
: [];
|
|
154
|
+
if (start.repo && repos.length === 0) return {
|
|
155
|
+
selectorMatched: false,
|
|
156
|
+
startDiagnostics: [selectorRepoNotFoundDiagnostic(start.repo)],
|
|
157
|
+
};
|
|
158
|
+
if (start.repo && repos.length > 1) return {
|
|
159
|
+
selectorMatched: false,
|
|
160
|
+
startDiagnostics: [selectorRepoAmbiguousDiagnostic(start.repo, repos)],
|
|
161
|
+
};
|
|
162
|
+
const repo = repos[0];
|
|
163
|
+
const operationScope = operationStartScope(
|
|
164
|
+
db, repo?.id, start, hintOptions, workspaceId,
|
|
165
|
+
);
|
|
181
166
|
const terminalOperationScope = operationScope && !operationScope.files && (operationScope.diagnostics ?? []).some((d) => d.resolutionStage === 'operation' || d.resolutionStage === 'implementation');
|
|
182
|
-
const sourceScope = operationScope?.files || terminalOperationScope ? operationScope : sourceFilesForStart(db, repo?.id, start);
|
|
167
|
+
const sourceScope = operationScope?.files || terminalOperationScope ? operationScope : sourceFilesForStart(db, repo?.id, start, workspaceId);
|
|
168
|
+
const terminalSelectorScope = Boolean(sourceScope?.diagnostics?.length && !sourceScope.files);
|
|
183
169
|
const sourceFiles = sourceScope?.files;
|
|
184
170
|
const hasSelector = Boolean(
|
|
185
171
|
start.handler ?? start.operation ?? start.operationPath ?? start.servicePath,
|
|
@@ -191,9 +177,12 @@ function startScope(db: Db, start: TraceStart, hintOptions: ImplementationHintOp
|
|
|
191
177
|
executionRepoId: sourceScope?.repoId ?? repo?.id,
|
|
192
178
|
sourceFiles,
|
|
193
179
|
symbolIds: sourceScope?.symbols,
|
|
194
|
-
selectorMatched: !terminalOperationScope &&
|
|
180
|
+
selectorMatched: !terminalOperationScope && !terminalSelectorScope
|
|
181
|
+
&& (!hasSelector || sourceFiles !== undefined),
|
|
195
182
|
startOperationId: operationScope?.operationId,
|
|
196
|
-
startDiagnostics: operationScope?.diagnostics
|
|
183
|
+
startDiagnostics: operationScope?.diagnostics?.length
|
|
184
|
+
? operationScope.diagnostics
|
|
185
|
+
: sourceScope?.diagnostics,
|
|
197
186
|
};
|
|
198
187
|
}
|
|
199
188
|
function handlerFilesForOperation(db: Db, operationId: string): Set<string> {
|
|
@@ -211,8 +200,19 @@ function handlerFilesForOperation(db: Db, operationId: string): Set<string> {
|
|
|
211
200
|
.prepare(
|
|
212
201
|
`SELECT DISTINCT hc.source_file sourceFile,sym.id symbolId FROM handler_classes hc
|
|
213
202
|
JOIN handler_methods hm ON hm.handler_class_id=hc.id
|
|
214
|
-
LEFT JOIN symbols sym ON sym.repo_id=hc.repo_id
|
|
215
|
-
|
|
203
|
+
LEFT JOIN symbols sym ON sym.repo_id=hc.repo_id
|
|
204
|
+
AND sym.source_file=hc.source_file
|
|
205
|
+
AND sym.qualified_name=hc.class_name || '.' || hm.method_name
|
|
206
|
+
AND sym.start_line=hm.source_line
|
|
207
|
+
WHERE hc.repo_id=?
|
|
208
|
+
AND COALESCE(json_extract(hm.decorator_resolution_json,'$.handlerKind'),
|
|
209
|
+
CASE WHEN hm.decorator_kind='Event' THEN 'event'
|
|
210
|
+
WHEN hm.decorator_kind IN ('Action','Func','On') THEN 'operation'
|
|
211
|
+
ELSE 'unsupported' END)='operation'
|
|
212
|
+
AND COALESCE(json_extract(hm.decorator_resolution_json,'$.executable'),
|
|
213
|
+
CASE WHEN hm.decorator_kind IN ('Action','Func','On')
|
|
214
|
+
THEN 1 ELSE 0 END)=1
|
|
215
|
+
AND (hm.decorator_value=? OR hm.method_name=? OR hm.decorator_value=?)`,
|
|
216
216
|
)
|
|
217
217
|
.all(op.repoId, operation, operation, op.operationName) as Array<{
|
|
218
218
|
sourceFile?: string;
|
|
@@ -230,7 +230,9 @@ function handlerMethodNode(db: Db, methodId: string): Record<string, unknown> |
|
|
|
230
230
|
function implementationScope(db: Db, operationId: string): { repoId?: number; files: Set<string>; symbolId?: number; edge?: GraphRow } {
|
|
231
231
|
const edge = implementationEdge(db, operationId);
|
|
232
232
|
if (!edge || edge.status !== 'resolved') return { files: new Set(), edge };
|
|
233
|
-
const row = db.prepare(
|
|
233
|
+
const row = db.prepare("SELECT hc.repo_id repoId,hc.source_file sourceFile,s.id symbolId FROM handler_methods hm JOIN handler_classes hc ON hc.id=hm.handler_class_id LEFT JOIN symbols s ON s.repo_id=hc.repo_id AND s.source_file=hc.source_file AND s.qualified_name=hc.class_name || '.' || hm.method_name AND s.start_line=hm.source_line WHERE hm.id=?").get(edge.to_id) as { repoId?: number; sourceFile?: string; symbolId?: number } | undefined;
|
|
234
|
+
if (!row || typeof row.symbolId !== 'number')
|
|
235
|
+
return { repoId: row?.repoId, files: new Set(), edge };
|
|
234
236
|
return { repoId: row?.repoId, files: new Set(row?.sourceFile ? [row.sourceFile] : []), symbolId: row?.symbolId, edge };
|
|
235
237
|
}
|
|
236
238
|
function implementationMethodIdFromHint(edge: GraphRow | undefined, options: ImplementationHintOptions): ImplementationSelection {
|
|
@@ -257,8 +259,8 @@ function contextImplementationMethodId(edge: GraphRow | undefined, callerRepoId:
|
|
|
257
259
|
return { blocksAutomatic: false, evidence: hinted.evidence.reason === 'no_scoped_hint_matched_edge' ? hinted.evidence : { status: 'tied', tieReason: scores.length > 1 ? 'duplicate_helper_implementation_candidates' : 'no_unique_materially_stronger_candidate', candidateScores: scores } };
|
|
258
260
|
}
|
|
259
261
|
function handlerScope(db: Db, methodId: string): { repoId?: number; files: Set<string>; symbolId?: number } | undefined {
|
|
260
|
-
const row = db.prepare(
|
|
261
|
-
if (!row) return undefined;
|
|
262
|
+
const row = db.prepare("SELECT hc.repo_id repoId,hc.source_file sourceFile,s.id symbolId FROM handler_methods hm JOIN handler_classes hc ON hc.id=hm.handler_class_id LEFT JOIN symbols s ON s.repo_id=hc.repo_id AND s.source_file=hc.source_file AND s.qualified_name=hc.class_name || '.' || hm.method_name AND s.start_line=hm.source_line WHERE hm.id=?").get(methodId) as { repoId?: number; sourceFile?: string; symbolId?: number } | undefined;
|
|
263
|
+
if (!row || typeof row.symbolId !== 'number') return undefined;
|
|
262
264
|
return { repoId: row.repoId, files: new Set(row.sourceFile ? [row.sourceFile] : []), symbolId: row.symbolId };
|
|
263
265
|
}
|
|
264
266
|
|
|
@@ -491,22 +493,25 @@ export function trace(
|
|
|
491
493
|
options: TraceOptions,
|
|
492
494
|
): TraceResult {
|
|
493
495
|
const hintOptions = { implementationRepo: options.implementationRepo, implementationHints: options.implementationHints };
|
|
494
|
-
const scope = startScope(db, start, hintOptions);
|
|
495
|
-
const
|
|
496
|
-
.
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
496
|
+
const scope = startScope(db, start, hintOptions, options.workspaceId);
|
|
497
|
+
const hasSelector = Boolean(start.repo || start.handler || start.operation
|
|
498
|
+
|| start.operationPath || start.servicePath);
|
|
499
|
+
const diagnosticRepoId = scope.executionRepoId ?? scope.repo?.id;
|
|
500
|
+
const diagnostics = loadTraceDiagnostics(
|
|
501
|
+
db,
|
|
502
|
+
diagnosticRepoId,
|
|
503
|
+
!hasSelector,
|
|
504
|
+
options.workspaceId,
|
|
505
|
+
);
|
|
506
|
+
const stale = diagnosticRepoId !== undefined || !hasSelector
|
|
507
|
+
? db.prepare('SELECT name,graph_stale_reason reason FROM repositories WHERE graph_stale_reason IS NOT NULL AND (? IS NULL OR id=?) AND (? IS NULL OR workspace_id=?) ORDER BY name,id').all(diagnosticRepoId, diagnosticRepoId, options.workspaceId, options.workspaceId) as Array<{ name?: string; reason?: string }>
|
|
508
|
+
: [];
|
|
501
509
|
for (const row of stale)
|
|
502
|
-
diagnostics
|
|
503
|
-
for (const diagnostic of scope.startDiagnostics ?? [])
|
|
510
|
+
prependTraceDiagnostic(diagnostics, { severity: 'warning', code: 'graph_stale', message: `Graph is stale for ${row.name ?? 'repository'}: ${row.reason ?? 'facts_changed'}. Run service-flow link.` });
|
|
511
|
+
for (const diagnostic of scope.startDiagnostics ?? [])
|
|
512
|
+
prependTraceDiagnostic(diagnostics, diagnostic);
|
|
504
513
|
if (!scope.selectorMatched && !(scope.startDiagnostics?.length))
|
|
505
|
-
diagnostics
|
|
506
|
-
severity: 'warning',
|
|
507
|
-
code: 'trace_start_not_found',
|
|
508
|
-
message: start.servicePath && !start.operation && !start.operationPath && !start.handler ? 'Service-only trace requires --operation or --path and will not broaden to the whole workspace' : 'No handler source matched the requested trace start selector',
|
|
509
|
-
});
|
|
514
|
+
prependTraceDiagnostic(diagnostics, selectorNotFoundDiagnostic(start));
|
|
510
515
|
const maxDepth = positiveDepth(options.depth);
|
|
511
516
|
const edges: TraceEdge[] = [];
|
|
512
517
|
const nodes = new Map<string, Record<string, unknown>>();
|
|
@@ -539,9 +544,9 @@ export function trace(
|
|
|
539
544
|
seenScopes.add(key);
|
|
540
545
|
const calls = db
|
|
541
546
|
.prepare(
|
|
542
|
-
`SELECT c.*,r.name repoName FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id WHERE (? IS NULL OR c.repo_id=?) ORDER BY c.source_file,c.source_line`,
|
|
547
|
+
`SELECT c.*,r.name repoName FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id WHERE (? IS NULL OR c.repo_id=?) AND (? IS NULL OR r.workspace_id=?) ORDER BY c.source_file,c.source_line`,
|
|
543
548
|
)
|
|
544
|
-
.all(current.repoId, current.repoId) as CallRow[];
|
|
549
|
+
.all(current.repoId, current.repoId, options.workspaceId, options.workspaceId) as CallRow[];
|
|
545
550
|
const filtered = calls.filter(
|
|
546
551
|
(c) =>
|
|
547
552
|
(!current.symbolIds || current.symbolIds.has(Number(c.source_symbol_id))) && (!current.files || current.files.has(String(c.source_file))) &&
|
|
@@ -623,7 +628,7 @@ export function trace(
|
|
|
623
628
|
if (implementation.edge) {
|
|
624
629
|
const implEvidence = JSON.parse(String(implementation.edge.evidence_json || '{}')) as Record<string, unknown>;
|
|
625
630
|
const hintDiagnostic = implementationHintDiagnostic(contextSelection, implEvidence.implementationHintSuggestions);
|
|
626
|
-
if (hintDiagnostic) diagnostics
|
|
631
|
+
if (hintDiagnostic) prependTraceDiagnostic(diagnostics, hintDiagnostic);
|
|
627
632
|
const handlerNode = implementation.edge.status === 'resolved' ? handlerMethodNode(db, implementation.edge.to_id) : contextNode;
|
|
628
633
|
const implTo = handlerNode?.label ? String(handlerNode.label) : `${implementation.edge.to_kind}:${implementation.edge.to_id}`;
|
|
629
634
|
if (handlerNode) nodes.set(String(handlerNode.id), handlerNode);
|
|
@@ -674,6 +679,8 @@ export function trace(
|
|
|
674
679
|
}
|
|
675
680
|
}
|
|
676
681
|
const runtimeDiagnostic = runtimeVariableDiagnostic(edges);
|
|
677
|
-
if (runtimeDiagnostic) diagnostics
|
|
682
|
+
if (runtimeDiagnostic) prependTraceDiagnostic(diagnostics, runtimeDiagnostic);
|
|
683
|
+
for (const diagnostic of runtimeNoCandidateDiagnostics(edges))
|
|
684
|
+
prependTraceDiagnostic(diagnostics, diagnostic);
|
|
678
685
|
return { start, nodes: [...nodes.values()], edges, diagnostics };
|
|
679
686
|
}
|
package/src/types.ts
CHANGED
|
@@ -95,14 +95,34 @@ export interface HandlerClassFact {
|
|
|
95
95
|
sourceFile: string;
|
|
96
96
|
sourceLine: number;
|
|
97
97
|
methods: HandlerMethodFact[];
|
|
98
|
-
|
|
98
|
+
hasHandlerDecorator?: boolean;
|
|
99
|
+
classDecoratorNames?: string[];
|
|
100
|
+
observedDecoratorNames?: string[];
|
|
101
|
+
unsupportedDecoratorNames?: string[];
|
|
102
|
+
}
|
|
103
|
+
export type HandlerMethodKind =
|
|
104
|
+
| 'operation'
|
|
105
|
+
| 'entity_lifecycle'
|
|
106
|
+
| 'event'
|
|
107
|
+
| 'unsupported_lifecycle'
|
|
108
|
+
| 'unsupported_decorator';
|
|
109
|
+
export type HandlerLifecyclePhase = 'on' | 'before' | 'after';
|
|
110
|
+
export type HandlerLifecycleEvent = 'CREATE' | 'READ' | 'UPDATE' | 'DELETE';
|
|
99
111
|
export interface HandlerMethodFact {
|
|
100
112
|
methodName: string;
|
|
101
113
|
decoratorKind: string;
|
|
102
114
|
decoratorValue?: string;
|
|
103
115
|
decoratorRawExpression: string;
|
|
116
|
+
handlerKind?: HandlerMethodKind;
|
|
117
|
+
executable?: boolean;
|
|
118
|
+
lifecyclePhase?: HandlerLifecyclePhase;
|
|
119
|
+
lifecycleEvent?: HandlerLifecycleEvent;
|
|
104
120
|
decoratorResolution: {
|
|
105
121
|
rawExpression: string;
|
|
122
|
+
decoratorExpression?: string;
|
|
123
|
+
argumentExpression?: string;
|
|
124
|
+
resolvedDecoratorKind?: string;
|
|
125
|
+
decoratorImportSource?: string;
|
|
106
126
|
resolvedValue?: string;
|
|
107
127
|
resolutionKind:
|
|
108
128
|
| 'literal'
|
|
@@ -110,8 +130,13 @@ export interface HandlerMethodFact {
|
|
|
110
130
|
| 'enum_member'
|
|
111
131
|
| 'const_object_property'
|
|
112
132
|
| 'generated_constant_name'
|
|
133
|
+
| 'lifecycle_implicit'
|
|
113
134
|
| 'unresolved';
|
|
114
135
|
unresolvedReason?: string;
|
|
136
|
+
handlerKind?: HandlerMethodKind;
|
|
137
|
+
executable?: boolean;
|
|
138
|
+
lifecyclePhase?: HandlerLifecyclePhase;
|
|
139
|
+
lifecycleEvent?: HandlerLifecycleEvent;
|
|
115
140
|
};
|
|
116
141
|
sourceFile: string;
|
|
117
142
|
sourceLine: number;
|
|
@@ -202,6 +227,7 @@ export interface ImplementationHint {
|
|
|
202
227
|
export type DynamicMode = 'strict' | 'candidates' | 'infer';
|
|
203
228
|
export interface TraceOptions {
|
|
204
229
|
depth: number;
|
|
230
|
+
workspaceId?: number;
|
|
205
231
|
vars?: Record<string, string>;
|
|
206
232
|
includeExternal?: boolean;
|
|
207
233
|
includeDb?: boolean;
|