@shapeshift-labs/frontier-lang-compiler 0.2.104 → 0.2.106
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/README.md +13 -1
- package/dist/declarations/js-ts-safe-member-merge.d.ts +29 -1
- package/dist/declarations/js-ts-safe-merge.d.ts +47 -0
- package/dist/declarations/native-project.d.ts +110 -0
- package/dist/internal/index-impl/createNativeProjectImportResult.js +177 -3
- package/dist/internal/index-impl/replaySemanticEditProjection.js +1 -1
- package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +200 -15
- package/dist/js-ts-safe-member-merge.js +69 -6
- package/dist/js-ts-safe-merge-composed.js +175 -0
- package/dist/js-ts-safe-merge-semantic-artifact-ledger.js +197 -0
- package/dist/js-ts-safe-merge-semantic-artifacts.js +266 -0
- package/dist/js-ts-safe-merge.js +6 -1
- package/dist/js-ts-semantic-merge-member-source.js +22 -4
- package/dist/js-ts-semantic-merge-parse.js +1 -0
- package/dist/js-ts-semantic-merge.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -168,13 +168,25 @@ The JS/TS semantic merge smoke corpus lives at
|
|
|
168
168
|
`test/smoke/js-ts-semantic-merge-oracles.mjs`. The fixtures are deliberately
|
|
169
169
|
small and dependency-free. They cover accepted projection/replay cases, exact
|
|
170
170
|
source preservation, generated/source-map boundaries, safe import/declaration
|
|
171
|
-
merges, safe unordered member merges,
|
|
171
|
+
merges, safe unordered member merges, composed top-level/member safe merges,
|
|
172
|
+
and rejected unsafe cases such as stale
|
|
172
173
|
ledger spans, import specifier reordering, computed keys, duplicate exported
|
|
173
174
|
names, duplicate object members, decorators, overload anchors, and same-anchor
|
|
174
175
|
edit conflicts. Fixture failures include the fixture id and the actual
|
|
175
176
|
reason-code or gate values so distributed swarm evidence can point at a stable
|
|
176
177
|
case instead of an agent transcript.
|
|
177
178
|
|
|
179
|
+
Successful `safeMergeJsTsImportsAndDeclarations` and `safeMergeJsTsSource`
|
|
180
|
+
results also include `semanticArtifacts`. These artifacts convert the
|
|
181
|
+
JS/TS ledger-approved head-to-merged source edits into a semantic edit script,
|
|
182
|
+
projection, replay, and already-applied replay. This is intentionally different
|
|
183
|
+
from asking the generic three-way edit classifier to bless every JS/TS case:
|
|
184
|
+
simultaneous import specifier additions are safe only because the JS/TS ledger
|
|
185
|
+
gates proved independent additions, stable anchors, and source replay. The
|
|
186
|
+
artifacts keep `autoMergeClaim: false` and `semanticEquivalenceClaim: false`,
|
|
187
|
+
but give coordinators machine-readable proof that the projected source matches
|
|
188
|
+
the merge output and that applying the same projection again is a no-op.
|
|
189
|
+
|
|
178
190
|
High-risk native features also have explicit evidence policies. These policies are advisory in this package: they tell a swarm or admission queue what evidence is missing without silently changing the existing readiness classification.
|
|
179
191
|
|
|
180
192
|
```js
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type JsTsSafeMemberMergeRegionKind = 'interface' | 'type' | 'object';
|
|
1
|
+
export type JsTsSafeMemberMergeRegionKind = 'interface' | 'type' | 'class' | 'object';
|
|
2
2
|
export type JsTsSafeMemberMergeOrder = 'non-semantic' | string;
|
|
3
3
|
export type JsTsSafeMemberMergeStatus = 'merged' | 'rejected';
|
|
4
4
|
|
|
@@ -26,6 +26,30 @@ export interface JsTsSafeMemberMergeInput {
|
|
|
26
26
|
readonly policy?: JsTsSafeMemberMergePolicy | readonly JsTsSafeMemberMergePolicyRegion[];
|
|
27
27
|
readonly mergePolicy?: JsTsSafeMemberMergePolicy | readonly JsTsSafeMemberMergePolicyRegion[];
|
|
28
28
|
readonly unorderedRegions?: readonly JsTsSafeMemberMergePolicyRegion[];
|
|
29
|
+
readonly allowNonPolicySourceChanges?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface JsTsSafeMemberMergeConflict {
|
|
33
|
+
readonly code: string;
|
|
34
|
+
readonly gateId: string;
|
|
35
|
+
readonly message: string;
|
|
36
|
+
readonly side?: 'base' | 'worker' | 'head' | string;
|
|
37
|
+
readonly sourcePath?: string;
|
|
38
|
+
readonly details?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface JsTsSafeMemberMergeGate {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly status: 'passed' | 'blocked' | 'skipped' | string;
|
|
44
|
+
readonly reasonCodes: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface JsTsSafeMemberMergeAdmission {
|
|
48
|
+
readonly status: 'auto-merge-candidate' | 'blocked' | string;
|
|
49
|
+
readonly action: 'apply' | 'human-review' | string;
|
|
50
|
+
readonly reviewRequired: boolean;
|
|
51
|
+
readonly autoApplyCandidate: boolean;
|
|
52
|
+
readonly reasonCodes: readonly string[];
|
|
29
53
|
}
|
|
30
54
|
|
|
31
55
|
export interface JsTsSafeMemberMergedRegion {
|
|
@@ -42,12 +66,16 @@ export interface JsTsSafeMemberMergeResult {
|
|
|
42
66
|
readonly status: JsTsSafeMemberMergeStatus;
|
|
43
67
|
readonly sourceText?: string;
|
|
44
68
|
readonly reasonCodes: readonly string[];
|
|
69
|
+
readonly conflicts: readonly JsTsSafeMemberMergeConflict[];
|
|
70
|
+
readonly gates: readonly JsTsSafeMemberMergeGate[];
|
|
71
|
+
readonly admission: JsTsSafeMemberMergeAdmission;
|
|
45
72
|
readonly mergedRegions: readonly JsTsSafeMemberMergedRegion[];
|
|
46
73
|
readonly summary: {
|
|
47
74
|
readonly regions: number;
|
|
48
75
|
readonly workerAdditions: number;
|
|
49
76
|
readonly headAdditions: number;
|
|
50
77
|
readonly appliedAdditions: number;
|
|
78
|
+
readonly conflicts: number;
|
|
51
79
|
};
|
|
52
80
|
readonly metadata: {
|
|
53
81
|
readonly explicitPolicy: boolean;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { FrontierSourceLanguage } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import type { SemanticEditProjection, SemanticEditReplay, SemanticEditScript } from './semantic-edit-script.js';
|
|
3
|
+
import type {
|
|
4
|
+
JsTsSafeMemberMergePolicy,
|
|
5
|
+
JsTsSafeMemberMergePolicyRegion
|
|
6
|
+
} from './js-ts-safe-member-merge.js';
|
|
2
7
|
|
|
3
8
|
export type JsTsSafeMergeStatus = 'merged' | 'blocked';
|
|
4
9
|
export type JsTsSafeMergeGateStatus = 'passed' | 'blocked' | 'skipped';
|
|
@@ -65,6 +70,9 @@ export interface JsTsSafeMergeInput {
|
|
|
65
70
|
readonly baseSourceLedger?: unknown;
|
|
66
71
|
readonly workerSourceLedger?: unknown;
|
|
67
72
|
readonly headSourceLedger?: unknown;
|
|
73
|
+
readonly policy?: JsTsSafeMemberMergePolicy | readonly JsTsSafeMemberMergePolicyRegion[];
|
|
74
|
+
readonly mergePolicy?: JsTsSafeMemberMergePolicy | readonly JsTsSafeMemberMergePolicyRegion[];
|
|
75
|
+
readonly unorderedRegions?: readonly JsTsSafeMemberMergePolicyRegion[];
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
export interface JsTsSafeMergeConflict {
|
|
@@ -98,6 +106,43 @@ export interface JsTsSafeMergeSummary {
|
|
|
98
106
|
readonly changedExistingDeclarations: number;
|
|
99
107
|
readonly conflicts: number;
|
|
100
108
|
readonly gatesPassed: number;
|
|
109
|
+
readonly memberRegions?: number;
|
|
110
|
+
readonly memberAdditions?: number;
|
|
111
|
+
readonly composedPhases?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface JsTsSafeMergeSemanticArtifacts {
|
|
115
|
+
readonly kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts';
|
|
116
|
+
readonly version: 1;
|
|
117
|
+
readonly schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1';
|
|
118
|
+
readonly id: string;
|
|
119
|
+
readonly hash: string;
|
|
120
|
+
readonly sourcePath?: string;
|
|
121
|
+
readonly language?: FrontierSourceLanguage | string;
|
|
122
|
+
readonly status: 'verified' | 'blocked' | string;
|
|
123
|
+
readonly script?: SemanticEditScript;
|
|
124
|
+
readonly projection?: SemanticEditProjection;
|
|
125
|
+
readonly replay?: SemanticEditReplay;
|
|
126
|
+
readonly alreadyAppliedReplay?: SemanticEditReplay;
|
|
127
|
+
readonly admission: {
|
|
128
|
+
readonly status: 'auto-merge-candidate' | 'blocked' | string;
|
|
129
|
+
readonly action: 'apply' | 'human-review' | string;
|
|
130
|
+
readonly reviewRequired: boolean;
|
|
131
|
+
readonly autoApplyCandidate: boolean;
|
|
132
|
+
readonly autoMergeClaim: false;
|
|
133
|
+
readonly semanticEquivalenceClaim: false;
|
|
134
|
+
readonly reasonCodes: readonly string[];
|
|
135
|
+
};
|
|
136
|
+
readonly summary: {
|
|
137
|
+
readonly operations: number;
|
|
138
|
+
readonly edits: number;
|
|
139
|
+
readonly replayStatus?: string;
|
|
140
|
+
readonly alreadyAppliedReplayStatus?: string;
|
|
141
|
+
readonly projectedSourceMatchesMerged: boolean;
|
|
142
|
+
readonly replayOutputMatchesMerged: boolean;
|
|
143
|
+
};
|
|
144
|
+
readonly evidence?: readonly unknown[];
|
|
145
|
+
readonly metadata?: Record<string, unknown>;
|
|
101
146
|
}
|
|
102
147
|
|
|
103
148
|
export interface JsTsSafeMergeResult {
|
|
@@ -114,7 +159,9 @@ export interface JsTsSafeMergeResult {
|
|
|
114
159
|
readonly gates: readonly JsTsSafeMergeGate[];
|
|
115
160
|
readonly admission: JsTsSafeMergeAdmission;
|
|
116
161
|
readonly summary: JsTsSafeMergeSummary;
|
|
162
|
+
readonly semanticArtifacts?: JsTsSafeMergeSemanticArtifacts;
|
|
117
163
|
readonly metadata?: Record<string, unknown>;
|
|
118
164
|
}
|
|
119
165
|
|
|
120
166
|
export declare function safeMergeJsTsImportsAndDeclarations(input: JsTsSafeMergeInput): JsTsSafeMergeResult;
|
|
167
|
+
export declare function safeMergeJsTsSource(input: JsTsSafeMergeInput): JsTsSafeMergeResult;
|
|
@@ -77,6 +77,114 @@ export interface ImportNativeProjectOptions {
|
|
|
77
77
|
readonly sources: readonly NativeProjectSourceInput[];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
export type NativeProjectSymbolGraphRemainingField =
|
|
81
|
+
| 'moduleEdges[].resolvedModulePath'
|
|
82
|
+
| 'moduleEdges[].targetDocumentId'
|
|
83
|
+
| 'moduleEdges[].resolvedTargetSymbolId'
|
|
84
|
+
| 'moduleEdges[].resolutionKind'
|
|
85
|
+
| 'moduleEdges[].packageName'
|
|
86
|
+
| 'moduleEdges[].packageExportCondition'
|
|
87
|
+
| 'reExportIdentities[].originSymbolId'
|
|
88
|
+
| 'reExportIdentities[].exportedSymbolId'
|
|
89
|
+
| 'reExportIdentities[].localSymbolId'
|
|
90
|
+
| 'publicContractRegions[].apiSurfaceKind'
|
|
91
|
+
| 'publicContractRegions[].signatureHash'
|
|
92
|
+
| 'publicContractRegions[].contractHash'
|
|
93
|
+
| string;
|
|
94
|
+
|
|
95
|
+
export interface NativeProjectSymbolGraphFileHashRecord {
|
|
96
|
+
readonly id: string;
|
|
97
|
+
readonly documentId: string;
|
|
98
|
+
readonly sourcePath?: string;
|
|
99
|
+
readonly language?: FrontierSourceLanguage | string;
|
|
100
|
+
readonly sourceHash: string;
|
|
101
|
+
readonly algorithm?: string;
|
|
102
|
+
readonly value: string;
|
|
103
|
+
readonly factId?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface NativeProjectSymbolGraphModuleEdgeRecord {
|
|
107
|
+
readonly id: string;
|
|
108
|
+
readonly sourceDocumentId: string;
|
|
109
|
+
readonly targetSymbolId: string;
|
|
110
|
+
readonly predicate: 'imports' | 'exports' | string;
|
|
111
|
+
readonly edgeKind?: 'import' | 'export' | 're-export' | string;
|
|
112
|
+
readonly sourcePath?: string;
|
|
113
|
+
readonly sourceHash?: string;
|
|
114
|
+
readonly moduleSpecifier?: string;
|
|
115
|
+
readonly importKind?: string;
|
|
116
|
+
readonly exportKind?: string;
|
|
117
|
+
readonly importedName?: string;
|
|
118
|
+
readonly exportedName?: string;
|
|
119
|
+
readonly localName?: string;
|
|
120
|
+
readonly namespace?: string;
|
|
121
|
+
readonly isTypeOnly?: boolean;
|
|
122
|
+
readonly isReExport?: boolean;
|
|
123
|
+
readonly publicContract?: boolean;
|
|
124
|
+
readonly evidenceIds?: readonly string[];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface NativeProjectSymbolGraphReExportIdentityRecord {
|
|
128
|
+
readonly kind?: 'frontier.lang.reExportIdentity' | string;
|
|
129
|
+
readonly version?: 1;
|
|
130
|
+
readonly id: string;
|
|
131
|
+
readonly sourceDocumentId?: string;
|
|
132
|
+
readonly sourcePath?: string;
|
|
133
|
+
readonly sourceHash?: string;
|
|
134
|
+
readonly moduleSpecifier?: string;
|
|
135
|
+
readonly exportedName?: string;
|
|
136
|
+
readonly importedName?: string;
|
|
137
|
+
readonly localName?: string;
|
|
138
|
+
readonly namespace?: string;
|
|
139
|
+
readonly isTypeOnly?: boolean;
|
|
140
|
+
readonly symbolId?: string;
|
|
141
|
+
readonly relationId?: string;
|
|
142
|
+
readonly ownershipRegionId?: string;
|
|
143
|
+
readonly ownershipRegionKey?: string;
|
|
144
|
+
readonly publicContract?: boolean;
|
|
145
|
+
readonly factId?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface NativeProjectSymbolGraphPublicContractRegionRecord {
|
|
149
|
+
readonly id: string;
|
|
150
|
+
readonly key?: string;
|
|
151
|
+
readonly regionKind?: string;
|
|
152
|
+
readonly granularity?: string;
|
|
153
|
+
readonly language?: FrontierSourceLanguage | string;
|
|
154
|
+
readonly documentId?: string;
|
|
155
|
+
readonly sourcePath?: string;
|
|
156
|
+
readonly sourceHash?: string;
|
|
157
|
+
readonly symbolId?: string;
|
|
158
|
+
readonly symbolName?: string;
|
|
159
|
+
readonly symbolKind?: string;
|
|
160
|
+
readonly nativeAstNodeId?: string;
|
|
161
|
+
readonly sourceSpan?: SourceSpan;
|
|
162
|
+
readonly precision?: string;
|
|
163
|
+
readonly publicContract?: boolean;
|
|
164
|
+
readonly exportedName?: string;
|
|
165
|
+
readonly moduleSpecifier?: string;
|
|
166
|
+
readonly edgeKind?: string;
|
|
167
|
+
readonly factId?: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface NativeProjectSymbolGraphSummary {
|
|
171
|
+
readonly kind: 'frontier.lang.projectSymbolGraph';
|
|
172
|
+
readonly version: 1;
|
|
173
|
+
readonly projectRoot?: string;
|
|
174
|
+
readonly sourceCount: number;
|
|
175
|
+
readonly documentCount: number;
|
|
176
|
+
readonly symbolCount: number;
|
|
177
|
+
readonly occurrenceCount: number;
|
|
178
|
+
readonly relationCount: number;
|
|
179
|
+
readonly factCount: number;
|
|
180
|
+
readonly fileHashes: readonly NativeProjectSymbolGraphFileHashRecord[];
|
|
181
|
+
readonly importEdges: readonly NativeProjectSymbolGraphModuleEdgeRecord[];
|
|
182
|
+
readonly exportEdges: readonly NativeProjectSymbolGraphModuleEdgeRecord[];
|
|
183
|
+
readonly reExportIdentities: readonly NativeProjectSymbolGraphReExportIdentityRecord[];
|
|
184
|
+
readonly publicContractRegions: readonly NativeProjectSymbolGraphPublicContractRegionRecord[];
|
|
185
|
+
readonly remainingFields: readonly NativeProjectSymbolGraphRemainingField[];
|
|
186
|
+
}
|
|
187
|
+
|
|
80
188
|
export interface NativeProjectImportResultMetadata extends Record<string, unknown> {
|
|
81
189
|
readonly importResultContract?: NativeImportResultContract;
|
|
82
190
|
readonly projectAdmission?: NativeProjectImportAdmission;
|
|
@@ -84,6 +192,7 @@ export interface NativeProjectImportResultMetadata extends Record<string, unknow
|
|
|
84
192
|
readonly nativeImportLossSummary?: NativeImportLossSummary;
|
|
85
193
|
readonly semanticMergeReadiness?: SemanticMergeReadiness;
|
|
86
194
|
readonly readinessReasons?: readonly string[];
|
|
195
|
+
readonly projectSymbolGraph?: NativeProjectSymbolGraphSummary;
|
|
87
196
|
}
|
|
88
197
|
|
|
89
198
|
export interface NativeProjectImportResult {
|
|
@@ -102,6 +211,7 @@ export interface NativeProjectImportResult {
|
|
|
102
211
|
readonly losses: readonly NativeAstLossRecord[];
|
|
103
212
|
readonly evidence: readonly EvidenceRecord[];
|
|
104
213
|
readonly mergeCandidates: readonly SemanticMergeCandidateRecord[];
|
|
214
|
+
readonly projectSymbolGraph?: NativeProjectSymbolGraphSummary;
|
|
105
215
|
readonly metadata?: NativeProjectImportResultMetadata;
|
|
106
216
|
}
|
|
107
217
|
|
|
@@ -4,7 +4,15 @@ export function createNativeProjectImportResult(input, imports) {
|
|
|
4
4
|
const idPart = idFragment(input.id ?? input.projectRoot ?? 'native_project');
|
|
5
5
|
const nodes = {};
|
|
6
6
|
const rootIds = [];
|
|
7
|
-
const
|
|
7
|
+
const mergedSemanticIndex = mergeSemanticIndexes(imports, input, idPart);
|
|
8
|
+
const projectSymbolGraph = createProjectSymbolGraphSummary(mergedSemanticIndex, imports, input);
|
|
9
|
+
const semanticIndex = mergedSemanticIndex ? {
|
|
10
|
+
...mergedSemanticIndex,
|
|
11
|
+
metadata: {
|
|
12
|
+
...mergedSemanticIndex.metadata,
|
|
13
|
+
projectSymbolGraph
|
|
14
|
+
}
|
|
15
|
+
} : mergedSemanticIndex;
|
|
8
16
|
const nativeSources = [];
|
|
9
17
|
const sourceMaps = [];
|
|
10
18
|
const losses = [];
|
|
@@ -43,6 +51,7 @@ export function createNativeProjectImportResult(input, imports) {
|
|
|
43
51
|
sourceCount: imports.length,
|
|
44
52
|
nativeImportLossSummary,
|
|
45
53
|
sourcePreservationSummary,
|
|
54
|
+
projectSymbolGraph,
|
|
46
55
|
...input.documentMetadata
|
|
47
56
|
}
|
|
48
57
|
});
|
|
@@ -60,6 +69,7 @@ export function createNativeProjectImportResult(input, imports) {
|
|
|
60
69
|
sourceCount: imports.length,
|
|
61
70
|
nativeImportLossSummary,
|
|
62
71
|
sourcePreservationSummary,
|
|
72
|
+
projectSymbolGraph,
|
|
63
73
|
...input.universalAstMetadata
|
|
64
74
|
}
|
|
65
75
|
});
|
|
@@ -75,7 +85,8 @@ export function createNativeProjectImportResult(input, imports) {
|
|
|
75
85
|
sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
|
|
76
86
|
sourceCount: imports.length,
|
|
77
87
|
nativeImportLossSummary,
|
|
78
|
-
sourcePreservationSummary
|
|
88
|
+
sourcePreservationSummary,
|
|
89
|
+
projectSymbolGraph
|
|
79
90
|
}
|
|
80
91
|
});
|
|
81
92
|
const projectResult = {
|
|
@@ -94,11 +105,13 @@ export function createNativeProjectImportResult(input, imports) {
|
|
|
94
105
|
losses: uniqueLosses,
|
|
95
106
|
evidence: uniqueEvidence,
|
|
96
107
|
mergeCandidates,
|
|
108
|
+
projectSymbolGraph,
|
|
97
109
|
metadata: {
|
|
98
110
|
sourceCount: imports.length,
|
|
99
111
|
sourcePaths: imports.map((result) => result.sourcePath).filter(Boolean),
|
|
100
112
|
nativeImportLossSummary,
|
|
101
113
|
sourcePreservationSummary,
|
|
114
|
+
projectSymbolGraph,
|
|
102
115
|
...input.metadata
|
|
103
116
|
}
|
|
104
117
|
};
|
|
@@ -120,7 +133,168 @@ export function createNativeProjectImportResult(input, imports) {
|
|
|
120
133
|
readinessReasons: importResultContract.readiness.reasons,
|
|
121
134
|
regionSummary: importResultContract.regions,
|
|
122
135
|
sourceMapSummary: importResultContract.sourceMaps,
|
|
123
|
-
adapterCoverageSummary: importResultContract.adapterCoverage
|
|
136
|
+
adapterCoverageSummary: importResultContract.adapterCoverage,
|
|
137
|
+
projectSymbolGraph
|
|
124
138
|
}
|
|
125
139
|
};
|
|
126
140
|
}
|
|
141
|
+
|
|
142
|
+
const PROJECT_SYMBOL_GRAPH_REMAINING_FIELDS = Object.freeze([
|
|
143
|
+
'moduleEdges[].resolvedModulePath',
|
|
144
|
+
'moduleEdges[].targetDocumentId',
|
|
145
|
+
'moduleEdges[].resolvedTargetSymbolId',
|
|
146
|
+
'moduleEdges[].resolutionKind',
|
|
147
|
+
'moduleEdges[].packageName',
|
|
148
|
+
'moduleEdges[].packageExportCondition',
|
|
149
|
+
'reExportIdentities[].originSymbolId',
|
|
150
|
+
'reExportIdentities[].exportedSymbolId',
|
|
151
|
+
'reExportIdentities[].localSymbolId',
|
|
152
|
+
'publicContractRegions[].apiSurfaceKind',
|
|
153
|
+
'publicContractRegions[].signatureHash',
|
|
154
|
+
'publicContractRegions[].contractHash'
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
function createProjectSymbolGraphSummary(semanticIndex, imports, input) {
|
|
158
|
+
const documents = semanticIndex?.documents ?? [];
|
|
159
|
+
const symbolsById = new Map((semanticIndex?.symbols ?? []).map((symbol) => [symbol.id, symbol]));
|
|
160
|
+
const documentsById = new Map(documents.map((document) => [document.id, document]));
|
|
161
|
+
const facts = semanticIndex?.facts ?? [];
|
|
162
|
+
const moduleEdgeFacts = facts.filter((fact) => fact.predicate === 'moduleEdge');
|
|
163
|
+
const moduleEdgeByRelation = new Map(moduleEdgeFacts.map((fact) => [fact.subjectId, fact]));
|
|
164
|
+
const relations = semanticIndex?.relations ?? [];
|
|
165
|
+
const importEdges = relations
|
|
166
|
+
.filter((relation) => relation.predicate === 'imports')
|
|
167
|
+
.map((relation) => moduleEdgeRecord(relation, moduleEdgeByRelation, symbolsById, documentsById));
|
|
168
|
+
const exportEdges = relations
|
|
169
|
+
.filter((relation) => relation.predicate === 'exports')
|
|
170
|
+
.map((relation) => moduleEdgeRecord(relation, moduleEdgeByRelation, symbolsById, documentsById));
|
|
171
|
+
const reExportIdentities = uniqueRecords([
|
|
172
|
+
...facts
|
|
173
|
+
.filter((fact) => fact.predicate === 'reExportIdentity' && fact.value)
|
|
174
|
+
.map((fact) => ({ ...objectValue(fact.value), factId: fact.id })),
|
|
175
|
+
...exportEdges
|
|
176
|
+
.filter((edge) => edge.isReExport)
|
|
177
|
+
.map((edge) => compactRecord({
|
|
178
|
+
id: `reexport_${idFragment(edge.id)}`,
|
|
179
|
+
sourceDocumentId: edge.sourceDocumentId,
|
|
180
|
+
sourcePath: edge.sourcePath,
|
|
181
|
+
sourceHash: edge.sourceHash,
|
|
182
|
+
moduleSpecifier: edge.moduleSpecifier,
|
|
183
|
+
symbolId: edge.targetSymbolId,
|
|
184
|
+
relationId: edge.id,
|
|
185
|
+
publicContract: edge.publicContract
|
|
186
|
+
}))
|
|
187
|
+
]);
|
|
188
|
+
const publicContractRegions = uniqueRecords(facts
|
|
189
|
+
.filter((fact) => fact.predicate === 'publicContractRegion' && fact.value)
|
|
190
|
+
.map((fact) => ({ ...objectValue(fact.value), factId: fact.id, symbolId: fact.subjectId })));
|
|
191
|
+
const fileHashes = uniqueRecords([
|
|
192
|
+
...documents.map((document) => fileHashRecord(document)),
|
|
193
|
+
...facts
|
|
194
|
+
.filter((fact) => fact.predicate === 'fileHash' && fact.value)
|
|
195
|
+
.map((fact) => ({ ...objectValue(fact.value), id: `file_hash_${idFragment(fact.subjectId)}`, factId: fact.id, documentId: fact.subjectId }))
|
|
196
|
+
].filter(Boolean));
|
|
197
|
+
return {
|
|
198
|
+
kind: 'frontier.lang.projectSymbolGraph',
|
|
199
|
+
version: 1,
|
|
200
|
+
projectRoot: input.projectRoot,
|
|
201
|
+
sourceCount: imports.length,
|
|
202
|
+
documentCount: documents.length,
|
|
203
|
+
symbolCount: semanticIndex?.symbols?.length ?? 0,
|
|
204
|
+
occurrenceCount: semanticIndex?.occurrences?.length ?? 0,
|
|
205
|
+
relationCount: relations.length,
|
|
206
|
+
factCount: facts.length,
|
|
207
|
+
fileHashes,
|
|
208
|
+
importEdges,
|
|
209
|
+
exportEdges,
|
|
210
|
+
reExportIdentities,
|
|
211
|
+
publicContractRegions,
|
|
212
|
+
remainingFields: PROJECT_SYMBOL_GRAPH_REMAINING_FIELDS
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function moduleEdgeRecord(relation, moduleEdgeByRelation, symbolsById, documentsById) {
|
|
217
|
+
const fact = moduleEdgeByRelation.get(relation.id);
|
|
218
|
+
const value = objectValue(fact?.value);
|
|
219
|
+
const metadata = objectValue(relation.metadata);
|
|
220
|
+
const moduleEdge = objectValue(metadata.moduleEdge);
|
|
221
|
+
const symbol = symbolsById.get(relation.targetId);
|
|
222
|
+
const symbolMetadata = objectValue(symbol?.metadata);
|
|
223
|
+
const document = documentsById.get(relation.sourceId);
|
|
224
|
+
const moduleSpecifier = firstString(
|
|
225
|
+
moduleEdge.moduleSpecifier,
|
|
226
|
+
value.moduleSpecifier,
|
|
227
|
+
metadata.moduleSpecifier,
|
|
228
|
+
symbolMetadata.moduleSpecifier,
|
|
229
|
+
symbol?.kind === 'module' ? symbol.name : undefined
|
|
230
|
+
);
|
|
231
|
+
return compactRecord({
|
|
232
|
+
id: relation.id,
|
|
233
|
+
sourceDocumentId: relation.sourceId,
|
|
234
|
+
targetSymbolId: relation.targetId,
|
|
235
|
+
predicate: relation.predicate,
|
|
236
|
+
edgeKind: firstString(moduleEdge.edgeKind, value.edgeKind, relation.predicate === 'imports' ? 'import' : moduleSpecifier ? 're-export' : 'export'),
|
|
237
|
+
sourcePath: document?.path,
|
|
238
|
+
sourceHash: document?.sourceHash,
|
|
239
|
+
moduleSpecifier,
|
|
240
|
+
importKind: firstString(moduleEdge.importKind, value.importKind),
|
|
241
|
+
exportKind: firstString(moduleEdge.exportKind, value.exportKind),
|
|
242
|
+
importedName: firstString(moduleEdge.importedName, value.importedName),
|
|
243
|
+
exportedName: firstString(moduleEdge.exportedName, value.exportedName),
|
|
244
|
+
localName: firstString(moduleEdge.localName, value.localName),
|
|
245
|
+
namespace: firstString(moduleEdge.namespace, value.namespace),
|
|
246
|
+
isTypeOnly: firstBoolean(moduleEdge.isTypeOnly, value.isTypeOnly),
|
|
247
|
+
isReExport: firstBoolean(moduleEdge.isReExport, value.isReExport) ?? (relation.predicate === 'exports' && Boolean(moduleSpecifier)),
|
|
248
|
+
publicContract: firstBoolean(moduleEdge.publicContract, value.publicContract, metadata.publicContract),
|
|
249
|
+
evidenceIds: uniqueStrings([...(relation.evidenceIds ?? []), ...(fact?.evidenceIds ?? [])])
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function fileHashRecord(document) {
|
|
254
|
+
if (!document?.sourceHash) return undefined;
|
|
255
|
+
const sourceHash = String(document.sourceHash);
|
|
256
|
+
const separator = sourceHash.indexOf(':');
|
|
257
|
+
return compactRecord({
|
|
258
|
+
id: `file_hash_${idFragment(document.id)}`,
|
|
259
|
+
documentId: document.id,
|
|
260
|
+
sourcePath: document.path,
|
|
261
|
+
language: document.language,
|
|
262
|
+
sourceHash,
|
|
263
|
+
algorithm: separator > 0 ? sourceHash.slice(0, separator) : undefined,
|
|
264
|
+
value: separator > 0 ? sourceHash.slice(separator + 1) : sourceHash
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function objectValue(value) {
|
|
269
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function compactRecord(record) {
|
|
273
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function uniqueRecords(records) {
|
|
277
|
+
const seen = new Set();
|
|
278
|
+
const result = [];
|
|
279
|
+
for (const record of records) {
|
|
280
|
+
const key = record.id ?? record.factId ?? JSON.stringify(record);
|
|
281
|
+
if (seen.has(key)) continue;
|
|
282
|
+
seen.add(key);
|
|
283
|
+
result.push(record);
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function firstString(...values) {
|
|
289
|
+
for (const value of values) {
|
|
290
|
+
if (value !== undefined && value !== null && String(value)) return String(value);
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function firstBoolean(...values) {
|
|
296
|
+
for (const value of values) {
|
|
297
|
+
if (typeof value === 'boolean') return value;
|
|
298
|
+
}
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
@@ -207,7 +207,7 @@ function currentSymbolRangeLabel(edit) {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
function replayStatus(reasonCodes, edits, projection) {
|
|
210
|
-
if (reasonCodes.
|
|
210
|
+
if (reasonCodes.length) return 'blocked';
|
|
211
211
|
if (!edits.length && !(projection.edits ?? []).length) return 'evidence-only';
|
|
212
212
|
if (edits.some((edit) => edit.status === 'blocked')) return 'blocked';
|
|
213
213
|
if (edits.some((edit) => edit.status === 'conflict')) return 'conflict';
|