@shapeshift-labs/frontier-lang-compiler 0.2.100 → 0.2.102
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/dist/declarations/bidirectional-target-change-evidence.d.ts +299 -0
- package/dist/declarations/bidirectional-target-change.d.ts +19 -120
- package/dist/declarations/import-adapter-core.d.ts +6 -0
- package/dist/declarations/native-project-admission.d.ts +43 -22
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +24 -0
- package/dist/declarations/semantic-edit-script.d.ts +20 -15
- package/dist/declarations/semantic-lineage.d.ts +3 -21
- package/dist/declarations/semantic-merge-candidates.d.ts +39 -0
- package/dist/declarations/semantic-sidecar-admission.d.ts +14 -0
- package/dist/declarations/semantic-sidecar.d.ts +12 -14
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +200 -0
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +62 -17
- package/dist/internal/index-impl/createLightweightNativeImport.js +9 -1
- package/dist/internal/index-impl/createNativeSourcePreservation.js +16 -1
- package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +151 -1
- package/dist/internal/index-impl/createSemanticImportSidecar.js +5 -0
- package/dist/internal/index-impl/createSemanticImportSidecarAdmission.js +29 -11
- package/dist/internal/index-impl/importNativeSource.js +14 -14
- package/dist/internal/index-impl/nativeChangeProjectionEndpoint.js +56 -16
- package/dist/internal/index-impl/nativeImportSemanticIndex.js +33 -0
- package/dist/internal/index-impl/projectImportAdmissionMergeScore.js +26 -74
- package/dist/internal/index-impl/projectImportAdmissionProjectionCoverage.js +74 -0
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +39 -13
- package/dist/internal/index-impl/replaySemanticEditProjection.js +65 -23
- package/dist/internal/index-impl/semanticEditInsertionAnchors.js +8 -5
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +167 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +94 -15
- package/dist/internal/index-impl/semanticHistoryLineageResolution.js +21 -2
- package/dist/internal/index-impl/semanticLineageHashEvidence.js +97 -0
- package/dist/internal/index-impl/semanticLineageInferenceMatching.js +8 -0
- package/dist/internal/index-impl/semanticLineageResolutionRecords.js +18 -1
- package/dist/internal/index-impl/semanticMergeCandidateRecords.js +22 -2
- package/dist/internal/index-impl/semanticMergeCandidateScoreFacets.js +221 -0
- package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +23 -1
- package/dist/internal/index-impl/sourcePreservationFromProjectionContext.js +9 -2
- package/dist/native-import-language-profiles.js +10 -2
- package/dist/native-region-scanner-js-helpers.js +8 -2
- package/dist/native-region-scanner-js-imports.js +7 -0
- package/dist/native-region-scanner-js.js +4 -4
- package/dist/native-region-scanner.js +2 -1
- package/dist/semantic-import-regions.js +18 -5
- package/dist/semantic-import-sidecar-admission-types.d.ts +14 -0
- package/dist/semantic-import-sidecar-entry.js +151 -7
- package/dist/semantic-import-sidecar-types.d.ts +18 -13
- package/dist/semantic-import-source-preservation-utils.js +55 -0
- package/dist/semantic-import-source-preservation.js +98 -3
- package/package.json +1 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { uniqueStrings } from '../../native-import-utils.js';
|
|
2
|
+
|
|
3
|
+
const scoreFacetWeights = Object.freeze({ ownership: 18, staleStatus: 20, testEvidence: 18, overlap: 18, size: 10, semanticSidecarQuality: 16 });
|
|
4
|
+
|
|
5
|
+
export function semanticMergeCandidateScoreFacets(input) {
|
|
6
|
+
const components = {
|
|
7
|
+
ownership: ownershipFacet(input),
|
|
8
|
+
staleStatus: staleStatusFacet(input),
|
|
9
|
+
testEvidence: testEvidenceFacet(input),
|
|
10
|
+
overlap: overlapFacet(input),
|
|
11
|
+
size: sizeFacet(input),
|
|
12
|
+
semanticSidecarQuality: semanticSidecarQualityFacet(input)
|
|
13
|
+
};
|
|
14
|
+
const weightedTotal = Object.values(components).reduce((sum, component) => sum + component.weightedScore, 0);
|
|
15
|
+
const weightTotal = Object.values(components).reduce((sum, component) => sum + component.weight, 0);
|
|
16
|
+
const value = roundScore(weightTotal ? weightedTotal * 100 / weightTotal : 0);
|
|
17
|
+
const weakFacets = Object.values(components).filter((component) => component.status === 'weak').map((component) => component.key);
|
|
18
|
+
const blockedFacets = Object.values(components).filter((component) => component.status === 'blocked').map((component) => component.key);
|
|
19
|
+
const lowestScore = Math.min(...Object.values(components).map((component) => component.score));
|
|
20
|
+
return {
|
|
21
|
+
schema: 'frontier.lang.semanticMergeCandidateScoreFacets.v1',
|
|
22
|
+
version: 1,
|
|
23
|
+
higherIsBetter: true,
|
|
24
|
+
value,
|
|
25
|
+
risk: value < 50 || blockedFacets.length ? 'high' : value < 80 || weakFacets.length ? 'medium' : 'low',
|
|
26
|
+
components,
|
|
27
|
+
summary: {
|
|
28
|
+
value,
|
|
29
|
+
risk: value < 50 || blockedFacets.length ? 'high' : value < 80 || weakFacets.length ? 'medium' : 'low',
|
|
30
|
+
lowestScore,
|
|
31
|
+
weakFacets,
|
|
32
|
+
blockedFacets,
|
|
33
|
+
availableFacets: Object.values(components).filter((component) => component.signals.available !== false).map((component) => component.key)
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ownershipFacet(input) {
|
|
39
|
+
const regions = input.changedSemanticRegions;
|
|
40
|
+
const keyedRegions = regions.filter((region) => region.key || region.conflictKey);
|
|
41
|
+
const kindedRegions = regions.filter((region) => region.regionKind);
|
|
42
|
+
const spannedRegions = regions.filter((region) => region.sourceSpan);
|
|
43
|
+
const score = regions.length
|
|
44
|
+
? roundScore(keyedRegions.length * 70 / regions.length + kindedRegions.length * 15 / regions.length + spannedRegions.length * 15 / regions.length)
|
|
45
|
+
: input.conflictKeys.length ? 45 : 70;
|
|
46
|
+
return scoreFacet('ownership', score, [
|
|
47
|
+
...(regions.length === 0 ? ['missing-changed-semantic-regions'] : []),
|
|
48
|
+
...(regions.length && keyedRegions.length < regions.length ? ['missing-ownership-keys'] : [])
|
|
49
|
+
], {
|
|
50
|
+
changedSemanticRegions: regions.length,
|
|
51
|
+
keyedRegions: keyedRegions.length,
|
|
52
|
+
regionKinds: uniqueStrings(regions.map((region) => region.regionKind)),
|
|
53
|
+
conflictKeys: input.conflictKeys,
|
|
54
|
+
ownershipKeys: uniqueStrings(regions.map((region) => region.key))
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function staleStatusFacet(input) {
|
|
59
|
+
const staleEvidence = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'stale' || record?.metadata?.stale === true);
|
|
60
|
+
const sourceHashVerified = [
|
|
61
|
+
input.source?.metadata?.sourceHashVerified,
|
|
62
|
+
input.source?.before?.metadata?.sourceHashVerified,
|
|
63
|
+
input.source?.after?.metadata?.sourceHashVerified,
|
|
64
|
+
input.candidate?.metadata?.sourceHashVerified,
|
|
65
|
+
input.patch?.metadata?.sourceHashVerified
|
|
66
|
+
].filter((value) => value !== undefined);
|
|
67
|
+
const staleProofs = semanticSidecarQuality(input)?.proofSummary?.stale ?? 0;
|
|
68
|
+
const sourceHashStale = sourceHashVerified.includes(false);
|
|
69
|
+
const score = sourceHashStale ? 0 : staleEvidence.length ? 35 : staleProofs > 0 ? 55 : 100;
|
|
70
|
+
return scoreFacet('staleStatus', score, [
|
|
71
|
+
...(sourceHashStale ? ['stale-source-hash'] : []),
|
|
72
|
+
...(staleEvidence.length ? ['stale-evidence'] : []),
|
|
73
|
+
...(staleProofs > 0 ? ['stale-sidecar-proof-obligations'] : [])
|
|
74
|
+
], {
|
|
75
|
+
staleEvidenceIds: staleEvidence.map((record) => record.id).filter(Boolean),
|
|
76
|
+
staleProofObligations: staleProofs,
|
|
77
|
+
sourceHashVerified
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function testEvidenceFacet(input) {
|
|
82
|
+
const failed = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'failed');
|
|
83
|
+
const stale = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'stale');
|
|
84
|
+
const pending = input.evidenceRecords.filter((record) => ['pending', 'assumed', 'unknown'].includes(evidenceStatus(record)));
|
|
85
|
+
const passed = input.evidenceRecords.filter((record) => ['passed', 'ok', 'success'].includes(evidenceStatus(record)));
|
|
86
|
+
const score = failed.length ? 0 : stale.length ? 45 : pending.length ? 65 : input.proofIds.length ? 100 : input.evidenceIds.length ? 82 : 35;
|
|
87
|
+
return scoreFacet('testEvidence', score, [
|
|
88
|
+
...(failed.length ? ['failed-evidence'] : []),
|
|
89
|
+
...(stale.length ? ['stale-evidence'] : []),
|
|
90
|
+
...(pending.length ? ['pending-evidence'] : []),
|
|
91
|
+
...(!input.evidenceIds.length && !input.proofIds.length ? ['missing-evidence'] : [])
|
|
92
|
+
], {
|
|
93
|
+
evidenceIds: input.evidenceIds,
|
|
94
|
+
proofIds: input.proofIds,
|
|
95
|
+
evidenceRecords: input.evidenceRecords.length,
|
|
96
|
+
passedEvidenceIds: passed.map((record) => record.id).filter(Boolean),
|
|
97
|
+
failedEvidenceIds: failed.map((record) => record.id).filter(Boolean),
|
|
98
|
+
staleEvidenceIds: stale.map((record) => record.id).filter(Boolean),
|
|
99
|
+
pendingEvidenceIds: pending.map((record) => record.id).filter(Boolean)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function overlapFacet(input) {
|
|
104
|
+
const high = input.overlaps.filter((overlap) => overlap.risk === 'high').length;
|
|
105
|
+
const medium = input.overlaps.filter((overlap) => overlap.risk === 'medium').length;
|
|
106
|
+
const score = clampScore(100 - high * 45 - medium * 25 - Math.max(0, input.overlaps.length - high - medium) * 15);
|
|
107
|
+
return scoreFacet('overlap', score, [
|
|
108
|
+
...(input.overlaps.length ? ['overlapping-semantic-regions'] : [])
|
|
109
|
+
], {
|
|
110
|
+
overlaps: input.overlaps.length,
|
|
111
|
+
highRiskOverlaps: high,
|
|
112
|
+
mediumRiskOverlaps: medium,
|
|
113
|
+
conflictKeys: uniqueStrings(input.overlaps.flatMap((overlap) => overlap.conflictKeys ?? [])),
|
|
114
|
+
byKind: countBy(input.overlaps.map((overlap) => overlap.overlapKind))
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function sizeFacet(input) {
|
|
119
|
+
const operationCount = array(input.candidate?.operations ?? input.patch?.operations).length;
|
|
120
|
+
const changedSemanticRegions = input.changedSemanticRegions.length;
|
|
121
|
+
const conflictKeys = input.conflictKeys.length;
|
|
122
|
+
const score = clampScore(100 - Math.max(0, changedSemanticRegions - 3) * 8 - Math.max(0, operationCount - 3) * 4 - Math.max(0, conflictKeys - 4) * 3);
|
|
123
|
+
return scoreFacet('size', score, [
|
|
124
|
+
...(changedSemanticRegions > 3 ? ['large-changed-region-set'] : []),
|
|
125
|
+
...(operationCount > 3 ? ['large-operation-set'] : []),
|
|
126
|
+
...(conflictKeys > 4 ? ['many-conflict-keys'] : [])
|
|
127
|
+
], { changedSemanticRegions, operationCount, conflictKeys });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function semanticSidecarQualityFacet(input) {
|
|
131
|
+
const quality = semanticSidecarQuality(input);
|
|
132
|
+
if (!quality) return scoreFacet('semanticSidecarQuality', 100, [], { available: false });
|
|
133
|
+
const proofSummary = quality.proofSummary ?? {};
|
|
134
|
+
const warningCodes = uniqueStrings([...(quality.warnings ?? []).map((warning) => warning.code), ...strings(quality.expectedMissingReasonCodes)]);
|
|
135
|
+
const reviewProofs = (proofSummary.open ?? 0) + (proofSummary.stale ?? 0) + (proofSummary.assumed ?? 0) + (proofSummary.externalToolRequired ?? 0) + (proofSummary.unknown ?? 0);
|
|
136
|
+
const score = clampScore(
|
|
137
|
+
(quality.imported === false || quality.selected === false ? 25 : 100)
|
|
138
|
+
- (quality.eligible === false ? 35 : 0)
|
|
139
|
+
- Math.min(35, (quality.warningCount ?? warningCodes.length ?? 0) * 7)
|
|
140
|
+
- Math.min(40, (proofSummary.failed ?? 0) * 20)
|
|
141
|
+
- Math.min(25, reviewProofs * 5)
|
|
142
|
+
);
|
|
143
|
+
return scoreFacet('semanticSidecarQuality', score, [
|
|
144
|
+
...(quality.imported === false || quality.selected === false ? ['semantic-sidecar-not-imported'] : []),
|
|
145
|
+
...(quality.eligible === false ? ['semantic-sidecar-not-eligible'] : []),
|
|
146
|
+
...warningCodes
|
|
147
|
+
], {
|
|
148
|
+
available: true,
|
|
149
|
+
expected: quality.expected,
|
|
150
|
+
expectedSatisfied: quality.expectedSatisfied,
|
|
151
|
+
selected: quality.selected,
|
|
152
|
+
imported: quality.imported,
|
|
153
|
+
eligible: quality.eligible,
|
|
154
|
+
importCount: quality.importCount,
|
|
155
|
+
symbolCount: quality.symbolCount,
|
|
156
|
+
ownershipRegionCount: quality.ownershipRegionCount,
|
|
157
|
+
patchHintCount: quality.patchHintCount,
|
|
158
|
+
evidenceCount: quality.evidenceCount,
|
|
159
|
+
warningCount: quality.warningCount ?? warningCodes.length,
|
|
160
|
+
warningCodes,
|
|
161
|
+
proofSummary
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function semanticSidecarQuality(input) {
|
|
166
|
+
return [
|
|
167
|
+
input.candidate?.semanticSidecarQuality,
|
|
168
|
+
input.candidate?.sidecarQuality,
|
|
169
|
+
input.candidate?.semanticSidecar?.quality,
|
|
170
|
+
input.candidate?.sidecar?.quality,
|
|
171
|
+
input.candidate?.metadata?.semanticSidecarQuality,
|
|
172
|
+
input.candidate?.metadata?.sidecarQuality,
|
|
173
|
+
input.source?.semanticSidecarQuality,
|
|
174
|
+
input.source?.sidecarQuality,
|
|
175
|
+
input.source?.semanticSidecar?.quality,
|
|
176
|
+
input.source?.sidecar?.quality,
|
|
177
|
+
input.source?.metadata?.semanticSidecarQuality,
|
|
178
|
+
input.source?.metadata?.sidecarQuality,
|
|
179
|
+
input.patch?.semanticSidecarQuality,
|
|
180
|
+
input.patch?.metadata?.semanticSidecarQuality
|
|
181
|
+
].find(looksLikeSemanticSidecarQuality);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function looksLikeSemanticSidecarQuality(value) {
|
|
185
|
+
return value && typeof value === 'object' && (
|
|
186
|
+
value.schema === 'frontier.lang.semanticSidecarQuality.v1'
|
|
187
|
+
|| value.eligible !== undefined
|
|
188
|
+
|| value.imported !== undefined
|
|
189
|
+
|| value.proofSummary !== undefined
|
|
190
|
+
|| value.warningCount !== undefined
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function scoreFacet(key, score, reasonCodes, signals) {
|
|
195
|
+
const normalizedScore = clampScore(score);
|
|
196
|
+
const weight = scoreFacetWeights[key] ?? 1;
|
|
197
|
+
return {
|
|
198
|
+
key,
|
|
199
|
+
score: normalizedScore,
|
|
200
|
+
weight,
|
|
201
|
+
weightedScore: roundScore(normalizedScore * weight / 100),
|
|
202
|
+
status: facetStatus(normalizedScore),
|
|
203
|
+
reasonCodes: uniqueStrings(reasonCodes),
|
|
204
|
+
signals: compactRecord(signals)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function facetStatus(score) {
|
|
209
|
+
if (score <= 0) return 'blocked';
|
|
210
|
+
if (score < 50) return 'weak';
|
|
211
|
+
if (score < 80) return 'partial';
|
|
212
|
+
return 'strong';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function evidenceStatus(record) { return String(record?.status ?? record?.metadata?.status ?? '').toLowerCase(); }
|
|
216
|
+
function countBy(values) { const counts = {}; for (const value of values ?? []) { const key = String(value ?? 'unknown'); counts[key] = (counts[key] ?? 0) + 1; } return counts; }
|
|
217
|
+
function clampScore(value) { return Math.max(0, Math.min(100, roundScore(value))); }
|
|
218
|
+
function roundScore(value) { return Math.round((Number.isFinite(value) ? value : 0) * 100) / 100; }
|
|
219
|
+
function array(value) { if (value === undefined || value === null) return []; return Array.isArray(value) ? value : [value]; }
|
|
220
|
+
function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
|
|
221
|
+
function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
|
|
@@ -90,7 +90,7 @@ export function querySemanticPatchBundleOverlaps(records,query={}){
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function sharedIndex(left,right,options){
|
|
93
|
-
|
|
93
|
+
const shared={
|
|
94
94
|
operationContentHashes:intersect(left.operationContentHashes,right.operationContentHashes),
|
|
95
95
|
editContentHashes:intersect(left.editContentHashes,right.editContentHashes),
|
|
96
96
|
semanticEditKeys:intersect(left.semanticEditKeys,right.semanticEditKeys),
|
|
@@ -110,6 +110,28 @@ function sharedIndex(left,right,options){
|
|
|
110
110
|
baseHashes:intersect(left.baseHashes,right.baseHashes),
|
|
111
111
|
targetHashes:intersect(left.targetHashes,right.targetHashes)
|
|
112
112
|
};
|
|
113
|
+
const scopedEdit=hasSharedEditScope(shared);
|
|
114
|
+
const scopedSource=hasSharedSourceScope(shared);
|
|
115
|
+
return{
|
|
116
|
+
...shared,
|
|
117
|
+
operationContentHashes:scopedEdit?shared.operationContentHashes:[],
|
|
118
|
+
editContentHashes:scopedEdit?shared.editContentHashes:[],
|
|
119
|
+
semanticEditKeys:scopedSource?shared.semanticEditKeys:[],
|
|
120
|
+
semanticIdentityHashes:scopedSource?shared.semanticIdentityHashes:[],
|
|
121
|
+
semanticEditReplayCurrentHashes:scopedSource?shared.semanticEditReplayCurrentHashes:[],
|
|
122
|
+
semanticEditReplayOutputHashes:scopedEdit?shared.semanticEditReplayOutputHashes:[],
|
|
123
|
+
semanticTransformContentHashes:shared.projectionIdentityHashes.length?shared.semanticTransformContentHashes:[],
|
|
124
|
+
semanticTransformIdentityHashes:shared.projectionIdentityHashes.length?shared.semanticTransformIdentityHashes:[]
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function hasSharedEditScope(shared){
|
|
129
|
+
return Boolean(shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length
|
|
130
|
+
||(shared.sourcePaths.length&&(shared.semanticEditKeys.length||shared.semanticIdentityHashes.length)));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function hasSharedSourceScope(shared){
|
|
134
|
+
return Boolean(shared.sourcePaths.length||shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length);
|
|
113
135
|
}
|
|
114
136
|
|
|
115
137
|
function overlapAdmission(shared,{leftIndex,rightIndex,options}){
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export function sourcePreservationFromProjectionContext(context) {
|
|
2
|
-
return context.
|
|
2
|
+
return context.sourcePreservation
|
|
3
|
+
?? context.metadata?.sourcePreservation
|
|
4
|
+
?? context.importResult?.metadata?.sourcePreservation
|
|
5
|
+
?? context.importResult?.nativeSource?.metadata?.sourcePreservation
|
|
6
|
+
?? context.importResult?.nativeAst?.metadata?.sourcePreservation
|
|
7
|
+
?? context.nativeSource?.metadata?.sourcePreservation
|
|
3
8
|
?? context.nativeAst?.metadata?.sourcePreservation
|
|
4
|
-
?? context.nativeSource?.ast?.metadata?.sourcePreservation
|
|
9
|
+
?? context.nativeSource?.ast?.metadata?.sourcePreservation
|
|
10
|
+
?? context.universalAst?.metadata?.sourcePreservation
|
|
11
|
+
?? context.importResult?.universalAst?.metadata?.sourcePreservation;
|
|
5
12
|
}
|
|
@@ -17,13 +17,21 @@ export const NativeImportLanguageProfiles = Object.freeze([
|
|
|
17
17
|
aliases: ['js', 'mjs', 'cjs', 'jsx'],
|
|
18
18
|
extensions: ['.js', '.mjs', '.cjs', '.jsx'],
|
|
19
19
|
parserAdapters: ['estree', 'babel', 'tree-sitter'],
|
|
20
|
-
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
|
|
20
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime'],
|
|
21
|
+
notes: [
|
|
22
|
+
'lightweight scanner records declarations only; exact parser adapters must be injected by the host',
|
|
23
|
+
'.jsx sources are classified as javascript for declaration scanning; JSX element trees remain opaque without host parser evidence'
|
|
24
|
+
]
|
|
21
25
|
}),
|
|
22
26
|
nativeImportLanguageProfile('typescript', {
|
|
23
27
|
aliases: ['ts', 'tsx'],
|
|
24
28
|
extensions: ['.ts', '.tsx'],
|
|
25
29
|
parserAdapters: ['typescript-compiler-api', 'babel', 'tree-sitter'],
|
|
26
|
-
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax']
|
|
30
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax'],
|
|
31
|
+
notes: [
|
|
32
|
+
'lightweight scanner records declarations only; exact parser adapters must be injected by the host',
|
|
33
|
+
'.tsx sources are classified as typescript for declaration scanning; JSX element trees remain opaque without host parser evidence'
|
|
34
|
+
]
|
|
27
35
|
}),
|
|
28
36
|
nativeImportLanguageProfile('python', {
|
|
29
37
|
aliases: ['py'],
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
splitParameters
|
|
5
5
|
} from './native-region-scanner-core.js';
|
|
6
6
|
import { jsImportDeclarations } from './native-region-scanner-js-imports.js';
|
|
7
|
-
|
|
8
7
|
function jsCommentOnlyLine(trimmed) {
|
|
9
8
|
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
|
|
10
9
|
}
|
|
@@ -128,7 +127,7 @@ function jsExportedContainerDeclaration(input, lineNumber, trimmed) {
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
|
|
131
|
-
const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer))\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
|
|
130
|
+
const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer)|Object\.freeze)\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
|
|
132
131
|
if (!match) return undefined;
|
|
133
132
|
const wrapper = match[1];
|
|
134
133
|
const argument = match[2].trim();
|
|
@@ -148,6 +147,13 @@ function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
|
|
|
148
147
|
parameters: splitParameters(functionMatch[1] ?? functionMatch[2])
|
|
149
148
|
}, true);
|
|
150
149
|
}
|
|
150
|
+
const classMatch = argument.match(/^(?:abstract\s+)?class\b(?:\s+(?!(?:extends|implements)\b)([A-Za-z_$][\w$]*))?/);
|
|
151
|
+
if (classMatch) {
|
|
152
|
+
return nativeDeclaration(input, lineNumber, 'ExportDefaultClassWrapperDeclaration', 'class', classMatch[1] ?? 'default', {
|
|
153
|
+
exportDefault: true,
|
|
154
|
+
wrapper
|
|
155
|
+
}, true);
|
|
156
|
+
}
|
|
151
157
|
const aliasMatch = argument.match(/^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*(?:[,)]|$)/);
|
|
152
158
|
if (!aliasMatch) return undefined;
|
|
153
159
|
return nativeDeclaration(input, lineNumber, 'ExportDefaultWrappedAlias', 'variable', 'default', {
|
|
@@ -44,6 +44,13 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
|
|
|
44
44
|
importedName: 'default',
|
|
45
45
|
importKind: trimmed.includes('import') ? 'dynamic-import-binding' : 'commonjs-require'
|
|
46
46
|
}]);
|
|
47
|
+
const sideEffectRequireMatch = trimmed.match(/^require\s*\(\s*(['"])([^'"]+)\1\s*\)\s*;?$/);
|
|
48
|
+
if (sideEffectRequireMatch) {
|
|
49
|
+
return jsImportModuleDeclarations(input, lineNumber, sideEffectRequireMatch[2], 'CommonJsSideEffectRequireDeclaration', [], {
|
|
50
|
+
sideEffectOnly: true,
|
|
51
|
+
importKind: 'commonjs-require'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
47
54
|
return [];
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -126,18 +126,18 @@ function scanJavaScriptLike(input) {
|
|
|
126
126
|
} else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
|
|
127
127
|
const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
|
|
128
128
|
pushDeclaration(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
|
|
129
|
-
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
|
|
129
|
+
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
|
|
130
130
|
pushDeclaration(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
|
|
131
131
|
methodName: match[1],
|
|
132
132
|
owner: currentClass,
|
|
133
133
|
parameters: splitParameters(match[2])
|
|
134
134
|
}, declarationLine.includes('{') || declarationLine.includes('=>')));
|
|
135
|
-
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
|
|
135
|
+
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*(#?[A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
|
|
136
136
|
pushDeclaration(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
|
|
137
137
|
propertyName: match[1],
|
|
138
138
|
owner: currentClass,
|
|
139
139
|
valueType: match[2]?.trim()
|
|
140
|
-
}, false));
|
|
140
|
+
}, false, { regionKind: 'property' }));
|
|
141
141
|
}
|
|
142
142
|
if (currentClass) {
|
|
143
143
|
classDepth += braceDelta(trimmed);
|
|
@@ -271,7 +271,7 @@ function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, cla
|
|
|
271
271
|
if (open < 0 || close <= open) return [];
|
|
272
272
|
const body = declarationLine.slice(open + 1, close);
|
|
273
273
|
const declarations = [];
|
|
274
|
-
for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
|
|
274
|
+
for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
|
|
275
275
|
if (jsControlKeyword(match[1])) continue;
|
|
276
276
|
declarations.push(nativeDeclaration(input, lineNumber, 'MethodDefinition', 'method', `${className}.${match[1]}`, {
|
|
277
277
|
methodName: match[1],
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
scanHaskell,
|
|
27
27
|
scanR
|
|
28
28
|
} from './native-region-scanner-functional.js';
|
|
29
|
+
import { normalizeNativeLanguageId } from './native-import-utils.js';
|
|
29
30
|
export { lightweightCoverageLosses } from './native-region-scanner-core.js';
|
|
30
31
|
export {
|
|
31
32
|
detectNewlineStyle,
|
|
@@ -34,7 +35,7 @@ export {
|
|
|
34
35
|
} from './native-source-preservation-scanner.js';
|
|
35
36
|
|
|
36
37
|
function scanNativeDeclarations(input) {
|
|
37
|
-
const language = String(input.language).toLowerCase();
|
|
38
|
+
const language = normalizeNativeLanguageId(input.language) || String(input.language).toLowerCase();
|
|
38
39
|
if (language === 'javascript' || language === 'typescript') return scanJavaScriptLike(input);
|
|
39
40
|
if (language === 'python') return scanPython(input);
|
|
40
41
|
if (language === 'rust') return scanRust(input);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { caseSensitiveIdFragment, idFragment, uniqueStrings } from './native-import-utils.js';
|
|
1
|
+
import { caseSensitiveIdFragment, idFragment, uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
|
|
2
2
|
|
|
3
3
|
const NativeImportRegionTaxonomyKinds = Object.freeze([
|
|
4
4
|
'symbol',
|
|
@@ -20,20 +20,20 @@ function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode,
|
|
|
20
20
|
const language = symbol.language ?? imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language;
|
|
21
21
|
const sourceSpan = mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span;
|
|
22
22
|
const regionKind = semanticRegionKindForSymbol(symbol, mapping, nativeNode);
|
|
23
|
-
const key = [
|
|
23
|
+
const key = symbol?.metadata?.ownershipRegionKey ?? [
|
|
24
24
|
options.regionPrefix ?? 'source',
|
|
25
25
|
sourcePath ?? `${language}:memory`,
|
|
26
26
|
regionKind,
|
|
27
27
|
symbol.name ?? symbol.id
|
|
28
28
|
].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
|
|
29
29
|
return {
|
|
30
|
-
id: `region_${caseSensitiveIdFragment(key)}`,
|
|
30
|
+
id: symbol?.metadata?.ownershipRegionId ?? `region_${caseSensitiveIdFragment(key)}`,
|
|
31
31
|
key,
|
|
32
32
|
regionKind,
|
|
33
33
|
granularity: 'symbol',
|
|
34
34
|
language,
|
|
35
35
|
sourcePath,
|
|
36
|
-
sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash,
|
|
36
|
+
sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? imported?.sourceHash,
|
|
37
37
|
symbolId: symbol.id,
|
|
38
38
|
symbolName: symbol.name,
|
|
39
39
|
symbolKind: symbol.kind,
|
|
@@ -76,17 +76,19 @@ function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function semanticPatchHintForRegion(region, readiness, options = {}) {
|
|
79
|
+
const supportedOperations = semanticRegionSupportedOperations(region);
|
|
79
80
|
return {
|
|
80
81
|
id: `hint_${idFragment(region.id)}`,
|
|
81
82
|
kind: 'source-region-patch',
|
|
82
83
|
ownershipRegionId: region.id,
|
|
83
84
|
ownershipKey: region.key,
|
|
85
|
+
operation: supportedOperations[0] ?? 'replace-region',
|
|
84
86
|
sourcePath: region.sourcePath,
|
|
85
87
|
sourceHash: region.sourceHash,
|
|
86
88
|
sourceSpan: region.sourceSpan,
|
|
87
89
|
readiness,
|
|
88
90
|
precision: region.precision,
|
|
89
|
-
supportedOperations
|
|
91
|
+
supportedOperations,
|
|
90
92
|
projection: {
|
|
91
93
|
sourceLanguage: region.language,
|
|
92
94
|
targetPath: options.targetPath ?? region.sourcePath,
|
|
@@ -170,9 +172,20 @@ function summarizeSemanticImportRegionTaxonomy(regions) {
|
|
|
170
172
|
};
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
function semanticOwnershipRegionsFromSemanticIndex(semanticIndex) {
|
|
176
|
+
const factRegions = (semanticIndex?.facts ?? [])
|
|
177
|
+
.filter((fact) => fact?.predicate === 'semanticOwnershipRegion' && fact.value && typeof fact.value === 'object')
|
|
178
|
+
.map((fact) => fact.value);
|
|
179
|
+
return uniqueRecordsById([
|
|
180
|
+
...(Array.isArray(semanticIndex?.ownershipRegions) ? semanticIndex.ownershipRegions : []),
|
|
181
|
+
...factRegions
|
|
182
|
+
]);
|
|
183
|
+
}
|
|
184
|
+
|
|
173
185
|
export {
|
|
174
186
|
NativeImportRegionTaxonomyKinds,
|
|
175
187
|
semanticOwnershipRegionForDeclaration,
|
|
188
|
+
semanticOwnershipRegionsFromSemanticIndex,
|
|
176
189
|
semanticOwnershipRegionForSymbol,
|
|
177
190
|
semanticPatchHintForRegion,
|
|
178
191
|
semanticRegionKindForSymbol,
|
|
@@ -8,6 +8,16 @@ export interface SemanticImportSidecarQualityWarning {
|
|
|
8
8
|
readonly sourcePaths: readonly string[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export type SemanticImportSidecarRecordClassification = 'useful' | 'expected-empty' | 'unexpectedly-empty' | 'missing' | string;
|
|
12
|
+
|
|
13
|
+
export interface SemanticImportSidecarQualityRecord {
|
|
14
|
+
readonly classification: SemanticImportSidecarRecordClassification;
|
|
15
|
+
readonly reasonCode: string;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
readonly action: string;
|
|
18
|
+
readonly sourcePaths: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
export interface SemanticImportSidecarProofAdmissionSummary {
|
|
12
22
|
readonly total: number;
|
|
13
23
|
readonly obligations: number;
|
|
@@ -26,12 +36,14 @@ export interface SemanticImportSidecarProofAdmissionSummary {
|
|
|
26
36
|
export interface SemanticImportSidecarQuality {
|
|
27
37
|
readonly schema: 'frontier.lang.semanticSidecarQuality.v1';
|
|
28
38
|
readonly expected: boolean;
|
|
39
|
+
readonly expectedEmpty: boolean;
|
|
29
40
|
readonly expectedSatisfied: boolean;
|
|
30
41
|
readonly expectedMissingReasonCodes: readonly string[];
|
|
31
42
|
readonly selected: boolean;
|
|
32
43
|
readonly eligible: boolean;
|
|
33
44
|
readonly imported: boolean;
|
|
34
45
|
readonly importCount: number;
|
|
46
|
+
readonly record: SemanticImportSidecarQualityRecord;
|
|
35
47
|
readonly symbolCount: number;
|
|
36
48
|
readonly ownershipRegionCount: number;
|
|
37
49
|
readonly patchHintCount: number;
|
|
@@ -45,6 +57,7 @@ export interface SemanticImportSidecarQuality {
|
|
|
45
57
|
export interface SemanticImportSidecarAdmission {
|
|
46
58
|
readonly schema: 'frontier.lang.semanticSidecarAdmission.v1';
|
|
47
59
|
readonly expected: boolean;
|
|
60
|
+
readonly expectedEmpty: boolean;
|
|
48
61
|
readonly expectedSatisfied: boolean;
|
|
49
62
|
readonly expectedMissingReasonCodes: readonly string[];
|
|
50
63
|
readonly selected: boolean;
|
|
@@ -53,6 +66,7 @@ export interface SemanticImportSidecarAdmission {
|
|
|
53
66
|
readonly importCount: number;
|
|
54
67
|
readonly readiness: SemanticMergeReadiness;
|
|
55
68
|
readonly action: string;
|
|
69
|
+
readonly record: SemanticImportSidecarQualityRecord;
|
|
56
70
|
readonly counts: {
|
|
57
71
|
readonly symbols: number;
|
|
58
72
|
readonly ownershipRegions: number;
|