@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,335 @@
|
|
|
1
|
+
import type { Db } from '../db/connection.js';
|
|
2
|
+
import type { TraceStart } from '../types.js';
|
|
3
|
+
import { implementationHintDiagnostic } from './implementation-hints.js';
|
|
4
|
+
import { hintedImplementationSelection } from './005-implementation-selection.js';
|
|
5
|
+
import { implementationStartDiagnostic } from './007-implementation-start-diagnostic.js';
|
|
6
|
+
import { parseTraceEvidence } from './017-trace-context.js';
|
|
7
|
+
import { shouldDeferTraceStartSelection } from './022-trace-fact-preflight.js';
|
|
8
|
+
import {
|
|
9
|
+
handlerScope,
|
|
10
|
+
implementationScope,
|
|
11
|
+
type ImplementationHintOptions,
|
|
12
|
+
} from './025-trace-implementation-scope.js';
|
|
13
|
+
import {
|
|
14
|
+
ambiguousStartDiagnostic,
|
|
15
|
+
selectorRepoAmbiguousDiagnostic,
|
|
16
|
+
selectorRepoNotFoundDiagnostic,
|
|
17
|
+
sourceScopeForSelector,
|
|
18
|
+
} from './selectors.js';
|
|
19
|
+
|
|
20
|
+
interface RepoRef {
|
|
21
|
+
id: number;
|
|
22
|
+
name: string;
|
|
23
|
+
packageName?: string;
|
|
24
|
+
workspaceId: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface OperationRow extends Record<string, unknown> {
|
|
28
|
+
operationId?: string | number;
|
|
29
|
+
repoName?: string;
|
|
30
|
+
servicePath?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface OperationStartScope {
|
|
34
|
+
files?: Set<string>;
|
|
35
|
+
symbols?: Set<number>;
|
|
36
|
+
repoId?: number;
|
|
37
|
+
operationId?: string;
|
|
38
|
+
diagnostics?: Array<Record<string, unknown>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TraceStartScope {
|
|
42
|
+
repo?: RepoRef;
|
|
43
|
+
executionRepoId?: number;
|
|
44
|
+
sourceFiles?: Set<string>;
|
|
45
|
+
symbolIds?: Set<number>;
|
|
46
|
+
selectorMatched: boolean;
|
|
47
|
+
startOperationId?: string;
|
|
48
|
+
startDiagnostics?: Array<Record<string, unknown>>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeOperation(value: string | undefined): string | undefined {
|
|
52
|
+
if (!value) return undefined;
|
|
53
|
+
return value.startsWith('/') ? value.slice(1) : value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function operationRows(
|
|
57
|
+
db: Db,
|
|
58
|
+
repoId: number | undefined,
|
|
59
|
+
start: TraceStart,
|
|
60
|
+
workspaceId: number | undefined,
|
|
61
|
+
requested: string,
|
|
62
|
+
): OperationRow[] {
|
|
63
|
+
return db.prepare(`SELECT o.id operationId,o.operation_name operationName,
|
|
64
|
+
o.operation_path operationPath,o.source_file sourceFile,
|
|
65
|
+
o.source_line sourceLine,s.service_path servicePath,
|
|
66
|
+
r.id repoId,r.name repoName
|
|
67
|
+
FROM cds_operations o
|
|
68
|
+
JOIN cds_services s ON s.id=o.service_id
|
|
69
|
+
JOIN repositories r ON r.id=s.repo_id
|
|
70
|
+
WHERE (? IS NULL OR r.workspace_id=?) AND (? IS NULL OR r.id=?)
|
|
71
|
+
AND (? IS NULL OR s.service_path=?)
|
|
72
|
+
AND (o.operation_name=? OR o.operation_path=? OR o.operation_path=?)
|
|
73
|
+
ORDER BY r.name,s.service_path,o.operation_name,o.id`).all(
|
|
74
|
+
workspaceId, workspaceId, repoId, repoId,
|
|
75
|
+
start.servicePath, start.servicePath, requested, requested,
|
|
76
|
+
requested.startsWith('/') ? requested : `/${requested}`,
|
|
77
|
+
) as OperationRow[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function operationAmbiguity(
|
|
81
|
+
requested: string,
|
|
82
|
+
start: TraceStart,
|
|
83
|
+
rows: OperationRow[],
|
|
84
|
+
): Record<string, unknown> | undefined {
|
|
85
|
+
const repoCount = new Set(rows.map((row) => String(row.repoName))).size;
|
|
86
|
+
const services = rows.map((row) =>
|
|
87
|
+
`${String(row.repoName)}:${String(row.servicePath)}`);
|
|
88
|
+
if (!start.repo && repoCount > 1)
|
|
89
|
+
return ambiguousStartDiagnostic(requested, rows,
|
|
90
|
+
'Operation trace start matched multiple repositories; add --repo to disambiguate');
|
|
91
|
+
if (!start.servicePath && new Set(services).size > 1)
|
|
92
|
+
return ambiguousStartDiagnostic(requested, rows,
|
|
93
|
+
'Operation trace start matched multiple services; add --service to disambiguate');
|
|
94
|
+
return rows.length !== 1
|
|
95
|
+
? ambiguousStartDiagnostic(requested, rows,
|
|
96
|
+
'Operation trace start matched multiple indexed operations')
|
|
97
|
+
: undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolvedOperationStart(
|
|
101
|
+
db: Db,
|
|
102
|
+
operationId: string,
|
|
103
|
+
hintOptions: ImplementationHintOptions,
|
|
104
|
+
): OperationStartScope {
|
|
105
|
+
const implementation = implementationScope(db, operationId);
|
|
106
|
+
if (implementation.edge?.status === 'resolved'
|
|
107
|
+
&& implementation.files.size > 0) return {
|
|
108
|
+
files: implementation.files,
|
|
109
|
+
symbols: implementation.symbolId
|
|
110
|
+
? new Set([implementation.symbolId]) : undefined,
|
|
111
|
+
repoId: implementation.repoId,
|
|
112
|
+
operationId,
|
|
113
|
+
diagnostics: [],
|
|
114
|
+
};
|
|
115
|
+
return hintedOrUnresolvedStart(
|
|
116
|
+
db, operationId, implementation, hintOptions,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function hintedOrUnresolvedStart(
|
|
121
|
+
db: Db,
|
|
122
|
+
operationId: string,
|
|
123
|
+
implementation: ReturnType<typeof implementationScope>,
|
|
124
|
+
hintOptions: ImplementationHintOptions,
|
|
125
|
+
): OperationStartScope {
|
|
126
|
+
const hinted = hintedImplementationSelection(
|
|
127
|
+
db, implementation.edge, operationId, hintOptions,
|
|
128
|
+
);
|
|
129
|
+
if (hinted.methodId) {
|
|
130
|
+
const scope = handlerScope(db, hinted.methodId);
|
|
131
|
+
if (scope?.files.size) return {
|
|
132
|
+
files: scope.files,
|
|
133
|
+
symbols: scope.symbolId ? new Set([scope.symbolId]) : undefined,
|
|
134
|
+
repoId: scope.repoId,
|
|
135
|
+
operationId,
|
|
136
|
+
diagnostics: [],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return unresolvedOperationStart(operationId, implementation.edge, hinted);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function unresolvedOperationStart(
|
|
143
|
+
operationId: string,
|
|
144
|
+
edge: ReturnType<typeof implementationScope>['edge'],
|
|
145
|
+
hinted: ReturnType<typeof hintedImplementationSelection>,
|
|
146
|
+
): OperationStartScope {
|
|
147
|
+
if (!edge) return {
|
|
148
|
+
operationId,
|
|
149
|
+
diagnostics: [{
|
|
150
|
+
severity: 'warning',
|
|
151
|
+
code: 'trace_start_implementation_unresolved',
|
|
152
|
+
message: 'Indexed operation matched but no implementation candidate exists',
|
|
153
|
+
resolutionStage: 'implementation',
|
|
154
|
+
resolutionStatus: 'operation_without_implementation',
|
|
155
|
+
}],
|
|
156
|
+
};
|
|
157
|
+
const evidence = parseTraceEvidence(edge.evidence_json);
|
|
158
|
+
const hintDiagnostic = implementationHintDiagnostic(hinted, evidence);
|
|
159
|
+
const diagnostic = implementationStartDiagnostic(edge, evidence);
|
|
160
|
+
return {
|
|
161
|
+
operationId,
|
|
162
|
+
diagnostics: hintDiagnostic ? [hintDiagnostic, diagnostic] : [diagnostic],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function operationStartScope(
|
|
167
|
+
db: Db,
|
|
168
|
+
repoId: number | undefined,
|
|
169
|
+
start: TraceStart,
|
|
170
|
+
hintOptions: ImplementationHintOptions,
|
|
171
|
+
workspaceId: number | undefined,
|
|
172
|
+
): OperationStartScope | undefined {
|
|
173
|
+
const requested = normalizeOperation(start.operationPath ?? start.operation);
|
|
174
|
+
if (!requested) return undefined;
|
|
175
|
+
const rows = operationRows(db, repoId, start, workspaceId, requested);
|
|
176
|
+
if (rows.length === 0) return undefined;
|
|
177
|
+
const diagnostic = operationAmbiguity(requested, start, rows);
|
|
178
|
+
if (diagnostic) return { diagnostics: [diagnostic] };
|
|
179
|
+
return resolvedOperationStart(db, String(rows[0]?.operationId), hintOptions);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function matchingRepos(
|
|
183
|
+
db: Db,
|
|
184
|
+
start: TraceStart,
|
|
185
|
+
workspaceId: number | undefined,
|
|
186
|
+
): RepoRef[] {
|
|
187
|
+
if (!start.repo) return [];
|
|
188
|
+
const rows = db.prepare(`SELECT id,name,package_name packageName,
|
|
189
|
+
workspace_id workspaceId
|
|
190
|
+
FROM repositories
|
|
191
|
+
WHERE (? IS NULL OR workspace_id=?)
|
|
192
|
+
AND (name=? OR package_name=?)
|
|
193
|
+
ORDER BY name COLLATE BINARY,absolute_path COLLATE BINARY,id`).all(
|
|
194
|
+
workspaceId, workspaceId, start.repo, start.repo,
|
|
195
|
+
);
|
|
196
|
+
return rows.flatMap((row) =>
|
|
197
|
+
typeof row.id === 'number' && typeof row.name === 'string'
|
|
198
|
+
&& typeof row.workspaceId === 'number'
|
|
199
|
+
? [{
|
|
200
|
+
id: row.id,
|
|
201
|
+
name: row.name,
|
|
202
|
+
packageName: typeof row.packageName === 'string'
|
|
203
|
+
? row.packageName : undefined,
|
|
204
|
+
workspaceId: row.workspaceId,
|
|
205
|
+
}]
|
|
206
|
+
: []);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function repoSelectionDiagnostic(
|
|
210
|
+
start: TraceStart,
|
|
211
|
+
repos: RepoRef[],
|
|
212
|
+
): TraceStartScope | undefined {
|
|
213
|
+
if (start.repo && repos.length === 0) return {
|
|
214
|
+
selectorMatched: false,
|
|
215
|
+
startDiagnostics: [selectorRepoNotFoundDiagnostic(start.repo)],
|
|
216
|
+
};
|
|
217
|
+
if (start.repo && repos.length > 1) return {
|
|
218
|
+
selectorMatched: false,
|
|
219
|
+
startDiagnostics: [selectorRepoAmbiguousDiagnostic(start.repo, repos)],
|
|
220
|
+
};
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function terminalOperationScope(
|
|
225
|
+
scope: OperationStartScope | undefined,
|
|
226
|
+
): boolean {
|
|
227
|
+
return Boolean(scope && !scope.files && (scope.diagnostics ?? []).some(
|
|
228
|
+
(diagnostic) => diagnostic.resolutionStage === 'operation'
|
|
229
|
+
|| diagnostic.resolutionStage === 'implementation',
|
|
230
|
+
));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function hasTraceSelector(start: TraceStart): boolean {
|
|
234
|
+
return Boolean(
|
|
235
|
+
start.handler ?? start.operation ?? start.operationPath ?? start.servicePath,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function serviceOnlyStart(start: TraceStart): boolean {
|
|
240
|
+
return Boolean(start.servicePath)
|
|
241
|
+
&& !start.operation && !start.operationPath && !start.handler;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function selectedExecutionRepoId(
|
|
245
|
+
source: ReturnType<typeof sourceScopeForSelector>,
|
|
246
|
+
repo: RepoRef | undefined,
|
|
247
|
+
): number | undefined {
|
|
248
|
+
return source?.repoId ?? repo?.id;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function startDiagnostics(
|
|
252
|
+
operation: OperationStartScope | undefined,
|
|
253
|
+
source: ReturnType<typeof sourceScopeForSelector>,
|
|
254
|
+
): Array<Record<string, unknown>> | undefined {
|
|
255
|
+
const operationDiagnostics = operation?.diagnostics;
|
|
256
|
+
return operationDiagnostics && operationDiagnostics.length > 0
|
|
257
|
+
? operationDiagnostics : source?.diagnostics;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function startSelectorMatched(
|
|
261
|
+
start: TraceStart,
|
|
262
|
+
operation: OperationStartScope | undefined,
|
|
263
|
+
source: ReturnType<typeof sourceScopeForSelector>,
|
|
264
|
+
): boolean {
|
|
265
|
+
if (terminalOperationScope(operation)) return false;
|
|
266
|
+
if (source?.diagnostics?.length && !source.files) return false;
|
|
267
|
+
if (!hasTraceSelector(start)) return true;
|
|
268
|
+
return source?.files !== undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function selectedStartScope(
|
|
272
|
+
start: TraceStart,
|
|
273
|
+
repo: RepoRef | undefined,
|
|
274
|
+
operation: OperationStartScope | undefined,
|
|
275
|
+
source: ReturnType<typeof sourceScopeForSelector>,
|
|
276
|
+
): TraceStartScope {
|
|
277
|
+
const sourceFiles = source?.files;
|
|
278
|
+
if (serviceOnlyStart(start)) return { repo, selectorMatched: false };
|
|
279
|
+
return {
|
|
280
|
+
repo,
|
|
281
|
+
executionRepoId: selectedExecutionRepoId(source, repo),
|
|
282
|
+
sourceFiles,
|
|
283
|
+
symbolIds: source?.symbols,
|
|
284
|
+
selectorMatched: startSelectorMatched(start, operation, source),
|
|
285
|
+
startOperationId: operation?.operationId,
|
|
286
|
+
startDiagnostics: startDiagnostics(operation, source),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function deferredStartScope(
|
|
291
|
+
db: Db,
|
|
292
|
+
workspaceId: number | undefined,
|
|
293
|
+
repo: RepoRef | undefined,
|
|
294
|
+
): TraceStartScope | undefined {
|
|
295
|
+
if (!shouldDeferTraceStartSelection(db, workspaceId, repo?.id))
|
|
296
|
+
return undefined;
|
|
297
|
+
return {
|
|
298
|
+
repo,
|
|
299
|
+
executionRepoId: repo?.id,
|
|
300
|
+
selectorMatched: true,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function selectorSourceScope(
|
|
305
|
+
db: Db,
|
|
306
|
+
start: TraceStart,
|
|
307
|
+
repo: RepoRef | undefined,
|
|
308
|
+
operation: OperationStartScope | undefined,
|
|
309
|
+
workspaceId: number | undefined,
|
|
310
|
+
): ReturnType<typeof sourceScopeForSelector> {
|
|
311
|
+
if (operation?.files || terminalOperationScope(operation)) return operation;
|
|
312
|
+
return sourceScopeForSelector(db, repo?.id, start, workspaceId);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function resolveTraceStartScope(
|
|
316
|
+
db: Db,
|
|
317
|
+
start: TraceStart,
|
|
318
|
+
hintOptions: ImplementationHintOptions,
|
|
319
|
+
workspaceId: number | undefined,
|
|
320
|
+
): TraceStartScope {
|
|
321
|
+
const repos = matchingRepos(db, start, workspaceId);
|
|
322
|
+
const exactRepo = repos.length === 1 ? repos[0] : undefined;
|
|
323
|
+
const deferred = deferredStartScope(db, workspaceId, exactRepo);
|
|
324
|
+
if (deferred) return deferred;
|
|
325
|
+
const repoDiagnostic = repoSelectionDiagnostic(start, repos);
|
|
326
|
+
if (repoDiagnostic) return repoDiagnostic;
|
|
327
|
+
const repo = repos[0];
|
|
328
|
+
const operation = operationStartScope(
|
|
329
|
+
db, repo?.id, start, hintOptions, workspaceId,
|
|
330
|
+
);
|
|
331
|
+
const source = selectorSourceScope(
|
|
332
|
+
db, start, repo, operation, workspaceId,
|
|
333
|
+
);
|
|
334
|
+
return selectedStartScope(start, repo, operation, source);
|
|
335
|
+
}
|