@saptools/service-flow 0.1.68 → 0.1.70
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 +11 -5
- package/TECHNICAL-NOTE.md +13 -0
- package/dist/{chunk-AEM4JY22.js → chunk-GSLFY6J2.js} +6986 -5503
- package/dist/chunk-GSLFY6J2.js.map +1 -0
- package/dist/cli.js +334 -122
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/{002-doctor-lifecycle.ts → doctor-lifecycle.ts} +1 -1
- package/src/cli/{001-doctor-projection.ts → doctor-projection.ts} +1 -1
- package/src/cli/doctor.ts +5 -5
- package/src/cli/index-summary.ts +22 -0
- package/src/cli.ts +155 -91
- package/src/db/{009-binding-fact-semantics.ts → binding-fact-semantics.ts} +6 -1
- package/src/db/binding-helper-provenance.ts +17 -0
- package/src/db/{012-binding-reference-proof.ts → binding-reference-proof.ts} +4 -0
- package/src/db/{000-call-fact-repository.ts → call-fact-repository.ts} +49 -23
- package/src/db/{003-current-fact-semantics.ts → current-fact-semantics.ts} +5 -4
- package/src/db/{001-fact-lifecycle.ts → fact-lifecycle.ts} +3 -3
- package/src/db/index-publication-failure.ts +91 -0
- package/src/db/{007-package-fact-semantics.ts → package-fact-semantics.ts} +6 -6
- package/src/db/{010-package-symbol-surface-semantics.ts → package-symbol-surface-semantics.ts} +2 -2
- package/src/db/{004-package-target-invalidation.ts → package-target-invalidation.ts} +11 -1
- package/src/db/{008-relative-fact-semantics.ts → relative-fact-semantics.ts} +9 -6
- package/src/db/{006-relative-symbol-resolution.ts → relative-symbol-resolution.ts} +28 -7
- package/src/db/repositories.ts +24 -7
- package/src/db/{011-symbol-call-semantics.ts → symbol-call-semantics.ts} +1 -1
- package/src/index.ts +3 -3
- package/src/indexer/cds-extension-resolver.ts +27 -3
- package/src/indexer/repository-indexer.ts +70 -9
- package/src/indexer/workspace-indexer.ts +142 -30
- package/src/linker/call-edge-insertion.ts +568 -0
- package/src/linker/{002-call-evidence.ts → call-evidence.ts} +2 -2
- package/src/linker/cross-repo-linker.ts +6 -169
- package/src/linker/dynamic-edge-resolver.ts +1 -1
- package/src/linker/{004-event-subscription-handler-linker.ts → event-subscription-handler-linker.ts} +32 -11
- package/src/linker/event-template-link.ts +72 -0
- package/src/linker/external-http-target.ts +1 -1
- package/src/linker/helper-package-linker.ts +1 -1
- package/src/linker/{000-implementation-candidates.ts → implementation-candidates.ts} +1 -1
- package/src/linker/{001-implementation-evidence-projection.ts → implementation-evidence-projection.ts} +1 -1
- package/src/linker/odata-path-normalizer.ts +1 -1
- package/src/linker/{005-odata-path-structure.ts → odata-path-structure.ts} +1 -1
- package/src/linker/{003-package-import-symbol-resolver.ts → package-import-symbol-resolver.ts} +3 -3
- package/src/linker/service-resolver.ts +2 -2
- package/src/output/{001-compact-json-output.ts → compact-json-output.ts} +1 -1
- package/src/parsers/{006-binding-identity.ts → binding-identity.ts} +9 -4
- package/src/parsers/{011-binding-lexical-scope.ts → binding-lexical-scope.ts} +1 -1
- package/src/parsers/{021-binding-visibility.ts → binding-visibility.ts} +18 -2
- package/src/parsers/{000-direct-query-execution.ts → direct-query-execution.ts} +11 -0
- package/src/parsers/{005-event-subscription-facts.ts → event-subscription-facts.ts} +2 -2
- package/src/parsers/{016-local-symbol-reference.ts → local-symbol-reference.ts} +2 -2
- package/src/parsers/operation-path-analysis.ts +1 -1
- package/src/parsers/outbound-call-classifier.ts +692 -0
- package/src/parsers/outbound-call-parser.ts +146 -509
- package/src/parsers/outbound-expression-analysis.ts +700 -0
- package/src/parsers/{018-package-commonjs-syntax.ts → package-commonjs-syntax.ts} +1 -1
- package/src/parsers/{012-package-fact-contract.ts → package-fact-contract.ts} +3 -3
- package/src/parsers/{010-package-public-surface-analysis.ts → package-public-surface-analysis.ts} +2 -2
- package/src/parsers/{003-package-public-surface.ts → package-public-surface.ts} +3 -3
- package/src/parsers/{008-package-surface-publication.ts → package-surface-publication.ts} +1 -1
- package/src/parsers/{001-query-entity-resolution.ts → query-entity-resolution.ts} +1 -1
- package/src/parsers/{015-service-binding-collector.ts → service-binding-collector.ts} +3 -3
- package/src/parsers/{014-service-binding-helper-flow.ts → service-binding-helper-flow.ts} +72 -6
- package/src/parsers/service-binding-parser-helpers.ts +1 -1
- package/src/parsers/service-binding-parser.ts +2 -2
- package/src/parsers/{007-source-fact-reconciliation.ts → source-fact-reconciliation.ts} +3 -3
- package/src/parsers/{020-stable-local-value.ts → stable-local-value.ts} +2 -2
- package/src/parsers/{009-symbol-call-facts.ts → symbol-call-facts.ts} +3 -3
- package/src/parsers/{017-symbol-derived-contexts.ts → symbol-derived-contexts.ts} +2 -2
- package/src/parsers/symbol-parser.ts +43 -11
- package/src/trace/{014-compact-contract.ts → compact-contract.ts} +6 -0
- package/src/trace/{021-compact-decision-normalization.ts → compact-decision-normalization.ts} +77 -11
- package/src/trace/{020-compact-field-projection.ts → compact-field-projection.ts} +77 -4
- package/src/trace/{024-compact-observation-decision.ts → compact-observation-decision.ts} +10 -5
- package/src/trace/{016-compact-projector.ts → compact-projector.ts} +6 -7
- package/src/trace/{018-compact-trace.ts → compact-trace.ts} +3 -3
- package/src/trace/{006-contextual-projection.ts → contextual-projection.ts} +1 -1
- package/src/trace/{008-contextual-runtime-state.ts → contextual-runtime-state.ts} +1 -1
- package/src/trace/{001-dynamic-identity.ts → dynamic-identity.ts} +2 -2
- package/src/trace/{003-dynamic-references.ts → dynamic-references.ts} +2 -2
- package/src/trace/dynamic-targets.ts +6 -6
- package/src/trace/event-runtime-resolution.ts +151 -0
- package/src/trace/{011-event-subscriber-traversal.ts → event-subscriber-traversal.ts} +101 -9
- package/src/trace/evidence.ts +2 -2
- package/src/trace/implementation-hints.ts +10 -7
- package/src/trace/{005-implementation-selection.ts → implementation-selection.ts} +2 -2
- package/src/trace/{007-implementation-start-diagnostic.ts → implementation-start-diagnostic.ts} +1 -0
- package/src/trace/local-call-expansion.ts +37 -0
- package/src/trace/{009-selected-handler-provenance.ts → selected-handler-provenance.ts} +1 -1
- package/src/trace/selectors.ts +2 -1
- package/src/trace/{017-trace-context.ts → trace-context.ts} +2 -2
- package/src/trace/{015-trace-edge-recorder.ts → trace-edge-recorder.ts} +62 -5
- package/src/trace/{019-trace-edge-semantics.ts → trace-edge-semantics.ts} +9 -13
- package/src/trace/trace-engine.ts +10 -10
- package/src/trace/{022-trace-fact-preflight.ts → trace-fact-preflight.ts} +2 -2
- package/src/trace/{025-trace-implementation-scope.ts → trace-implementation-scope.ts} +1 -1
- package/src/trace/{028-trace-operation-execution.ts → trace-operation-execution.ts} +10 -10
- package/src/trace/{013-trace-root-scopes.ts → trace-root-scopes.ts} +5 -6
- package/src/trace/{027-trace-scope-execution.ts → trace-scope-execution.ts} +44 -44
- package/src/trace/{029-trace-start-implementation.ts → trace-start-implementation.ts} +7 -7
- package/src/trace/{026-trace-start-scope.ts → trace-start-scope.ts} +6 -5
- package/src/trace/{010-traversal-scope.ts → traversal-scope.ts} +1 -1
- package/src/types.ts +2 -1
- package/src/version.ts +1 -1
- package/dist/chunk-AEM4JY22.js.map +0 -1
- /package/src/cli/{000-clean.ts → clean.ts} +0 -0
- /package/src/cli/{003-doctor-package-resolution.ts → doctor-package-resolution.ts} +0 -0
- /package/src/db/{002-fact-json-inventory.ts → fact-json-inventory.ts} +0 -0
- /package/src/db/{005-schema-structure.ts → schema-structure.ts} +0 -0
- /package/src/output/{000-stdout-policy.ts → stdout-policy.ts} +0 -0
- /package/src/parsers/{019-binding-assignment-targets.ts → binding-assignment-targets.ts} +0 -0
- /package/src/parsers/{013-executable-body-eligibility.ts → executable-body-eligibility.ts} +0 -0
- /package/src/parsers/{004-fact-identity.ts → fact-identity.ts} +0 -0
- /package/src/parsers/{002-symbol-import-bindings.ts → symbol-import-bindings.ts} +0 -0
- /package/src/trace/{004-dynamic-candidate-sources.ts → dynamic-candidate-sources.ts} +0 -0
- /package/src/trace/{000-dynamic-target-types.ts → dynamic-target-types.ts} +0 -0
- /package/src/trace/{023-nested-event-scopes.ts → nested-event-scopes.ts} +0 -0
- /package/src/trace/{002-trace-diagnostics.ts → trace-diagnostics.ts} +0 -0
- /package/src/trace/{012-trace-graph-lookups.ts → trace-graph-lookups.ts} +0 -0
- /package/src/utils/{000-bounded-projection.ts → bounded-projection.ts} +0 -0
- /package/src/utils/{001-placeholders.ts → placeholders.ts} +0 -0
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
TraceOptions,
|
|
5
5
|
TraceStart,
|
|
6
6
|
} from '../types.js';
|
|
7
|
-
import { compareBinary } from './
|
|
7
|
+
import { compareBinary } from './traversal-scope.js';
|
|
8
8
|
import type {
|
|
9
9
|
CompactDecisionInput,
|
|
10
10
|
CompactDecisionV1,
|
|
@@ -16,12 +16,15 @@ import type {
|
|
|
16
16
|
CompactQueryV1,
|
|
17
17
|
CompactStartV1,
|
|
18
18
|
CompactStatusCountsV1,
|
|
19
|
-
} from './
|
|
19
|
+
} from './compact-contract.js';
|
|
20
20
|
import {
|
|
21
21
|
compactMissingRemediation,
|
|
22
|
+
isSafeCompactReferenceName,
|
|
23
|
+
isSafeCompactSelectorSuggestion,
|
|
24
|
+
projectCompactReferenceGroup,
|
|
22
25
|
projectCompactMissingNames,
|
|
23
26
|
type CompactMissingNameProjection,
|
|
24
|
-
} from './
|
|
27
|
+
} from './compact-decision-normalization.js';
|
|
25
28
|
|
|
26
29
|
const compactDiagnosticMessages: Readonly<Record<string, string>> = {
|
|
27
30
|
schema_upgrade_required: 'The database schema must be upgraded before tracing.',
|
|
@@ -31,8 +34,19 @@ const compactDiagnosticMessages: Readonly<Record<string, string>> = {
|
|
|
31
34
|
implementation_hint_mismatch: 'The implementation hint did not select one implementation.',
|
|
32
35
|
selected_handler_provenance_mismatch: 'Selected handler provenance did not match its graph target.',
|
|
33
36
|
selected_handler_target_not_found: 'The selected handler target is not indexed.',
|
|
37
|
+
trace_start_ambiguous: 'The trace start selector is ambiguous.',
|
|
38
|
+
trace_start_not_found: 'The trace start selector did not match an indexed start.',
|
|
34
39
|
trace_start_implementation_unresolved: 'The trace start implementation is unresolved.',
|
|
35
40
|
};
|
|
41
|
+
const selectorDiagnosticCodes = new Set([
|
|
42
|
+
'handler_decorators_not_indexed',
|
|
43
|
+
'handler_methods_not_indexed',
|
|
44
|
+
'selector_repo_ambiguous',
|
|
45
|
+
'selector_repo_not_found',
|
|
46
|
+
'trace_start_ambiguous',
|
|
47
|
+
'trace_start_implementation_unresolved',
|
|
48
|
+
'trace_start_not_found',
|
|
49
|
+
]);
|
|
36
50
|
|
|
37
51
|
export function projectCompactDecision(
|
|
38
52
|
input: CompactDecisionInput | undefined,
|
|
@@ -87,6 +101,8 @@ function addImplementationDecision(
|
|
|
87
101
|
out.implementationGuided = input.implementationGuided;
|
|
88
102
|
if (input.implementationContextual !== undefined)
|
|
89
103
|
out.implementationContextual = input.implementationContextual;
|
|
104
|
+
if (input.tiedCandidateRepos)
|
|
105
|
+
out.tiedCandidateRepos = input.tiedCandidateRepos;
|
|
90
106
|
}
|
|
91
107
|
|
|
92
108
|
function addEventDecision(out: CompactDecisionV1, input: CompactDecisionInput): void {
|
|
@@ -243,6 +259,10 @@ function compactDiagnosticDetails(
|
|
|
243
259
|
const out: CompactDiagnosticDetailsV1 = {};
|
|
244
260
|
const reasonCode = compactSafeCode(value.reasonCode);
|
|
245
261
|
if (reasonCode) out.reasonCode = reasonCode;
|
|
262
|
+
if (selectorDiagnosticCodes.has(code)) addDiagnosticSelector(out, value);
|
|
263
|
+
if (code === 'reindex_required') addInvalidFactCategories(out, value);
|
|
264
|
+
if (code === 'implementation_hint_mismatch')
|
|
265
|
+
addImplementationHintCandidates(out, value);
|
|
246
266
|
if (code === 'trace_runtime_variables_missing') addDiagnosticNames(out, value);
|
|
247
267
|
addDiagnosticCounts(out, value);
|
|
248
268
|
const hint = compactDiagnosticRemediation(code, out);
|
|
@@ -255,6 +275,51 @@ function compactDiagnosticDetails(
|
|
|
255
275
|
return out;
|
|
256
276
|
}
|
|
257
277
|
|
|
278
|
+
function addDiagnosticSelector(
|
|
279
|
+
out: CompactDiagnosticDetailsV1,
|
|
280
|
+
value: Record<string, unknown>,
|
|
281
|
+
): void {
|
|
282
|
+
const selectorKind = compactSafeCode(value.selectorKind);
|
|
283
|
+
if (selectorKind) out.selectorKind = selectorKind;
|
|
284
|
+
const suggestions = projectCompactReferenceGroup(
|
|
285
|
+
compactStringArray(value.selectorSuggestions),
|
|
286
|
+
value.selectorSuggestionCount,
|
|
287
|
+
isSafeCompactSelectorSuggestion,
|
|
288
|
+
);
|
|
289
|
+
if (suggestions) out.selectorSuggestions = suggestions;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function addInvalidFactCategories(
|
|
293
|
+
out: CompactDiagnosticDetailsV1,
|
|
294
|
+
value: Record<string, unknown>,
|
|
295
|
+
): void {
|
|
296
|
+
const categories = compactRecordArray(value.invalidFactCategories)
|
|
297
|
+
.flatMap((item) =>
|
|
298
|
+
typeof item.category === 'string' ? [item.category] : []);
|
|
299
|
+
const projection = projectCompactReferenceGroup(
|
|
300
|
+
categories, value.invalidFactCategoryCount,
|
|
301
|
+
(item) => compactSafeCode(item) === item,
|
|
302
|
+
);
|
|
303
|
+
if (projection) out.invalidFactCategories = projection;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function addImplementationHintCandidates(
|
|
307
|
+
out: CompactDiagnosticDetailsV1,
|
|
308
|
+
value: Record<string, unknown>,
|
|
309
|
+
): void {
|
|
310
|
+
const repos = compactRecordArray(value.implementationHintSuggestions)
|
|
311
|
+
.flatMap((item) =>
|
|
312
|
+
typeof item.implementationRepo === 'string'
|
|
313
|
+
? [item.implementationRepo] : []);
|
|
314
|
+
const uniqueCount = new Set(repos.map((repo) => repo.trim())).size;
|
|
315
|
+
if (uniqueCount < 2) return;
|
|
316
|
+
const projection = projectCompactReferenceGroup(
|
|
317
|
+
repos, value.implementationHintSuggestionCount,
|
|
318
|
+
isSafeCompactReferenceName,
|
|
319
|
+
);
|
|
320
|
+
if (projection) out.tiedCandidateRepos = projection;
|
|
321
|
+
}
|
|
322
|
+
|
|
258
323
|
function addDiagnosticNames(
|
|
259
324
|
out: CompactDiagnosticDetailsV1,
|
|
260
325
|
value: Record<string, unknown>,
|
|
@@ -345,7 +410,8 @@ function compactDiagnosticHintCount(
|
|
|
345
410
|
|
|
346
411
|
function compactRemediationHint(code: string): string | undefined {
|
|
347
412
|
if (code === 'provide_runtime_variables') return 'Provide the missing variable names listed in details.';
|
|
348
|
-
if (code === 'select_implementation')
|
|
413
|
+
if (code === 'select_implementation')
|
|
414
|
+
return 'Use --implementation-hint with service, operation, package, repository, family, and repo keys; repo is required.';
|
|
349
415
|
if (code === 'reindex_and_link') return 'Force reindex, then force relink the workspace.';
|
|
350
416
|
if (code === 'inspect_detailed_edge') return 'Inspect the correlated detailed trace edge.';
|
|
351
417
|
return undefined;
|
|
@@ -389,6 +455,13 @@ function compactStringArray(value: unknown): string[] {
|
|
|
389
455
|
? value.filter((item): item is string => typeof item === 'string') : [];
|
|
390
456
|
}
|
|
391
457
|
|
|
458
|
+
function compactRecordArray(value: unknown): Array<Record<string, unknown>> {
|
|
459
|
+
return Array.isArray(value)
|
|
460
|
+
? value.filter((item): item is Record<string, unknown> =>
|
|
461
|
+
Boolean(item && typeof item === 'object' && !Array.isArray(item)))
|
|
462
|
+
: [];
|
|
463
|
+
}
|
|
464
|
+
|
|
392
465
|
function compactArrayLength(value: unknown): number {
|
|
393
466
|
return Array.isArray(value) ? value.length : 0;
|
|
394
467
|
}
|
|
@@ -2,14 +2,15 @@ import type {
|
|
|
2
2
|
CompactDecisionTargetInput,
|
|
3
3
|
CompactDecisionV1,
|
|
4
4
|
CompactEdgeObservation,
|
|
5
|
-
} from './
|
|
6
|
-
import { projectCompactDecision } from './
|
|
5
|
+
} from './compact-contract.js';
|
|
6
|
+
import { projectCompactDecision } from './compact-field-projection.js';
|
|
7
7
|
import { normalizeCompactDecisionEquivalence } from
|
|
8
|
-
'./
|
|
8
|
+
'./compact-decision-normalization.js';
|
|
9
9
|
|
|
10
10
|
export interface CompactDecisionNode {
|
|
11
11
|
key?: string;
|
|
12
12
|
decisionTarget?: string;
|
|
13
|
+
projectedIdentity?: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function resolvedNode(
|
|
@@ -40,7 +41,10 @@ function sameCanonicalNode(
|
|
|
40
41
|
left: CompactDecisionNode | undefined,
|
|
41
42
|
right: CompactDecisionNode,
|
|
42
43
|
): boolean {
|
|
43
|
-
|
|
44
|
+
if (left?.key === undefined) return false;
|
|
45
|
+
return left.projectedIdentity === true
|
|
46
|
+
? left.key === right.decisionTarget
|
|
47
|
+
: left.key === right.key;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
function persistedCanonical(
|
|
@@ -49,7 +53,7 @@ function persistedCanonical(
|
|
|
49
53
|
target: CompactDecisionNode,
|
|
50
54
|
): boolean {
|
|
51
55
|
if (!sameCanonicalNode(persisted, target)) return false;
|
|
52
|
-
return effective?.key === undefined || effective
|
|
56
|
+
return effective?.key === undefined || sameCanonicalNode(effective, target);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
export function projectObservationDecision(
|
|
@@ -71,6 +75,7 @@ export function projectObservationDecision(
|
|
|
71
75
|
target.decisionTarget,
|
|
72
76
|
sameCanonicalNode(effective, target),
|
|
73
77
|
persistedCanonical(persisted, effective, target),
|
|
78
|
+
source?.persistedResolutionStatus,
|
|
74
79
|
);
|
|
75
80
|
return decision;
|
|
76
81
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
2
|
import { redactText } from '../utils/redaction.js';
|
|
3
|
-
import { compareBinary as binaryCompare } from './
|
|
3
|
+
import { compareBinary as binaryCompare } from './traversal-scope.js';
|
|
4
4
|
import {
|
|
5
5
|
type CompactDecisionTargetInput,
|
|
6
6
|
type CompactProjectedDiagnostic,
|
|
@@ -17,16 +17,16 @@ import {
|
|
|
17
17
|
type CompactSemanticEndpoint,
|
|
18
18
|
type CompactSourceSite,
|
|
19
19
|
type CompactStatus,
|
|
20
|
-
} from './
|
|
20
|
+
} from './compact-contract.js';
|
|
21
21
|
import {
|
|
22
22
|
compactCompleteness, compactSafeCode, compactStatusCounts, compactStatusTotal,
|
|
23
23
|
projectCompactDecisionTarget, projectCompactDiagnostics,
|
|
24
24
|
projectCompactQuery, projectCompactStart,
|
|
25
|
-
} from './
|
|
25
|
+
} from './compact-field-projection.js';
|
|
26
26
|
import {
|
|
27
27
|
projectObservationDecision,
|
|
28
28
|
type CompactDecisionNode,
|
|
29
|
-
} from './
|
|
29
|
+
} from './compact-observation-decision.js';
|
|
30
30
|
|
|
31
31
|
const REFERENCE_LIMIT = 5;
|
|
32
32
|
|
|
@@ -111,9 +111,8 @@ function decisionInputNode(
|
|
|
111
111
|
return symbolNode(db, numeric) ?? {};
|
|
112
112
|
if (target.kind === 'handler_method' && numeric !== undefined)
|
|
113
113
|
return handlerNode(db, numeric) ?? {};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
114
|
+
const decisionTarget = projectCompactDecisionTarget(target.kind, target.id);
|
|
115
|
+
return { key: decisionTarget, decisionTarget, projectedIdentity: true };
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
function resolveEndpoint(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
2
|
import { CURRENT_SCHEMA_VERSION } from '../db/migrations.js';
|
|
3
|
-
import type { CompactGraphV1, CompactSourceContext } from './
|
|
4
|
-
import { CompactObservationCollector } from './
|
|
5
|
-
import { projectCompactGraph } from './
|
|
3
|
+
import type { CompactGraphV1, CompactSourceContext } from './compact-contract.js';
|
|
4
|
+
import { CompactObservationCollector } from './compact-contract.js';
|
|
5
|
+
import { projectCompactGraph } from './compact-projector.js';
|
|
6
6
|
import { traceWithObserver } from './trace-engine.js';
|
|
7
7
|
import type { TraceOptions, TraceResult, TraceStart } from '../types.js';
|
|
8
8
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
2
|
import { normalizeODataOperationInvocationPath } from '../linker/odata-path-normalizer.js';
|
|
3
3
|
import { resolveOperation, type OperationResolution } from '../linker/service-resolver.js';
|
|
4
|
-
import { boundedContextCandidates } from './
|
|
4
|
+
import { boundedContextCandidates } from './contextual-projection.js';
|
|
5
5
|
|
|
6
6
|
export interface ContextBinding {
|
|
7
7
|
bindingId?: number;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
|
-
import { scanPlaceholders } from '../utils/
|
|
2
|
+
import { scanPlaceholders } from '../utils/placeholders.js';
|
|
3
3
|
import type {
|
|
4
4
|
DynamicTargetCandidate,
|
|
5
5
|
DynamicTemplates,
|
|
6
6
|
DynamicVariableProvenance,
|
|
7
|
-
} from './
|
|
7
|
+
} from './dynamic-target-types.js';
|
|
8
8
|
|
|
9
9
|
interface RouteImplementationEvidence {
|
|
10
10
|
routeRepoId?: number;
|
|
@@ -2,8 +2,8 @@ import type { Db } from '../db/connection.js';
|
|
|
2
2
|
import {
|
|
3
3
|
projectBounded,
|
|
4
4
|
type BoundedProjection,
|
|
5
|
-
} from '../utils/
|
|
6
|
-
import type { DynamicVariableProvenance } from './
|
|
5
|
+
} from '../utils/bounded-projection.js';
|
|
6
|
+
import type { DynamicVariableProvenance } from './dynamic-target-types.js';
|
|
7
7
|
|
|
8
8
|
export interface DynamicReferenceRow {
|
|
9
9
|
bindingId?: number;
|
|
@@ -7,25 +7,25 @@ import {
|
|
|
7
7
|
import { normalizeODataOperationInvocationPath } from '../linker/odata-path-normalizer.js';
|
|
8
8
|
import type { OperationTarget } from '../linker/service-resolver.js';
|
|
9
9
|
import type { DynamicMode } from '../types.js';
|
|
10
|
-
import { dynamicCandidateTargets } from './
|
|
11
|
-
import { projectBounded } from '../utils/
|
|
12
|
-
import { uniqueIdentityDerivations } from './
|
|
10
|
+
import { dynamicCandidateTargets } from './dynamic-candidate-sources.js';
|
|
11
|
+
import { projectBounded } from '../utils/bounded-projection.js';
|
|
12
|
+
import { uniqueIdentityDerivations } from './dynamic-identity.js';
|
|
13
13
|
import {
|
|
14
14
|
dynamicReferenceProvenance,
|
|
15
15
|
dynamicRoutingContext,
|
|
16
16
|
type DynamicReferenceRow,
|
|
17
17
|
type DynamicRoutingContext,
|
|
18
|
-
} from './
|
|
18
|
+
} from './dynamic-references.js';
|
|
19
19
|
import type {
|
|
20
20
|
DynamicTargetAnalysis,
|
|
21
21
|
DynamicTargetCandidate,
|
|
22
22
|
DynamicTemplates,
|
|
23
23
|
DynamicVariableProvenance,
|
|
24
|
-
} from './
|
|
24
|
+
} from './dynamic-target-types.js';
|
|
25
25
|
export type {
|
|
26
26
|
DynamicTargetAnalysis,
|
|
27
27
|
DynamicTargetCandidate,
|
|
28
|
-
} from './
|
|
28
|
+
} from './dynamic-target-types.js';
|
|
29
29
|
type Templates = DynamicTemplates;
|
|
30
30
|
type VariableProvenance = DynamicVariableProvenance;
|
|
31
31
|
interface AnalysisInputs {
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
substituteVariables,
|
|
3
|
+
type RuntimeSubstitution,
|
|
4
|
+
} from '../linker/dynamic-edge-resolver.js';
|
|
5
|
+
import type { Db } from '../db/connection.js';
|
|
6
|
+
import {
|
|
7
|
+
eventSubscriberMissingVariables,
|
|
8
|
+
planEventSubscriberTransitions,
|
|
9
|
+
type EventSubscriberTransitionQuery,
|
|
10
|
+
type PlannedEventSubscriberTransition,
|
|
11
|
+
} from './event-subscriber-traversal.js';
|
|
12
|
+
import type {
|
|
13
|
+
TraversalScopeScheduler,
|
|
14
|
+
TraversalScopeState,
|
|
15
|
+
} from './traversal-scope.js';
|
|
16
|
+
import type { TraceGraphRow } from './evidence.js';
|
|
17
|
+
|
|
18
|
+
export interface EventRuntimeResolution {
|
|
19
|
+
row: TraceGraphRow;
|
|
20
|
+
evidence: Record<string, unknown>;
|
|
21
|
+
unresolvedReason?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface EventSubscriberRuntimePlan {
|
|
25
|
+
plans: PlannedEventSubscriberTransition[];
|
|
26
|
+
diagnostic?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
30
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
31
|
+
? value as Record<string, unknown> : undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function eventTemplate(evidence: Record<string, unknown>): string | undefined {
|
|
35
|
+
const resolution = record(evidence.eventTemplateResolution);
|
|
36
|
+
return typeof resolution?.original === 'string'
|
|
37
|
+
? resolution.original : undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function missingReason(substitution: RuntimeSubstitution): string {
|
|
41
|
+
return `Dynamic target requires runtime variable overrides: ${
|
|
42
|
+
substitution.missing.join(', ')}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function missingSubscriberDiagnostic(
|
|
46
|
+
missing: readonly string[],
|
|
47
|
+
): Record<string, unknown> | undefined {
|
|
48
|
+
if (missing.length === 0) return undefined;
|
|
49
|
+
return {
|
|
50
|
+
severity: 'warning',
|
|
51
|
+
code: 'trace_runtime_variables_missing',
|
|
52
|
+
message: `Runtime variables are required to resolve dynamic event subscribers: ${
|
|
53
|
+
missing.join(', ')}`,
|
|
54
|
+
missingVariables: missing,
|
|
55
|
+
suggestions: missing.map((key) => `--var ${key}=<value>`),
|
|
56
|
+
source: 'event_subscription',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function runtimeEventSubscriberPlans(
|
|
61
|
+
db: Db,
|
|
62
|
+
query: EventSubscriberTransitionQuery,
|
|
63
|
+
scheduler: TraversalScopeScheduler,
|
|
64
|
+
parent: TraversalScopeState,
|
|
65
|
+
depth: number,
|
|
66
|
+
maxDepth: number,
|
|
67
|
+
): EventSubscriberRuntimePlan {
|
|
68
|
+
const missing = eventSubscriberMissingVariables(db, query);
|
|
69
|
+
return {
|
|
70
|
+
plans: planEventSubscriberTransitions(
|
|
71
|
+
db, query, scheduler, parent, depth, maxDepth,
|
|
72
|
+
),
|
|
73
|
+
diagnostic: missingSubscriberDiagnostic(missing),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function eventEvidence(
|
|
78
|
+
evidence: Record<string, unknown>,
|
|
79
|
+
substitution: RuntimeSubstitution,
|
|
80
|
+
row: TraceGraphRow,
|
|
81
|
+
unresolvedReason?: string,
|
|
82
|
+
): Record<string, unknown> {
|
|
83
|
+
const effectiveResolution = {
|
|
84
|
+
status: unresolvedReason ? 'dynamic' : 'terminal',
|
|
85
|
+
targetKind: row.to_kind,
|
|
86
|
+
targetId: row.to_id,
|
|
87
|
+
confidence: row.confidence,
|
|
88
|
+
unresolvedReason,
|
|
89
|
+
edgeType: row.edge_type,
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
...evidence,
|
|
93
|
+
runtimeSubstitutions: { eventName: substitution },
|
|
94
|
+
suppliedRuntimeVariables: Object.fromEntries(
|
|
95
|
+
substitution.supplied.map((key) => [key, true]),
|
|
96
|
+
),
|
|
97
|
+
...(substitution.supplied.length > 0
|
|
98
|
+
? { runtimeVariablesApplied: true } : {}),
|
|
99
|
+
...(substitution.missing.length > 0
|
|
100
|
+
? { missingRuntimeVariables: substitution.missing } : {}),
|
|
101
|
+
effectiveResolution,
|
|
102
|
+
linker: {
|
|
103
|
+
status: effectiveResolution.status,
|
|
104
|
+
confidence: row.confidence,
|
|
105
|
+
reason: unresolvedReason,
|
|
106
|
+
edgeType: row.edge_type,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function runtimeEventRow(
|
|
112
|
+
row: TraceGraphRow,
|
|
113
|
+
evidence: Record<string, unknown>,
|
|
114
|
+
substitution: RuntimeSubstitution,
|
|
115
|
+
template: string,
|
|
116
|
+
): TraceGraphRow {
|
|
117
|
+
const missing = substitution.missing.length > 0;
|
|
118
|
+
const eventName = substitution.effective ?? template;
|
|
119
|
+
const callType = evidence.callType;
|
|
120
|
+
const edgeType = callType === 'async_emit'
|
|
121
|
+
? 'HANDLER_EMITS_EVENT' : 'EVENT_CONSUMED_BY_HANDLER';
|
|
122
|
+
return {
|
|
123
|
+
...row,
|
|
124
|
+
edge_type: missing ? 'DYNAMIC_EDGE_CANDIDATE' : edgeType,
|
|
125
|
+
status: missing ? 'dynamic' : 'terminal',
|
|
126
|
+
to_kind: missing ? 'event_candidate' : 'event',
|
|
127
|
+
to_id: missing ? `Event: ${eventName}` : eventName,
|
|
128
|
+
unresolved_reason: missing ? missingReason(substitution) : undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function runtimeEventResolution(
|
|
133
|
+
row: TraceGraphRow,
|
|
134
|
+
evidence: Record<string, unknown>,
|
|
135
|
+
variables: Record<string, string> | undefined,
|
|
136
|
+
): EventRuntimeResolution | undefined {
|
|
137
|
+
const template = eventTemplate(evidence);
|
|
138
|
+
if (!template || row.to_kind === 'event' && variables === undefined)
|
|
139
|
+
return undefined;
|
|
140
|
+
const substitution = substituteVariables(template, variables ?? {});
|
|
141
|
+
const effectiveRow = runtimeEventRow(
|
|
142
|
+
row, evidence, substitution, template,
|
|
143
|
+
);
|
|
144
|
+
return {
|
|
145
|
+
row: effectiveRow,
|
|
146
|
+
evidence: eventEvidence(
|
|
147
|
+
evidence, substitution, effectiveRow, effectiveRow.unresolved_reason,
|
|
148
|
+
),
|
|
149
|
+
unresolvedReason: effectiveRow.unresolved_reason,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
2
|
import {
|
|
3
|
+
matchRuntimeTemplate,
|
|
4
|
+
substituteVariables,
|
|
5
|
+
} from '../linker/dynamic-edge-resolver.js';
|
|
6
|
+
import {
|
|
7
|
+
compareBinary,
|
|
3
8
|
type TraversalScopeScheduler,
|
|
4
9
|
type TraversalScopeState,
|
|
5
|
-
} from './
|
|
10
|
+
} from './traversal-scope.js';
|
|
6
11
|
|
|
7
12
|
export type EventSubscriberTransitionStatus =
|
|
8
13
|
| 'resolved'
|
|
@@ -53,6 +58,8 @@ export interface EventSubscriberTransition {
|
|
|
53
58
|
candidateCount: number;
|
|
54
59
|
symbolCallUnresolvedReason?: string;
|
|
55
60
|
omittedSymbolCallUnresolvedReasonCharacterCount?: number;
|
|
61
|
+
matchStrategy?: string;
|
|
62
|
+
dispatchCertainty?: string;
|
|
56
63
|
handler?: EventSubscriberSymbolTarget;
|
|
57
64
|
}
|
|
58
65
|
|
|
@@ -60,6 +67,7 @@ export interface EventSubscriberTransitionQuery {
|
|
|
60
67
|
workspaceId: number;
|
|
61
68
|
graphGeneration: number;
|
|
62
69
|
eventName: string;
|
|
70
|
+
vars?: Record<string, string>;
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
export type EventBodyExpansion =
|
|
@@ -156,8 +164,8 @@ export function eventTransitionEvidence(
|
|
|
156
164
|
subscribeCallId: transition.subscribeCallId,
|
|
157
165
|
symbolCallId: transition.symbolCallId,
|
|
158
166
|
eventName: transition.eventName,
|
|
159
|
-
matchStrategy: 'workspace_exact_event_name',
|
|
160
|
-
dispatchCertainty: 'static_name_only',
|
|
167
|
+
matchStrategy: transition.matchStrategy ?? 'workspace_exact_event_name',
|
|
168
|
+
dispatchCertainty: transition.dispatchCertainty ?? 'static_name_only',
|
|
161
169
|
associationBasis: transition.associationBasis,
|
|
162
170
|
dispatchScope: transition.dispatchScope,
|
|
163
171
|
roleSiteMatchCount: transition.roleSiteMatchCount,
|
|
@@ -194,7 +202,8 @@ export function loadEventSubscriberTransitions(
|
|
|
194
202
|
query: EventSubscriberTransitionQuery,
|
|
195
203
|
): EventSubscriberTransition[] {
|
|
196
204
|
const rows = db.prepare(`SELECT ge.id graphEdgeId,ge.generation graphGeneration,
|
|
197
|
-
ge.
|
|
205
|
+
ge.from_kind fromKind,ge.from_id eventName,ge.status,
|
|
206
|
+
ge.to_kind targetKind,ge.to_id targetId,
|
|
198
207
|
ge.confidence,ge.unresolved_reason unresolvedReason,ge.evidence_json evidenceJson,
|
|
199
208
|
subscribe.id subscribeCallId,subscribe.repo_id subscriptionRepoId,
|
|
200
209
|
subscribe.source_file sourceFile,subscribe.source_line sourceLine,
|
|
@@ -218,19 +227,100 @@ export function loadEventSubscriberTransitions(
|
|
|
218
227
|
LEFT JOIN repositories handler_repo
|
|
219
228
|
ON handler_repo.id=handler.repo_id AND handler_repo.workspace_id=ge.workspace_id
|
|
220
229
|
WHERE ge.workspace_id=? AND ge.generation=?
|
|
221
|
-
AND ge.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
|
|
222
|
-
AND ge.
|
|
230
|
+
AND ge.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
|
|
231
|
+
AND ge.from_kind IN ('event','event_candidate')
|
|
223
232
|
ORDER BY COALESCE(subscription_repo.name,'') COLLATE BINARY,
|
|
224
233
|
COALESCE(subscription_repo.id,0),COALESCE(subscribe.source_file,'') COLLATE BINARY,
|
|
225
234
|
subscribe.call_site_start_offset,subscribe.call_site_end_offset,ge.id`).all(
|
|
226
|
-
query.workspaceId, query.graphGeneration,
|
|
235
|
+
query.workspaceId, query.graphGeneration,
|
|
227
236
|
);
|
|
228
|
-
return rows.flatMap((
|
|
229
|
-
const
|
|
237
|
+
return rows.flatMap((raw) => {
|
|
238
|
+
const row = eventRowForQuery(raw, query);
|
|
239
|
+
const transition = row ? transitionFromRow(row) : undefined;
|
|
230
240
|
return transition ? [transition] : [];
|
|
231
241
|
});
|
|
232
242
|
}
|
|
233
243
|
|
|
244
|
+
function eventRowForQuery(
|
|
245
|
+
row: Record<string, unknown>,
|
|
246
|
+
query: EventSubscriberTransitionQuery,
|
|
247
|
+
): Record<string, unknown> | undefined {
|
|
248
|
+
const variables = query.vars;
|
|
249
|
+
if (variables === undefined) return exactPersistedEventRow(row, query);
|
|
250
|
+
const evidence = parseEvidence(row.evidenceJson);
|
|
251
|
+
const resolution = isRecord(evidence.eventTemplateResolution)
|
|
252
|
+
? evidence.eventTemplateResolution : {};
|
|
253
|
+
if (typeof resolution.original !== 'string')
|
|
254
|
+
return exactPersistedEventRow(row, query);
|
|
255
|
+
return runtimeMatchedEventRow(
|
|
256
|
+
row, query, evidence, resolution.original, variables,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function exactPersistedEventRow(
|
|
261
|
+
row: Record<string, unknown>,
|
|
262
|
+
query: EventSubscriberTransitionQuery,
|
|
263
|
+
): Record<string, unknown> | undefined {
|
|
264
|
+
return row.fromKind === 'event' && row.eventName === query.eventName
|
|
265
|
+
? row : undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function runtimeMatchedEventRow(
|
|
269
|
+
row: Record<string, unknown>,
|
|
270
|
+
query: EventSubscriberTransitionQuery,
|
|
271
|
+
evidence: Record<string, unknown>,
|
|
272
|
+
template: string,
|
|
273
|
+
variables: Record<string, string>,
|
|
274
|
+
): Record<string, unknown> | undefined {
|
|
275
|
+
const substitution = substituteVariables(template, variables);
|
|
276
|
+
if (substitution.missing.length > 0
|
|
277
|
+
|| substitution.effective !== query.eventName) return undefined;
|
|
278
|
+
const resolvedAssociation = evidence.associationStatus === 'resolved';
|
|
279
|
+
return {
|
|
280
|
+
...row,
|
|
281
|
+
eventName: query.eventName,
|
|
282
|
+
status: typeof evidence.associationStatus === 'string'
|
|
283
|
+
? evidence.associationStatus : row.status,
|
|
284
|
+
unresolvedReason: resolvedAssociation ? null : row.unresolvedReason,
|
|
285
|
+
evidenceJson: JSON.stringify({
|
|
286
|
+
...evidence,
|
|
287
|
+
eventSubscriptionRuntimeSubstitution: substitution,
|
|
288
|
+
matchStrategy: 'workspace_exact_event_name_after_runtime_substitution',
|
|
289
|
+
dispatchCertainty: 'runtime_variables_exact',
|
|
290
|
+
}),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function eventSubscriberMissingVariables(
|
|
295
|
+
db: Db,
|
|
296
|
+
query: EventSubscriberTransitionQuery,
|
|
297
|
+
): string[] {
|
|
298
|
+
if (query.vars === undefined) return [];
|
|
299
|
+
const variables = query.vars;
|
|
300
|
+
const rows = db.prepare(`SELECT ge.from_id eventName,
|
|
301
|
+
ge.evidence_json evidenceJson FROM graph_edges ge
|
|
302
|
+
WHERE ge.workspace_id=? AND ge.generation=?
|
|
303
|
+
AND ge.edge_type='EVENT_SUBSCRIPTION_HANDLED_BY'
|
|
304
|
+
AND ge.from_kind='event_candidate'`).all(
|
|
305
|
+
query.workspaceId, query.graphGeneration,
|
|
306
|
+
);
|
|
307
|
+
const missing = rows.flatMap((row) => {
|
|
308
|
+
const evidence = parseEvidence(row.evidenceJson);
|
|
309
|
+
const resolution = isRecord(evidence.eventTemplateResolution)
|
|
310
|
+
? evidence.eventTemplateResolution : {};
|
|
311
|
+
const template = typeof resolution.original === 'string'
|
|
312
|
+
? resolution.original : undefined;
|
|
313
|
+
if (!template) return [];
|
|
314
|
+
const substitution = substituteVariables(template, variables);
|
|
315
|
+
if (substitution.missing.length === 0
|
|
316
|
+
|| matchRuntimeTemplate(
|
|
317
|
+
substitution.effective, query.eventName,
|
|
318
|
+
) === undefined) return [];
|
|
319
|
+
return substitution.missing;
|
|
320
|
+
});
|
|
321
|
+
return [...new Set(missing)].sort(compareBinary);
|
|
322
|
+
}
|
|
323
|
+
|
|
234
324
|
function transitionFromRow(
|
|
235
325
|
row: Record<string, unknown>,
|
|
236
326
|
): EventSubscriberTransition | undefined {
|
|
@@ -290,6 +380,8 @@ function associationEvidence(
|
|
|
290
380
|
omittedSymbolCallUnresolvedReasonCharacterCount: symbolCallUnresolvedReason
|
|
291
381
|
? nonNegativeCount(evidence.omittedSymbolCallUnresolvedReasonCharacterCount)
|
|
292
382
|
: undefined,
|
|
383
|
+
matchStrategy: stringValue(evidence.matchStrategy),
|
|
384
|
+
dispatchCertainty: stringValue(evidence.dispatchCertainty),
|
|
293
385
|
};
|
|
294
386
|
}
|
|
295
387
|
|
package/src/trace/evidence.ts
CHANGED
|
@@ -4,12 +4,12 @@ import { normalizeODataOperationInvocationPath } from '../linker/odata-path-norm
|
|
|
4
4
|
import { resolveOperation, type OperationTarget } from '../linker/service-resolver.js';
|
|
5
5
|
import type { DynamicMode } from '../types.js';
|
|
6
6
|
import { analyzeDynamicTargetCandidates, type DynamicTargetAnalysis, type DynamicTargetCandidate } from './dynamic-targets.js';
|
|
7
|
-
import { boundCandidateLikeEvidence } from '../utils/
|
|
7
|
+
import { boundCandidateLikeEvidence } from '../utils/bounded-projection.js';
|
|
8
8
|
import {
|
|
9
9
|
dynamicMissingReason,
|
|
10
10
|
isStructuralContextualBlocker,
|
|
11
11
|
type ContextualRuntimeState,
|
|
12
|
-
} from './
|
|
12
|
+
} from './contextual-runtime-state.js';
|
|
13
13
|
|
|
14
14
|
export interface TraceGraphRow extends Record<string, unknown> {
|
|
15
15
|
id: number;
|