@saptools/service-flow 0.1.67 → 0.1.69
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 +20 -0
- package/README.md +27 -9
- package/TECHNICAL-NOTE.md +36 -0
- package/dist/chunk-3N3B5KHV.js +19596 -0
- package/dist/chunk-3N3B5KHV.js.map +1 -0
- package/dist/cli.js +2645 -521
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +67 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/001-index-summary.ts +22 -0
- package/src/cli/003-doctor-package-resolution.ts +68 -0
- package/src/cli/doctor.ts +25 -14
- package/src/cli.ts +151 -87
- package/src/db/000-call-fact-repository.ts +499 -340
- 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 +699 -0
- package/src/db/004-package-target-invalidation.ts +183 -0
- package/src/db/005-schema-structure.ts +201 -0
- package/src/db/006-relative-symbol-resolution.ts +464 -0
- package/src/db/007-package-fact-semantics.ts +573 -0
- package/src/db/008-relative-fact-semantics.ts +210 -0
- package/src/db/009-binding-fact-semantics.ts +352 -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 +268 -0
- package/src/db/013-index-publication-failure.ts +91 -0
- package/src/db/014-binding-helper-provenance.ts +17 -0
- package/src/db/migrations.ts +16 -3
- package/src/db/repositories.ts +130 -6
- package/src/db/schema.ts +4 -2
- package/src/index.ts +12 -0
- package/src/indexer/cds-extension-resolver.ts +27 -3
- package/src/indexer/repository-indexer.ts +135 -13
- package/src/indexer/workspace-indexer.ts +237 -34
- package/src/linker/003-package-import-symbol-resolver.ts +363 -131
- package/src/linker/004-event-subscription-handler-linker.ts +34 -11
- package/src/linker/005-odata-path-structure.ts +371 -0
- package/src/linker/006-event-template-link.ts +72 -0
- package/src/linker/007-call-edge-insertion.ts +568 -0
- package/src/linker/cross-repo-linker.ts +4 -166
- package/src/linker/odata-path-normalizer.ts +273 -180
- package/src/linker/service-resolver.ts +197 -77
- package/src/parsers/000-direct-query-execution.ts +11 -0
- 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 +348 -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 +306 -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 +168 -0
- package/src/parsers/022-outbound-expression-analysis.ts +700 -0
- package/src/parsers/023-outbound-call-classifier.ts +692 -0
- package/src/parsers/operation-path-analysis.ts +6 -1
- package/src/parsers/outbound-call-parser.ts +162 -512
- 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 +513 -352
- package/src/trace/002-trace-diagnostics.ts +36 -1
- package/src/trace/007-implementation-start-diagnostic.ts +1 -0
- package/src/trace/011-event-subscriber-traversal.ts +100 -8
- package/src/trace/013-trace-root-scopes.ts +2 -3
- package/src/trace/014-compact-contract.ts +6 -0
- package/src/trace/015-trace-edge-recorder.ts +61 -4
- package/src/trace/016-compact-projector.ts +15 -17
- package/src/trace/019-trace-edge-semantics.ts +6 -10
- package/src/trace/020-compact-field-projection.ts +122 -38
- package/src/trace/021-compact-decision-normalization.ts +171 -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 +81 -0
- package/src/trace/025-trace-implementation-scope.ts +123 -0
- package/src/trace/026-trace-start-scope.ts +336 -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/030-event-runtime-resolution.ts +151 -0
- package/src/trace/031-local-call-expansion.ts +37 -0
- package/src/trace/implementation-hints.ts +9 -6
- package/src/trace/selectors.ts +1 -0
- package/src/trace/trace-engine.ts +122 -624
- package/src/types.ts +57 -0
- package/src/utils/001-placeholders.ts +188 -10
- package/src/version.ts +1 -1
- package/dist/chunk-ZQABU7MR.js +0 -12151
- package/dist/chunk-ZQABU7MR.js.map +0 -1
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
|
+
import type {
|
|
3
|
+
PackagePublicScope,
|
|
4
|
+
PackagePublicSurfaceFact,
|
|
5
|
+
PublicSurfaceTarget,
|
|
6
|
+
} from '../parsers/003-package-public-surface.js';
|
|
7
|
+
import type {
|
|
8
|
+
SymbolImportReference,
|
|
9
|
+
} from '../parsers/002-symbol-import-bindings.js';
|
|
10
|
+
import {
|
|
11
|
+
parsePackageImportReference,
|
|
12
|
+
parsePackagePublicSurfaceFact,
|
|
13
|
+
} from '../parsers/012-package-fact-contract.js';
|
|
2
14
|
|
|
3
15
|
export interface PackageSymbolLinkSummary {
|
|
4
16
|
resolved: number;
|
|
@@ -6,189 +18,409 @@ export interface PackageSymbolLinkSummary {
|
|
|
6
18
|
unresolved: number;
|
|
7
19
|
}
|
|
8
20
|
|
|
9
|
-
interface RepoExports {
|
|
10
|
-
publicName: Map<string, number[]>;
|
|
11
|
-
qualified: Map<string, number[]>;
|
|
12
|
-
fileById: Map<number, string>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
21
|
interface PackageCallRow {
|
|
16
22
|
id: number;
|
|
17
23
|
callerRepoId: number;
|
|
18
|
-
|
|
19
|
-
importSource: string;
|
|
24
|
+
binding: SymbolImportReference;
|
|
20
25
|
evidence: Record<string, unknown>;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
interface
|
|
28
|
+
interface PackageRepository {
|
|
29
|
+
id: number;
|
|
30
|
+
surface: PackagePublicSurfaceFact;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PackageCallResolution {
|
|
24
34
|
id: number | null;
|
|
25
35
|
status: 'resolved' | 'ambiguous' | 'unresolved';
|
|
26
36
|
reason: string | null;
|
|
27
|
-
strategy:
|
|
37
|
+
strategy: string;
|
|
28
38
|
candidateCount: number;
|
|
39
|
+
eligibleCandidateCount: number;
|
|
40
|
+
selectedCandidateCount: 0 | 1;
|
|
41
|
+
candidateSetComplete: boolean;
|
|
29
42
|
resolvedModulePath?: string;
|
|
43
|
+
targetRepoId?: number;
|
|
44
|
+
repositoryCandidateIds: number[];
|
|
45
|
+
publicSurface?: {
|
|
46
|
+
total: number;
|
|
47
|
+
shown: number;
|
|
48
|
+
omitted: number;
|
|
49
|
+
};
|
|
30
50
|
}
|
|
31
51
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
export interface PackageResolutionInput {
|
|
53
|
+
callId: number;
|
|
54
|
+
binding: SymbolImportReference;
|
|
55
|
+
}
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (existing) existing.push(id);
|
|
40
|
-
else map.set(key, [id]);
|
|
57
|
+
export interface PackageResolutionResult extends PackageCallResolution {
|
|
58
|
+
callId: number;
|
|
41
59
|
}
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
const REPOSITORY_REFERENCE_CAP = 5;
|
|
62
|
+
|
|
63
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
64
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
65
|
+
? value as Record<string, unknown> : undefined;
|
|
45
66
|
}
|
|
46
67
|
|
|
47
|
-
function
|
|
48
|
-
if (typeof value !== 'string'
|
|
68
|
+
function parseRecord(value: unknown): Record<string, unknown> | undefined {
|
|
69
|
+
if (typeof value !== 'string') return undefined;
|
|
49
70
|
try {
|
|
50
|
-
|
|
51
|
-
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
52
|
-
? parsed as Record<string, unknown>
|
|
53
|
-
: {};
|
|
71
|
+
return record(JSON.parse(value) as unknown);
|
|
54
72
|
} catch {
|
|
55
|
-
return
|
|
73
|
+
return undefined;
|
|
56
74
|
}
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
function importBinding(
|
|
78
|
+
evidence: Record<string, unknown>,
|
|
79
|
+
): SymbolImportReference | undefined {
|
|
80
|
+
return parsePackageImportReference(evidence.importBinding);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function packageCallRows(db: Db, workspaceId: number): PackageCallRow[] {
|
|
84
|
+
const rows = db.prepare(`SELECT sc.id,sc.repo_id callerRepoId,
|
|
85
|
+
sc.evidence_json evidenceJson FROM symbol_calls sc
|
|
86
|
+
JOIN repositories r ON r.id=sc.repo_id
|
|
87
|
+
WHERE r.workspace_id=?
|
|
88
|
+
AND json_extract(sc.evidence_json,'$.importBinding.moduleKind')='package'
|
|
89
|
+
ORDER BY sc.id`).all(workspaceId);
|
|
90
|
+
return rows.flatMap((row) => {
|
|
91
|
+
const evidence = parseRecord(row.evidenceJson);
|
|
92
|
+
const binding = evidence ? importBinding(evidence) : undefined;
|
|
93
|
+
return typeof row.id === 'number' && typeof row.callerRepoId === 'number'
|
|
94
|
+
&& evidence && binding
|
|
95
|
+
? [{ id: row.id, callerRepoId: row.callerRepoId, evidence, binding }]
|
|
96
|
+
: [];
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseSurface(
|
|
101
|
+
value: unknown,
|
|
102
|
+
packageName: string,
|
|
103
|
+
): PackagePublicSurfaceFact | undefined {
|
|
104
|
+
if (typeof value !== 'string') return undefined;
|
|
105
|
+
try {
|
|
106
|
+
return parsePackagePublicSurfaceFact(
|
|
107
|
+
JSON.parse(value) as unknown,
|
|
108
|
+
packageName,
|
|
109
|
+
);
|
|
110
|
+
} catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function repositoriesByPackage(
|
|
116
|
+
db: Db,
|
|
117
|
+
workspaceId: number,
|
|
118
|
+
): Map<string, PackageRepository[]> {
|
|
119
|
+
const rows = db.prepare(`SELECT id,package_name packageName,
|
|
120
|
+
package_public_surface_json surfaceJson FROM repositories
|
|
121
|
+
WHERE workspace_id=? AND package_name IS NOT NULL
|
|
122
|
+
ORDER BY package_name COLLATE BINARY,id`).all(workspaceId);
|
|
123
|
+
const result = new Map<string, PackageRepository[]>();
|
|
63
124
|
for (const row of rows) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
125
|
+
if (typeof row.id !== 'number' || typeof row.packageName !== 'string')
|
|
126
|
+
continue;
|
|
127
|
+
const surface = parseSurface(row.surfaceJson, row.packageName);
|
|
128
|
+
if (!surface) continue;
|
|
129
|
+
const repositories = result.get(row.packageName) ?? [];
|
|
130
|
+
repositories.push({ id: row.id, surface });
|
|
131
|
+
result.set(row.packageName, repositories);
|
|
67
132
|
}
|
|
68
133
|
return result;
|
|
69
134
|
}
|
|
70
135
|
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
136
|
+
function unresolved(
|
|
137
|
+
reason: string,
|
|
138
|
+
options: Partial<PackageCallResolution> = {},
|
|
139
|
+
): PackageCallResolution {
|
|
140
|
+
return {
|
|
141
|
+
id: null,
|
|
142
|
+
status: 'unresolved',
|
|
143
|
+
reason,
|
|
144
|
+
strategy: 'package_public_surface_unresolved',
|
|
145
|
+
candidateCount: 0,
|
|
146
|
+
eligibleCandidateCount: 0,
|
|
147
|
+
selectedCandidateCount: 0,
|
|
148
|
+
candidateSetComplete: true,
|
|
149
|
+
repositoryCandidateIds: [],
|
|
150
|
+
...options,
|
|
151
|
+
};
|
|
75
152
|
}
|
|
76
153
|
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
154
|
+
function repositoryScopeResolution(
|
|
155
|
+
call: PackageCallRow,
|
|
156
|
+
repositories: Map<string, PackageRepository[]>,
|
|
157
|
+
): PackageRepository | PackageCallResolution {
|
|
158
|
+
const matches = repositories.get(call.binding.requestedPackageName ?? '')
|
|
159
|
+
?? [];
|
|
160
|
+
if (matches.length === 1 && matches[0]) return matches[0];
|
|
161
|
+
const ids = matches.map((item) => item.id);
|
|
162
|
+
if (matches.length > 1) return unresolved(
|
|
163
|
+
'package_repository_scope_ambiguous',
|
|
164
|
+
{
|
|
165
|
+
candidateSetComplete: false,
|
|
166
|
+
candidateCount: matches.length,
|
|
167
|
+
repositoryCandidateIds: ids,
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
return unresolved('package_repository_not_indexed');
|
|
80
171
|
}
|
|
81
172
|
|
|
82
|
-
function
|
|
83
|
-
|
|
173
|
+
function publicScope(
|
|
174
|
+
surface: PackagePublicSurfaceFact,
|
|
175
|
+
binding: SymbolImportReference,
|
|
176
|
+
): PackagePublicScope | PackageCallResolution {
|
|
177
|
+
const surfaceCounts = {
|
|
178
|
+
total: surface.total,
|
|
179
|
+
shown: surface.shown,
|
|
180
|
+
omitted: surface.omitted,
|
|
181
|
+
};
|
|
182
|
+
if (surface.status !== 'complete') return unresolved(
|
|
183
|
+
surface.status === 'unsupported'
|
|
184
|
+
? 'package_public_surface_unsupported'
|
|
185
|
+
: 'public_surface_evidence_incomplete',
|
|
186
|
+
{ candidateSetComplete: false, publicSurface: surfaceCounts },
|
|
187
|
+
);
|
|
188
|
+
if (binding.typeOnly) return unresolved(
|
|
189
|
+
'package_binding_type_only',
|
|
190
|
+
{ publicSurface: surfaceCounts },
|
|
191
|
+
);
|
|
192
|
+
const matches = surface.scopes.filter((scope) =>
|
|
193
|
+
scope.entry === binding.requestedModuleSubpath
|
|
194
|
+
&& scope.publicName === binding.requestedPublicName);
|
|
195
|
+
if (matches.length === 1 && matches[0]) return matches[0];
|
|
196
|
+
if (matches.length > 1) return unresolved(
|
|
197
|
+
'package_public_scope_duplicate',
|
|
198
|
+
{
|
|
199
|
+
candidateSetComplete: false,
|
|
200
|
+
candidateCount: matches.reduce(
|
|
201
|
+
(sum, scope) => sum + scope.candidateCount, 0,
|
|
202
|
+
),
|
|
203
|
+
publicSurface: surfaceCounts,
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
return unresolved(
|
|
207
|
+
surface.omitted > 0
|
|
208
|
+
? 'public_surface_evidence_incomplete'
|
|
209
|
+
: 'package_public_name_not_exposed',
|
|
210
|
+
{
|
|
211
|
+
candidateSetComplete: surface.omitted === 0,
|
|
212
|
+
publicSurface: surfaceCounts,
|
|
213
|
+
},
|
|
214
|
+
);
|
|
84
215
|
}
|
|
85
216
|
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
217
|
+
function symbolEvidenceMatches(
|
|
218
|
+
evidenceJson: unknown,
|
|
219
|
+
target: PublicSurfaceTarget,
|
|
220
|
+
scope: PackagePublicScope,
|
|
221
|
+
): boolean {
|
|
222
|
+
const evidence = parseRecord(evidenceJson);
|
|
223
|
+
if (!evidence) return false;
|
|
224
|
+
const surface = record(evidence.packagePublicSurface);
|
|
225
|
+
if (!surface) return false;
|
|
226
|
+
const body = record(surface.bodyEligibility);
|
|
227
|
+
const exposures = surface.exposures;
|
|
228
|
+
if (!body || !Array.isArray(exposures)) return false;
|
|
229
|
+
const exactProof = [
|
|
230
|
+
body.eligible === true,
|
|
231
|
+
body.reason === target.bodyEligibility.reason,
|
|
232
|
+
surface.recordCap === 256,
|
|
233
|
+
surface.exposureTotal === exposures.length,
|
|
234
|
+
surface.shownExposureCount === exposures.length,
|
|
235
|
+
surface.omittedExposureCount === 0,
|
|
236
|
+
];
|
|
237
|
+
if (exactProof.includes(false)) return false;
|
|
238
|
+
return exposures.some((item) => {
|
|
239
|
+
const exposure = record(item);
|
|
240
|
+
return exposure?.entry === scope.entry
|
|
241
|
+
&& exposure.modulePath === scope.modulePath
|
|
242
|
+
&& exposure.publicName === scope.publicName;
|
|
243
|
+
});
|
|
94
244
|
}
|
|
95
245
|
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
246
|
+
function targetSymbolIds(
|
|
247
|
+
db: Db,
|
|
248
|
+
repositoryId: number,
|
|
249
|
+
scope: PackagePublicScope,
|
|
250
|
+
): number[] {
|
|
251
|
+
return scope.targets.flatMap((target) => {
|
|
252
|
+
if (!target.bodyEligibility.eligible) return [];
|
|
253
|
+
const rows = db.prepare(`SELECT id,evidence_json evidenceJson FROM symbols
|
|
254
|
+
WHERE repo_id=? AND source_file=? AND kind=? AND qualified_name=?
|
|
255
|
+
AND start_offset=? AND end_offset=?
|
|
256
|
+
ORDER BY id`).all(
|
|
257
|
+
repositoryId,
|
|
258
|
+
target.sourceFile,
|
|
259
|
+
target.kind,
|
|
260
|
+
target.qualifiedName,
|
|
261
|
+
target.startOffset,
|
|
262
|
+
target.endOffset,
|
|
263
|
+
);
|
|
264
|
+
return rows.flatMap((row) =>
|
|
265
|
+
typeof row.id === 'number'
|
|
266
|
+
&& symbolEvidenceMatches(row.evidenceJson, target, scope)
|
|
267
|
+
? [row.id] : []);
|
|
268
|
+
});
|
|
106
269
|
}
|
|
107
270
|
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
271
|
+
function scopeResolution(
|
|
272
|
+
db: Db,
|
|
273
|
+
repository: PackageRepository,
|
|
274
|
+
scope: PackagePublicScope,
|
|
275
|
+
): PackageCallResolution {
|
|
276
|
+
const ids = [...new Set(targetSymbolIds(db, repository.id, scope))];
|
|
277
|
+
if (ids.length !== scope.eligibleCandidateCount) return unresolved(
|
|
278
|
+
'public_surface_evidence_incomplete',
|
|
279
|
+
{
|
|
280
|
+
candidateCount: scope.candidateCount,
|
|
281
|
+
eligibleCandidateCount: ids.length,
|
|
282
|
+
candidateSetComplete: false,
|
|
283
|
+
resolvedModulePath: scope.modulePath,
|
|
284
|
+
targetRepoId: repository.id,
|
|
285
|
+
repositoryCandidateIds: [repository.id],
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
const base = {
|
|
289
|
+
candidateCount: scope.candidateCount,
|
|
290
|
+
eligibleCandidateCount: scope.eligibleCandidateCount,
|
|
291
|
+
selectedCandidateCount: 0 as const,
|
|
292
|
+
candidateSetComplete: scope.candidateSetComplete,
|
|
293
|
+
resolvedModulePath: scope.modulePath,
|
|
294
|
+
targetRepoId: repository.id,
|
|
295
|
+
repositoryCandidateIds: [repository.id],
|
|
296
|
+
};
|
|
297
|
+
if (ids.length === 1 && ids[0] !== undefined) return {
|
|
298
|
+
...base,
|
|
299
|
+
id: ids[0],
|
|
300
|
+
status: 'resolved',
|
|
301
|
+
reason: null,
|
|
302
|
+
strategy: 'package_public_surface_exact',
|
|
303
|
+
selectedCandidateCount: 1,
|
|
304
|
+
};
|
|
305
|
+
if (ids.length > 1) return {
|
|
306
|
+
...base,
|
|
307
|
+
id: null,
|
|
308
|
+
status: 'ambiguous',
|
|
309
|
+
reason: 'package_public_target_ambiguous',
|
|
310
|
+
strategy: 'package_public_surface_ambiguous',
|
|
311
|
+
};
|
|
312
|
+
return unresolved(
|
|
313
|
+
scope.candidateCount > 0 && scope.eligibleCandidateCount === 0
|
|
314
|
+
? 'public_symbol_has_no_executable_body'
|
|
315
|
+
: 'package_public_name_not_exposed',
|
|
316
|
+
base,
|
|
317
|
+
);
|
|
144
318
|
}
|
|
145
319
|
|
|
146
320
|
function resolvePackageCall(
|
|
321
|
+
db: Db,
|
|
147
322
|
call: PackageCallRow,
|
|
148
|
-
|
|
149
|
-
exports: Map<number, RepoExports>,
|
|
323
|
+
repositories: Map<string, PackageRepository[]>,
|
|
150
324
|
): PackageCallResolution {
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
325
|
+
const repository = repositoryScopeResolution(call, repositories);
|
|
326
|
+
if ('status' in repository) return repository;
|
|
327
|
+
const scope = publicScope(repository.surface, call.binding);
|
|
328
|
+
if ('status' in scope) return {
|
|
329
|
+
...scope,
|
|
330
|
+
targetRepoId: repository.id,
|
|
331
|
+
repositoryCandidateIds: [repository.id],
|
|
332
|
+
};
|
|
333
|
+
return scopeResolution(db, repository, scope);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function expectedPackageImportResolutions(
|
|
337
|
+
db: Db,
|
|
338
|
+
workspaceId: number,
|
|
339
|
+
inputs: readonly PackageResolutionInput[],
|
|
340
|
+
): PackageResolutionResult[] {
|
|
341
|
+
const repositories = repositoriesByPackage(db, workspaceId);
|
|
342
|
+
return inputs.map((input) => ({
|
|
343
|
+
callId: input.callId,
|
|
344
|
+
...resolvePackageCall(
|
|
345
|
+
db,
|
|
346
|
+
{
|
|
347
|
+
id: input.callId,
|
|
348
|
+
callerRepoId: 0,
|
|
349
|
+
binding: input.binding,
|
|
350
|
+
evidence: {},
|
|
351
|
+
},
|
|
352
|
+
repositories,
|
|
353
|
+
),
|
|
354
|
+
}));
|
|
166
355
|
}
|
|
167
356
|
|
|
168
|
-
|
|
169
|
-
|
|
357
|
+
const resolverEvidenceKeys = new Set([
|
|
358
|
+
'candidateStrategy', 'candidateCount', 'eligibleCandidateCount',
|
|
359
|
+
'selectedCandidateCount', 'candidateSetComplete', 'resolvedModulePath',
|
|
360
|
+
'resolvedTargetRepositoryId', 'unresolvedReason',
|
|
361
|
+
'targetRepositoryCandidateCount', 'targetRepositoryCandidates',
|
|
362
|
+
'shownTargetRepositoryCandidateCount',
|
|
363
|
+
'omittedTargetRepositoryCandidateCount', 'publicSurface',
|
|
364
|
+
]);
|
|
365
|
+
|
|
366
|
+
function parserEvidence(
|
|
367
|
+
evidence: Record<string, unknown>,
|
|
368
|
+
): Record<string, unknown> {
|
|
369
|
+
return Object.fromEntries(Object.entries(evidence).filter(
|
|
370
|
+
([key]) => !resolverEvidenceKeys.has(key),
|
|
371
|
+
));
|
|
170
372
|
}
|
|
171
373
|
|
|
172
|
-
function resolutionEvidence(
|
|
173
|
-
|
|
174
|
-
|
|
374
|
+
function resolutionEvidence(
|
|
375
|
+
call: PackageCallRow,
|
|
376
|
+
resolution: PackageCallResolution,
|
|
377
|
+
): string {
|
|
378
|
+
const ids = resolution.repositoryCandidateIds
|
|
379
|
+
.slice(0, REPOSITORY_REFERENCE_CAP);
|
|
380
|
+
return JSON.stringify({
|
|
381
|
+
...parserEvidence(call.evidence),
|
|
175
382
|
candidateStrategy: resolution.strategy,
|
|
176
383
|
candidateCount: resolution.candidateCount,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
384
|
+
eligibleCandidateCount: resolution.eligibleCandidateCount,
|
|
385
|
+
selectedCandidateCount: resolution.selectedCandidateCount,
|
|
386
|
+
candidateSetComplete: resolution.candidateSetComplete,
|
|
387
|
+
unresolvedReason: resolution.reason,
|
|
388
|
+
...(resolution.resolvedModulePath
|
|
389
|
+
? { resolvedModulePath: resolution.resolvedModulePath } : {}),
|
|
390
|
+
...(resolution.targetRepoId
|
|
391
|
+
? { resolvedTargetRepositoryId: resolution.targetRepoId } : {}),
|
|
392
|
+
targetRepositoryCandidateCount:
|
|
393
|
+
resolution.repositoryCandidateIds.length,
|
|
394
|
+
targetRepositoryCandidates: ids,
|
|
395
|
+
shownTargetRepositoryCandidateCount: ids.length,
|
|
396
|
+
omittedTargetRepositoryCandidateCount:
|
|
397
|
+
resolution.repositoryCandidateIds.length - ids.length,
|
|
398
|
+
...(resolution.publicSurface
|
|
399
|
+
? { publicSurface: resolution.publicSurface } : {}),
|
|
400
|
+
});
|
|
181
401
|
}
|
|
182
402
|
|
|
183
|
-
export function linkPackageImportSymbolCalls(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
403
|
+
export function linkPackageImportSymbolCalls(
|
|
404
|
+
db: Db,
|
|
405
|
+
workspaceId: number,
|
|
406
|
+
): PackageSymbolLinkSummary {
|
|
407
|
+
const repositories = repositoriesByPackage(db, workspaceId);
|
|
408
|
+
const update = db.prepare(`UPDATE symbol_calls SET callee_symbol_id=?,
|
|
409
|
+
status=?,unresolved_reason=?,evidence_json=? WHERE id=?`);
|
|
410
|
+
const summary: PackageSymbolLinkSummary = {
|
|
411
|
+
resolved: 0,
|
|
412
|
+
ambiguous: 0,
|
|
413
|
+
unresolved: 0,
|
|
414
|
+
};
|
|
189
415
|
for (const call of packageCallRows(db, workspaceId)) {
|
|
190
|
-
const resolution = resolvePackageCall(
|
|
191
|
-
update.run(
|
|
416
|
+
const resolution = resolvePackageCall(db, call, repositories);
|
|
417
|
+
update.run(
|
|
418
|
+
resolution.id,
|
|
419
|
+
resolution.status,
|
|
420
|
+
resolution.reason,
|
|
421
|
+
resolutionEvidence(call, resolution),
|
|
422
|
+
call.id,
|
|
423
|
+
);
|
|
192
424
|
summary[resolution.status] += 1;
|
|
193
425
|
}
|
|
194
426
|
return summary;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { Db } from '../db/connection.js';
|
|
2
|
+
import {
|
|
3
|
+
linkEventTemplate,
|
|
4
|
+
type LinkedEventTemplate,
|
|
5
|
+
} from './006-event-template-link.js';
|
|
2
6
|
|
|
3
7
|
export interface SubscriptionHandlerLinkSummary {
|
|
4
8
|
edgeCount: number;
|
|
@@ -20,6 +24,7 @@ interface SubscriptionRow {
|
|
|
20
24
|
startOffset?: number | null;
|
|
21
25
|
endOffset?: number | null;
|
|
22
26
|
confidence: number;
|
|
27
|
+
unresolvedReason?: string | null;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
interface HandlerCallRow {
|
|
@@ -58,9 +63,11 @@ function subscriptionRows(db: Db, workspaceId: number): SubscriptionRow[] {
|
|
|
58
63
|
c.source_symbol_id sourceSymbolId,c.event_name_expr eventName,
|
|
59
64
|
c.source_file sourceFile,c.source_line sourceLine,
|
|
60
65
|
c.call_site_start_offset startOffset,c.call_site_end_offset endOffset,
|
|
61
|
-
c.confidence
|
|
66
|
+
c.confidence,c.unresolved_reason unresolvedReason
|
|
62
67
|
FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id
|
|
63
68
|
WHERE r.workspace_id=? AND c.call_type='async_subscribe'
|
|
69
|
+
AND json_extract(c.evidence_json,'$.handlerReferenceStatus')
|
|
70
|
+
='role_required'
|
|
64
71
|
ORDER BY r.name COLLATE BINARY,r.id,c.source_file COLLATE BINARY,
|
|
65
72
|
c.call_site_start_offset,c.call_site_end_offset,c.id`).all(
|
|
66
73
|
workspaceId,
|
|
@@ -200,11 +207,16 @@ function missingAssociation(
|
|
|
200
207
|
function evidenceFor(
|
|
201
208
|
subscription: SubscriptionRow,
|
|
202
209
|
association: HandlerAssociation,
|
|
210
|
+
event: LinkedEventTemplate,
|
|
203
211
|
): Record<string, unknown> {
|
|
204
212
|
const call: Partial<HandlerCallRow> = association.call ?? {};
|
|
205
213
|
const symbolCallReason = boundedSymbolCallReason(call.unresolvedReason);
|
|
206
214
|
return {
|
|
207
215
|
eventName: subscription.eventName,
|
|
216
|
+
...(event.substitution.placeholders.length > 0 ? {
|
|
217
|
+
effectiveEventName: event.targetId,
|
|
218
|
+
eventTemplateResolution: event.substitution,
|
|
219
|
+
} : {}),
|
|
208
220
|
associationBasis: 'exact_subscription_call_span',
|
|
209
221
|
dispatchScope: 'workspace_event_name_only',
|
|
210
222
|
subscribeCallId: subscription.id,
|
|
@@ -251,22 +263,27 @@ function insertAssociationEdge(
|
|
|
251
263
|
generation: number,
|
|
252
264
|
subscription: SubscriptionRow,
|
|
253
265
|
association: HandlerAssociation,
|
|
266
|
+
event: LinkedEventTemplate,
|
|
254
267
|
): void {
|
|
268
|
+
const status = event.isDynamic ? 'unresolved' : association.status;
|
|
269
|
+
const reason = event.isDynamic
|
|
270
|
+
? 'event_template_variables_missing'
|
|
271
|
+
: association.reasonCode ?? association.call?.unresolvedReason ?? null;
|
|
255
272
|
db.prepare(`INSERT INTO graph_edges(
|
|
256
273
|
workspace_id,edge_type,status,from_kind,from_id,to_kind,to_id,
|
|
257
274
|
confidence,evidence_json,is_dynamic,unresolved_reason,generation
|
|
258
275
|
) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)`).run(
|
|
259
276
|
workspaceId,
|
|
260
277
|
'EVENT_SUBSCRIPTION_HANDLED_BY',
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
278
|
+
status,
|
|
279
|
+
event.targetKind,
|
|
280
|
+
event.targetId,
|
|
264
281
|
association.toKind,
|
|
265
282
|
association.toId,
|
|
266
283
|
association.call?.confidence ?? subscription.confidence,
|
|
267
|
-
JSON.stringify(evidenceFor(subscription, association)),
|
|
268
|
-
0,
|
|
269
|
-
|
|
284
|
+
JSON.stringify(evidenceFor(subscription, association, event)),
|
|
285
|
+
event.isDynamic ? 1 : 0,
|
|
286
|
+
reason,
|
|
270
287
|
generation,
|
|
271
288
|
);
|
|
272
289
|
}
|
|
@@ -275,6 +292,7 @@ export function linkEventSubscriptionHandlers(
|
|
|
275
292
|
db: Db,
|
|
276
293
|
workspaceId: number,
|
|
277
294
|
generation: number,
|
|
295
|
+
variables: Record<string, string> = {},
|
|
278
296
|
): SubscriptionHandlerLinkSummary {
|
|
279
297
|
const summary: SubscriptionHandlerLinkSummary = {
|
|
280
298
|
edgeCount: 0,
|
|
@@ -284,16 +302,21 @@ export function linkEventSubscriptionHandlers(
|
|
|
284
302
|
missingAssociationCount: 0,
|
|
285
303
|
};
|
|
286
304
|
for (const subscription of subscriptionRows(db, workspaceId)) {
|
|
305
|
+
const event = linkEventTemplate(
|
|
306
|
+
subscription.eventName, variables,
|
|
307
|
+
subscription.unresolvedReason ?? undefined,
|
|
308
|
+
);
|
|
287
309
|
const association = associationFor(
|
|
288
310
|
subscription, roleSiteRows(db, subscription),
|
|
289
311
|
);
|
|
290
312
|
insertAssociationEdge(
|
|
291
|
-
db, workspaceId, generation, subscription, association,
|
|
313
|
+
db, workspaceId, generation, subscription, association, event,
|
|
292
314
|
);
|
|
315
|
+
const status = event.isDynamic ? 'unresolved' : association.status;
|
|
293
316
|
summary.edgeCount += 1;
|
|
294
|
-
summary.resolvedCount +=
|
|
295
|
-
summary.ambiguousCount +=
|
|
296
|
-
summary.unresolvedCount +=
|
|
317
|
+
summary.resolvedCount += status === 'resolved' ? 1 : 0;
|
|
318
|
+
summary.ambiguousCount += status === 'ambiguous' ? 1 : 0;
|
|
319
|
+
summary.unresolvedCount += status === 'unresolved' ? 1 : 0;
|
|
297
320
|
summary.missingAssociationCount += association.missing ? 1 : 0;
|
|
298
321
|
}
|
|
299
322
|
return summary;
|