@shapeshift-labs/frontier-lang-compiler 0.2.68 → 0.2.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/dist/declarations/semantic-impact.d.ts +12 -0
- package/dist/internal/index-impl/compileNativeSource.js +13 -1
- package/dist/internal/index-impl/createUniversalAstFromDocument.js +4 -0
- package/dist/internal/index-impl/nativeProjectionReview.js +73 -0
- package/dist/internal/index-impl/projectNativeImportToSource.js +10 -1
- package/dist/native-region-scanner-js-imports.js +27 -1
- package/dist/native-region-scanner-js.js +63 -3
- package/dist/semantic-import-impact-types.d.ts +21 -0
- package/dist/semantic-import-impact.js +5 -2
- package/dist/semantic-import-merge-signal.js +86 -0
- package/package.json +1 -1
|
@@ -3,12 +3,21 @@ import type { NativeImportRegionTaxonomyKind } from './native-import-losses.js';
|
|
|
3
3
|
|
|
4
4
|
export type SemanticImportImpactRisk = 'low' | 'medium' | 'high' | string;
|
|
5
5
|
export type SemanticImportImpactConfidence = 'source-exact' | 'source-addressed' | 'estimated-source-region' | 'review-required' | string;
|
|
6
|
+
export type SemanticImportMergeSignalStatus = 'strong' | 'partial' | 'weak' | 'blocked' | string;
|
|
6
7
|
|
|
7
8
|
export interface SemanticImportImpactVerificationStep {
|
|
8
9
|
readonly kind: 'dependency-review' | 'source-map-review' | 'reject-or-reprove' | 'proof-review' | 'patch-admission' | 'evidence-review' | string;
|
|
9
10
|
readonly reason: string;
|
|
10
11
|
readonly required: boolean;
|
|
11
12
|
}
|
|
13
|
+
export interface SemanticImportMergeSignal {
|
|
14
|
+
readonly status: SemanticImportMergeSignalStatus;
|
|
15
|
+
readonly score: number;
|
|
16
|
+
readonly missing: readonly string[];
|
|
17
|
+
readonly reviewRequired: boolean;
|
|
18
|
+
readonly queryKeys: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
export interface SemanticImportMergeSignalScoreSummary { readonly min: number; readonly max: number; readonly average: number; }
|
|
12
21
|
|
|
13
22
|
export interface SemanticImportImpactRecord {
|
|
14
23
|
readonly id: string;
|
|
@@ -39,6 +48,7 @@ export interface SemanticImportImpactRecord {
|
|
|
39
48
|
readonly readiness: SemanticMergeReadiness;
|
|
40
49
|
readonly risk: SemanticImportImpactRisk;
|
|
41
50
|
readonly confidence: SemanticImportImpactConfidence;
|
|
51
|
+
readonly mergeSignal: SemanticImportMergeSignal;
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
export interface SemanticImportImpactSummary {
|
|
@@ -51,5 +61,7 @@ export interface SemanticImportImpactSummary {
|
|
|
51
61
|
readonly sourceMapMappings: number; readonly sourcePreservationRecords: number; readonly patchHints: number;
|
|
52
62
|
readonly proofObligations: number; readonly openProofObligations: number; readonly failedProofObligations: number; readonly paradigmSemantics: number; readonly loweringRecords: number;
|
|
53
63
|
readonly evidenceIds: number; readonly verificationPlans: readonly string[]; readonly requiredVerificationSteps: number;
|
|
64
|
+
readonly byMergeSignal: Readonly<Record<string, number>>; readonly weakMergeSignals: number; readonly reviewRequiredMergeSignals: number;
|
|
65
|
+
readonly mergeSignalQueryKeys: readonly string[]; readonly mergeSignalScores: SemanticImportMergeSignalScoreSummary;
|
|
54
66
|
};
|
|
55
67
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{nativeLanguageCompileTarget}from'../../coverage-matrix-profiles.js';import{idFragment,uniqueByEvidenceId,uniqueByLossId}from'../../native-import-utils.js';
|
|
2
|
-
import{classifyNativeImportReadiness}from'./classifyNativeImportReadiness.js';import{createNativeRoundtripEvidence}from'./createNativeRoundtripEvidence.js';import{createProjectionTargetLossMatrix}from'./createProjectionTargetLossMatrix.js';import{importNativeSource}from'./importNativeSource.js';import{isNativeSourceImportResult}from'./isNativeSourceImportResult.js';import{nativeCompileSourceLanguage}from'./nativeCompileSourceLanguage.js';import{nativeCompileTarget}from'./nativeCompileTarget.js';import{nativeSourceCompileEvidence}from'./nativeSourceCompileEvidence.js';import{nativeSourceCompileSourceMaps}from'./nativeSourceCompileSourceMaps.js';import{nativeSourceCompileTargetCoverage}from'./nativeSourceCompileTargetCoverage.js';import{nativeSourceCompileTargetLosses}from'./nativeSourceCompileTargetLosses.js';import{projectNativeImportToSource}from'./projectNativeImportToSource.js';import{resolveNativeTargetProjectionAdapter}from'./resolveNativeTargetProjectionAdapter.js';import{runNativeTargetProjectionAdapter}from'./runNativeTargetProjectionAdapter.js';import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';
|
|
2
|
+
import{classifyNativeImportReadiness}from'./classifyNativeImportReadiness.js';import{createNativeRoundtripEvidence}from'./createNativeRoundtripEvidence.js';import{createProjectionTargetLossMatrix}from'./createProjectionTargetLossMatrix.js';import{importNativeSource}from'./importNativeSource.js';import{isNativeSourceImportResult}from'./isNativeSourceImportResult.js';import{nativeCompileSourceLanguage}from'./nativeCompileSourceLanguage.js';import{nativeCompileTarget}from'./nativeCompileTarget.js';import{nativeProjectionReview}from'./nativeProjectionReview.js';import{nativeSourceCompileEvidence}from'./nativeSourceCompileEvidence.js';import{nativeSourceCompileSourceMaps}from'./nativeSourceCompileSourceMaps.js';import{nativeSourceCompileTargetCoverage}from'./nativeSourceCompileTargetCoverage.js';import{nativeSourceCompileTargetLosses}from'./nativeSourceCompileTargetLosses.js';import{projectNativeImportToSource}from'./projectNativeImportToSource.js';import{resolveNativeTargetProjectionAdapter}from'./resolveNativeTargetProjectionAdapter.js';import{runNativeTargetProjectionAdapter}from'./runNativeTargetProjectionAdapter.js';import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';
|
|
3
3
|
export function compileNativeSource(input, options = {}) {
|
|
4
4
|
const importResult = isNativeSourceImportResult(input) ? input : importNativeSource(input);
|
|
5
5
|
const sourceLanguage = nativeCompileSourceLanguage(importResult, input);
|
|
@@ -124,6 +124,17 @@ export function compileNativeSource(input, options = {}) {
|
|
|
124
124
|
compileResultId: id
|
|
125
125
|
});
|
|
126
126
|
const sourceMap = sourceMaps[0];
|
|
127
|
+
const projectionReview = nativeProjectionReview({
|
|
128
|
+
mode: projection.mode, outputMode, language: projection.language, sourceLanguage, target,
|
|
129
|
+
sourcePath: importResult.sourcePath ?? importResult.nativeSource?.sourcePath,
|
|
130
|
+
exactSourceAvailable: projection.metadata?.exactSourceAvailable === true,
|
|
131
|
+
sourceTextAvailable: projection.metadata?.sourceTextAvailable === true,
|
|
132
|
+
sourceHashVerified: projection.metadata?.sourceHashVerified === true,
|
|
133
|
+
declarationCount: projection.declarations.length, sourceMapCount: sourceMaps.length,
|
|
134
|
+
losses, targetLosses, readiness: readiness.readiness, targetCoverage, targetProjection
|
|
135
|
+
});
|
|
136
|
+
compileEvidence.metadata.projectionReview = projectionReview;
|
|
137
|
+
for (const record of sourceMaps) record.metadata = { ...(record.metadata ?? {}), projectionReview };
|
|
127
138
|
const roundtripEvidence = createNativeRoundtripEvidence(importResult, {
|
|
128
139
|
id: `evidence_${idPart}_${idFragment(target)}_native_roundtrip`,
|
|
129
140
|
projection,
|
|
@@ -179,6 +190,7 @@ export function compileNativeSource(input, options = {}) {
|
|
|
179
190
|
targetLossClass: targetCoverage.lossClass,
|
|
180
191
|
targetReadiness: targetCoverage.readiness,
|
|
181
192
|
targetSupported: targetCoverage.supported,
|
|
193
|
+
projectionReview,
|
|
182
194
|
...options.metadata,
|
|
183
195
|
roundtripEvidence: roundtripEvidence.metadata.roundtripEvidence
|
|
184
196
|
}
|
|
@@ -9,6 +9,10 @@ export function createUniversalAstFromDocument(document, input = {}) {
|
|
|
9
9
|
losses: input.losses,
|
|
10
10
|
evidence: input.evidence ?? [],
|
|
11
11
|
mergeCandidates: input.mergeCandidates,
|
|
12
|
+
semanticOperations: input.semanticOperations,
|
|
13
|
+
proof: input.proof ?? input.universalAstProof,
|
|
14
|
+
paradigmSemantics: input.paradigmSemantics ?? input.universalAstParadigmSemantics,
|
|
15
|
+
replayLinks: input.replayLinks,
|
|
12
16
|
layers: input.layers,
|
|
13
17
|
metadata: input.metadata
|
|
14
18
|
}), input);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import{uniqueStrings}from'../../native-import-utils.js';
|
|
2
|
+
|
|
3
|
+
export function nativeProjectionReview(input) {
|
|
4
|
+
const lossIds = (input.losses ?? []).map((loss) => loss.id).filter(Boolean);
|
|
5
|
+
const blockingLossIds = (input.losses ?? []).filter((loss) => loss.severity === 'error').map((loss) => loss.id).filter(Boolean);
|
|
6
|
+
const targetLossIds = (input.targetLosses ?? []).map((loss) => loss.id).filter(Boolean);
|
|
7
|
+
const fallbackReasons = nativeProjectionFallbackReasons(input, blockingLossIds, targetLossIds);
|
|
8
|
+
const reviewRequired = input.readiness !== 'ready' || fallbackReasons.length > 0 || blockingLossIds.length > 0;
|
|
9
|
+
const status = nativeProjectionReviewStatus(input, blockingLossIds);
|
|
10
|
+
return {
|
|
11
|
+
kind: 'frontier.lang.nativeProjectionReview',
|
|
12
|
+
version: 1,
|
|
13
|
+
status,
|
|
14
|
+
reviewRequired,
|
|
15
|
+
projectionMode: input.mode,
|
|
16
|
+
outputMode: input.outputMode ?? input.mode,
|
|
17
|
+
language: input.language,
|
|
18
|
+
sourceLanguage: input.sourceLanguage ?? input.language,
|
|
19
|
+
target: input.target,
|
|
20
|
+
sourcePath: input.sourcePath,
|
|
21
|
+
exactSourceAvailable: input.exactSourceAvailable === true,
|
|
22
|
+
sourceTextAvailable: input.sourceTextAvailable === true,
|
|
23
|
+
sourceHashVerified: input.sourceHashVerified === true,
|
|
24
|
+
declarationCount: input.declarationCount ?? 0,
|
|
25
|
+
sourceMapCount: input.sourceMapCount ?? 0,
|
|
26
|
+
readiness: input.readiness,
|
|
27
|
+
targetLossClass: input.targetCoverage?.lossClass,
|
|
28
|
+
targetReadiness: input.targetCoverage?.readiness,
|
|
29
|
+
targetSupported: input.targetCoverage?.supported,
|
|
30
|
+
targetProjectionAdapterId: input.targetProjection?.adapter?.id,
|
|
31
|
+
fallbackReasons,
|
|
32
|
+
lossIds: uniqueStrings(lossIds),
|
|
33
|
+
blockingLossIds: uniqueStrings(blockingLossIds),
|
|
34
|
+
targetLossIds: uniqueStrings(targetLossIds),
|
|
35
|
+
queryKeys: nativeProjectionReviewQueryKeys(input, status, fallbackReasons, reviewRequired)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function nativeProjectionFallbackReasons(input, blockingLossIds, targetLossIds) {
|
|
40
|
+
const reasons = [];
|
|
41
|
+
if (input.exactSourceAvailable !== true) reasons.push('no-exact-source');
|
|
42
|
+
if (input.sourceHashVerified !== true) reasons.push('source-hash-unverified');
|
|
43
|
+
if (input.mode === 'native-source-stubs') reasons.push('declaration-stubs');
|
|
44
|
+
if (input.outputMode === 'target-stubs') reasons.push('target-stubs');
|
|
45
|
+
if (input.targetCoverage?.supported === false) reasons.push('target-unsupported');
|
|
46
|
+
if (input.targetCoverage?.lossClass === 'missingAdapter') reasons.push('missing-target-adapter');
|
|
47
|
+
if (blockingLossIds.length > 0) reasons.push('blocking-losses');
|
|
48
|
+
if (targetLossIds.length > 0) reasons.push('target-losses');
|
|
49
|
+
return uniqueStrings(reasons);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function nativeProjectionReviewStatus(input, blockingLossIds) {
|
|
53
|
+
if (blockingLossIds.length > 0 || input.readiness === 'blocked' || input.targetCoverage?.readiness === 'blocked') return 'blocked';
|
|
54
|
+
if (input.outputMode === 'target-adapter') return 'target-adapter';
|
|
55
|
+
if (input.outputMode === 'target-stubs' || input.mode === 'native-source-stubs') return 'stub-only';
|
|
56
|
+
if (input.sourceHashVerified === true && input.exactSourceAvailable === true) return 'preserved-source';
|
|
57
|
+
return 'needs-review';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function nativeProjectionReviewQueryKeys(input, status, fallbackReasons, reviewRequired) {
|
|
61
|
+
return uniqueStrings([
|
|
62
|
+
`projection-review:${status}`,
|
|
63
|
+
reviewRequired ? 'review-required' : 'review-not-required',
|
|
64
|
+
input.language ? `language:${input.language}` : undefined,
|
|
65
|
+
input.sourceLanguage ? `source-language:${input.sourceLanguage}` : undefined,
|
|
66
|
+
input.target ? `target:${input.target}` : undefined,
|
|
67
|
+
input.sourcePath ? `source:${input.sourcePath}` : undefined,
|
|
68
|
+
input.mode ? `projection-mode:${input.mode}` : undefined,
|
|
69
|
+
input.outputMode ? `output-mode:${input.outputMode}` : undefined,
|
|
70
|
+
input.targetCoverage?.lossClass ? `target-loss-class:${input.targetCoverage.lossClass}` : undefined,
|
|
71
|
+
...fallbackReasons.map((reason) => `fallback:${reason}`)
|
|
72
|
+
].filter(Boolean));
|
|
73
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
-
import{classifyNativeImportReadiness}from'./classifyNativeImportReadiness.js';import{createNativeRoundtripEvidence}from'./createNativeRoundtripEvidence.js';import{nativeImportProjectionContext}from'./nativeImportProjectionContext.js';import{nativeProjectionDeclarations}from'./nativeProjectionDeclarations.js';import{nativeProjectionSourceCandidate}from'./nativeProjectionSourceCandidate.js';import{nativeProjectionStubLosses}from'./nativeProjectionStubLosses.js';import{renderNativeProjectionStubs}from'./renderNativeProjectionStubs.js';import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';
|
|
2
|
+
import{classifyNativeImportReadiness}from'./classifyNativeImportReadiness.js';import{createNativeRoundtripEvidence}from'./createNativeRoundtripEvidence.js';import{nativeImportProjectionContext}from'./nativeImportProjectionContext.js';import{nativeProjectionDeclarations}from'./nativeProjectionDeclarations.js';import{nativeProjectionReview}from'./nativeProjectionReview.js';import{nativeProjectionSourceCandidate}from'./nativeProjectionSourceCandidate.js';import{nativeProjectionStubLosses}from'./nativeProjectionStubLosses.js';import{renderNativeProjectionStubs}from'./renderNativeProjectionStubs.js';import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';
|
|
3
3
|
export function projectNativeImportToSource(importResult, options = {}) {
|
|
4
4
|
if (!importResult || typeof importResult !== 'object') {
|
|
5
5
|
throw new Error('projectNativeImportToSource requires a native import result');
|
|
@@ -49,6 +49,14 @@ export function projectNativeImportToSource(importResult, options = {}) {
|
|
|
49
49
|
parser: context.parser,
|
|
50
50
|
semanticStatus: context.semanticStatus
|
|
51
51
|
});
|
|
52
|
+
const projectionReview = nativeProjectionReview({
|
|
53
|
+
mode, language: context.language, sourcePath: context.sourcePath,
|
|
54
|
+
exactSourceAvailable: candidateSource?.exact === true,
|
|
55
|
+
sourceTextAvailable: typeof candidateSource?.sourceText === 'string',
|
|
56
|
+
sourceHashVerified: candidateSource?.hashVerified ?? false,
|
|
57
|
+
declarationCount: declarations.length, losses, readiness: readiness.readiness
|
|
58
|
+
});
|
|
59
|
+
evidence[0].metadata.projectionReview = projectionReview;
|
|
52
60
|
const result = {
|
|
53
61
|
kind: 'frontier.lang.nativeSourceProjection',
|
|
54
62
|
version: 1,
|
|
@@ -75,6 +83,7 @@ export function projectNativeImportToSource(importResult, options = {}) {
|
|
|
75
83
|
sourcePreservationId: candidateSource?.sourcePreservationId,
|
|
76
84
|
sourceHashVerified: candidateSource?.hashVerified ?? false,
|
|
77
85
|
nativeImportLossSummary,
|
|
86
|
+
projectionReview,
|
|
78
87
|
...options.metadata
|
|
79
88
|
}
|
|
80
89
|
};
|
|
@@ -30,6 +30,14 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
|
|
|
30
30
|
: [];
|
|
31
31
|
return jsImportModuleDeclarations(input, lineNumber, importPath, 'ExportFromDeclaration', bindings, { typeOnly, reexport: true, exportStar: Boolean(exportMatch[2]) });
|
|
32
32
|
}
|
|
33
|
+
const destructuredRequireMatch = trimmed.match(/^(?:const|let|var)\s+\{([^}]+)\}\s*=\s*(?:await\s+)?(require|import)\s*\(\s*(['"])([^'"]+)\3\s*\)/);
|
|
34
|
+
if (destructuredRequireMatch) {
|
|
35
|
+
const importKind = destructuredRequireMatch[2] === 'import' ? 'dynamic-import-binding' : 'commonjs-require';
|
|
36
|
+
const bindings = jsDestructuredImportBindings(destructuredRequireMatch[1], importKind);
|
|
37
|
+
if (bindings.length) {
|
|
38
|
+
return jsImportModuleDeclarations(input, lineNumber, destructuredRequireMatch[4], 'DestructuredRequireDeclaration', bindings, { destructured: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
33
41
|
const requireMatch = trimmed.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:await\s+)?(?:require|import)\s*\(\s*(['"])([^'"]+)\2\s*\)/);
|
|
34
42
|
if (requireMatch) return jsImportModuleDeclarations(input, lineNumber, requireMatch[3], 'CommonJsRequireDeclaration', [{
|
|
35
43
|
localName: requireMatch[1],
|
|
@@ -39,6 +47,23 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
|
|
|
39
47
|
return [];
|
|
40
48
|
}
|
|
41
49
|
|
|
50
|
+
function jsDestructuredImportBindings(raw, importKind) {
|
|
51
|
+
return String(raw ?? '')
|
|
52
|
+
.split(',')
|
|
53
|
+
.map((part) => {
|
|
54
|
+
const text = part.trim();
|
|
55
|
+
if (!text || text.startsWith('...')) return undefined;
|
|
56
|
+
const match = text.match(/^([A-Za-z_$][\w$]*|\*)\s*(?::\s*([A-Za-z_$][\w$]*))?$/);
|
|
57
|
+
if (!match) return undefined;
|
|
58
|
+
return {
|
|
59
|
+
localName: match[2] ?? match[1],
|
|
60
|
+
importedName: match[1],
|
|
61
|
+
importKind
|
|
62
|
+
};
|
|
63
|
+
})
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
|
|
42
67
|
function jsImportModuleDeclarations(input, lineNumber, importPath, languageKind, bindings = [], metadata = {}) {
|
|
43
68
|
const bindingSummaries = bindings.map((binding) => ({
|
|
44
69
|
localName: binding.localName,
|
|
@@ -55,7 +80,8 @@ function jsImportModuleDeclarations(input, lineNumber, importPath, languageKind,
|
|
|
55
80
|
...(metadata.typeOnly ? { typeOnly: true } : {}),
|
|
56
81
|
...(metadata.sideEffectOnly ? { sideEffectOnly: true } : {}),
|
|
57
82
|
...(metadata.reexport ? { reexport: true } : {}),
|
|
58
|
-
...(metadata.exportStar ? { exportStar: true } : {})
|
|
83
|
+
...(metadata.exportStar ? { exportStar: true } : {}),
|
|
84
|
+
...(metadata.destructured ? { destructured: true } : {})
|
|
59
85
|
},
|
|
60
86
|
metadata: {
|
|
61
87
|
moduleOnly: true,
|
|
@@ -33,6 +33,7 @@ function scanJavaScriptLike(input) {
|
|
|
33
33
|
let currentClass;
|
|
34
34
|
let classDepth = 0;
|
|
35
35
|
let currentObject;
|
|
36
|
+
let currentType;
|
|
36
37
|
const lexicalState = { inBlockComment: false, inTemplateString: false };
|
|
37
38
|
for (const { line, number } of lines) {
|
|
38
39
|
const scanLine = jsDeclarationScanLine(line, lexicalState);
|
|
@@ -40,6 +41,9 @@ function scanJavaScriptLike(input) {
|
|
|
40
41
|
if (!trimmed || jsCommentOnlyLine(trimmed)) continue;
|
|
41
42
|
const declarationLine = trimmed.replace(/^(?:export\s+)?(?:declare\s+)?/, '');
|
|
42
43
|
let match;
|
|
44
|
+
if (currentType && number !== currentType.startLine) {
|
|
45
|
+
pushDeclaration(jsTypeMemberDeclaration(input, number, declarationLine, currentType));
|
|
46
|
+
}
|
|
43
47
|
if (currentObject) {
|
|
44
48
|
const routeRecord = jsRouteRecordDeclaration(input, number, trimmed, currentObject);
|
|
45
49
|
if (routeRecord) {
|
|
@@ -66,13 +70,18 @@ function scanJavaScriptLike(input) {
|
|
|
66
70
|
classDepth = 0;
|
|
67
71
|
}
|
|
68
72
|
} else if ((match = declarationLine.match(/^interface\s+([A-Za-z_$][\w$]*)/))) {
|
|
69
|
-
|
|
73
|
+
const regionKind = 'type';
|
|
74
|
+
pushDeclaration(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, declarationLine.includes('{'), { regionKind }));
|
|
75
|
+
currentType = jsTypeRegionContext(match[1], declarationLine, number, regionKind, 'interface');
|
|
70
76
|
} else if ((match = declarationLine.match(/^(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)/))) {
|
|
71
77
|
pushDeclaration(nativeDeclaration(input, number, 'EnumDeclaration', 'type', match[1], {}, declarationLine.includes('{')));
|
|
72
78
|
} else if ((match = declarationLine.match(/^(?:namespace|module)\s+([A-Za-z_$][\w$.]*)/))) {
|
|
73
79
|
pushDeclaration(nativeDeclaration(input, number, 'ModuleDeclaration', 'module', match[1], {}, declarationLine.includes('{')));
|
|
74
|
-
} else if ((match = declarationLine.match(/^type\s+([A-Za-z_$][\w$]*)
|
|
75
|
-
|
|
80
|
+
} else if ((match = declarationLine.match(/^type\s+([A-Za-z_$][\w$]*)(?:\s*<[^=]+>)?\s*=/))) {
|
|
81
|
+
const regionKind = 'type';
|
|
82
|
+
const hasTypeBody = declarationLine.includes('{');
|
|
83
|
+
pushDeclaration(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, hasTypeBody, { regionKind }));
|
|
84
|
+
currentType = jsTypeRegionContext(match[1], declarationLine, number, regionKind, 'type');
|
|
76
85
|
} else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*[^=]+)?=\s*(?:async\s*)?(?:<[^=]+>\s*)?(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*(?::\s*[^=]+)?=>/))) {
|
|
77
86
|
pushDeclaration(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, true));
|
|
78
87
|
} else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/))) {
|
|
@@ -113,6 +122,10 @@ function scanJavaScriptLike(input) {
|
|
|
113
122
|
classDepth = 0;
|
|
114
123
|
}
|
|
115
124
|
}
|
|
125
|
+
if (currentType) {
|
|
126
|
+
if (number !== currentType.startLine) currentType.depth += jsStructureDelta(trimmed).value;
|
|
127
|
+
if (currentType.depth <= 0) currentType = undefined;
|
|
128
|
+
}
|
|
116
129
|
if (currentObject) {
|
|
117
130
|
if (number !== currentObject.startLine) currentObject.depth += jsContainerDelta(trimmed);
|
|
118
131
|
if (currentObject.depth <= 0) currentObject = undefined;
|
|
@@ -179,6 +192,53 @@ function jsStructureDelta(source) {
|
|
|
179
192
|
return { value, opened };
|
|
180
193
|
}
|
|
181
194
|
|
|
195
|
+
function jsTypeRegionContext(name, declarationLine, lineNumber, regionKind, typeKind) {
|
|
196
|
+
const depth = jsStructureDelta(declarationLine).value;
|
|
197
|
+
if (depth <= 0) return undefined;
|
|
198
|
+
return {
|
|
199
|
+
name,
|
|
200
|
+
typeKind,
|
|
201
|
+
regionKind,
|
|
202
|
+
depth,
|
|
203
|
+
startLine: lineNumber
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function jsTypeMemberDeclaration(input, lineNumber, declarationLine, context) {
|
|
208
|
+
const text = String(declarationLine ?? '').trim();
|
|
209
|
+
if (!text || /^[}\])]/.test(text) || text.startsWith('//')) return undefined;
|
|
210
|
+
let match = text.match(/^(?:readonly\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*([^;,]+))?[;,]?$/);
|
|
211
|
+
if (match && !jsControlKeyword(match[2])) {
|
|
212
|
+
return nativeDeclaration(input, lineNumber, 'TypeMethodSignature', 'method', `${context.name}.${match[2]}`, {
|
|
213
|
+
owner: context.name,
|
|
214
|
+
propertyName: match[2],
|
|
215
|
+
parameters: splitParameters(match[3]),
|
|
216
|
+
returnType: match[4]?.trim(),
|
|
217
|
+
typeKind: context.typeKind
|
|
218
|
+
}, false, {
|
|
219
|
+
regionKind: jsTypeMemberRegionKind(context, match[2], text),
|
|
220
|
+
metadata: { owner: context.name, propertyName: match[2], typeKind: context.typeKind }
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
match = text.match(/^(?:readonly\s+)?(['"]?)([A-Za-z_$][\w$-]*)\1\??\s*:\s*(.+?)[;,]?$/);
|
|
224
|
+
if (!match || jsControlKeyword(match[2])) return undefined;
|
|
225
|
+
const valueType = match[3].trim();
|
|
226
|
+
const functionLike = /=>/.test(valueType) || /^\([^)]*\)\s*=>/.test(valueType);
|
|
227
|
+
return nativeDeclaration(input, lineNumber, functionLike ? 'TypeFunctionPropertySignature' : 'TypePropertySignature', functionLike ? 'function' : 'property', `${context.name}.${match[2]}`, {
|
|
228
|
+
owner: context.name,
|
|
229
|
+
propertyName: match[2],
|
|
230
|
+
valueType,
|
|
231
|
+
typeKind: context.typeKind
|
|
232
|
+
}, false, {
|
|
233
|
+
regionKind: jsTypeMemberRegionKind(context, match[2], text),
|
|
234
|
+
metadata: { owner: context.name, propertyName: match[2], typeKind: context.typeKind }
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function jsTypeMemberRegionKind(context, propertyName, source) {
|
|
239
|
+
return jsRegionKindForDeclarationName(propertyName, source) ?? (context.regionKind === 'type' ? 'property' : context.regionKind) ?? 'property';
|
|
240
|
+
}
|
|
241
|
+
|
|
182
242
|
function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, className) {
|
|
183
243
|
const open = declarationLine.indexOf('{');
|
|
184
244
|
const close = declarationLine.lastIndexOf('}');
|
|
@@ -3,6 +3,7 @@ import type { NativeImportRegionTaxonomyKind } from './index.js';
|
|
|
3
3
|
|
|
4
4
|
export type SemanticImportImpactRisk = 'low' | 'medium' | 'high' | string;
|
|
5
5
|
export type SemanticImportImpactConfidence = 'source-exact' | 'source-addressed' | 'estimated-source-region' | 'review-required' | string;
|
|
6
|
+
export type SemanticImportMergeSignalStatus = 'strong' | 'partial' | 'weak' | 'blocked' | string;
|
|
6
7
|
|
|
7
8
|
export interface SemanticImportImpactVerificationStep {
|
|
8
9
|
readonly kind: 'dependency-review' | 'source-map-review' | 'reject-or-reprove' | 'proof-review' | 'patch-admission' | 'evidence-review' | string;
|
|
@@ -10,6 +11,20 @@ export interface SemanticImportImpactVerificationStep {
|
|
|
10
11
|
readonly required: boolean;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
export interface SemanticImportMergeSignal {
|
|
15
|
+
readonly status: SemanticImportMergeSignalStatus;
|
|
16
|
+
readonly score: number;
|
|
17
|
+
readonly missing: readonly string[];
|
|
18
|
+
readonly reviewRequired: boolean;
|
|
19
|
+
readonly queryKeys: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SemanticImportMergeSignalScoreSummary {
|
|
23
|
+
readonly min: number;
|
|
24
|
+
readonly max: number;
|
|
25
|
+
readonly average: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
export interface SemanticImportImpactRecord {
|
|
14
29
|
readonly id: string;
|
|
15
30
|
readonly kind: 'ownership-region-impact' | string;
|
|
@@ -39,6 +54,7 @@ export interface SemanticImportImpactRecord {
|
|
|
39
54
|
readonly readiness: SemanticMergeReadiness;
|
|
40
55
|
readonly risk: SemanticImportImpactRisk;
|
|
41
56
|
readonly confidence: SemanticImportImpactConfidence;
|
|
57
|
+
readonly mergeSignal: SemanticImportMergeSignal;
|
|
42
58
|
}
|
|
43
59
|
|
|
44
60
|
export interface SemanticImportImpactSummary {
|
|
@@ -63,5 +79,10 @@ export interface SemanticImportImpactSummary {
|
|
|
63
79
|
readonly evidenceIds: number;
|
|
64
80
|
readonly verificationPlans: readonly string[];
|
|
65
81
|
readonly requiredVerificationSteps: number;
|
|
82
|
+
readonly byMergeSignal: Readonly<Record<string, number>>;
|
|
83
|
+
readonly weakMergeSignals: number;
|
|
84
|
+
readonly reviewRequiredMergeSignals: number;
|
|
85
|
+
readonly mergeSignalQueryKeys: readonly string[];
|
|
86
|
+
readonly mergeSignalScores: SemanticImportMergeSignalScoreSummary;
|
|
66
87
|
};
|
|
67
88
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { countBy, idFragment, maxSemanticMergeReadiness, uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
|
|
2
2
|
import { semanticDependencyPredicateKey } from './semantic-import-dependencies.js';
|
|
3
|
+
import { semanticImpactMergeSignal, summarizeSemanticImpactMergeSignals } from './semantic-import-merge-signal.js';
|
|
3
4
|
|
|
4
5
|
function createSemanticImportImpact(input) {
|
|
5
6
|
const relations = collectDependencyRelations(input.imports, input.dependencies);
|
|
@@ -60,7 +61,7 @@ function semanticImpactRecord(input) {
|
|
|
60
61
|
relationCount: input.relations.length,
|
|
61
62
|
verificationPlan
|
|
62
63
|
});
|
|
63
|
-
|
|
64
|
+
const record = {
|
|
64
65
|
id: `impact_${idFragment(input.region.id ?? input.region.key)}`,
|
|
65
66
|
kind: 'ownership-region-impact',
|
|
66
67
|
ownershipRegionId: input.region.id,
|
|
@@ -93,6 +94,7 @@ function semanticImpactRecord(input) {
|
|
|
93
94
|
risk,
|
|
94
95
|
confidence: semanticImpactConfidence(input.region, input.sourceMapMappings, input.sourcePreservationRecords)
|
|
95
96
|
};
|
|
97
|
+
return { ...record, mergeSignal: semanticImpactMergeSignal(record) };
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
function collectDependencyRelations(imports, dependencies) {
|
|
@@ -299,7 +301,8 @@ function summarizeSemanticImpactRecords(records) {
|
|
|
299
301
|
loweringRecords: uniqueStrings(records.flatMap((record) => record.loweringRecordIds)).length,
|
|
300
302
|
evidenceIds: uniqueStrings(records.flatMap((record) => record.evidenceIds)).length,
|
|
301
303
|
verificationPlans: uniqueStrings(verificationPlans),
|
|
302
|
-
requiredVerificationSteps: records.flatMap((record) => record.verificationPlan).filter((step) => step.required).length
|
|
304
|
+
requiredVerificationSteps: records.flatMap((record) => record.verificationPlan).filter((step) => step.required).length,
|
|
305
|
+
...summarizeSemanticImpactMergeSignals(records)
|
|
303
306
|
};
|
|
304
307
|
}
|
|
305
308
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { countBy, uniqueStrings } from './native-import-utils.js';
|
|
2
|
+
|
|
3
|
+
export function semanticImpactMergeSignal(record) {
|
|
4
|
+
const missing = semanticImpactMissingSignals(record);
|
|
5
|
+
const requiredSteps = (record.verificationPlan ?? []).filter((step) => step.required).length;
|
|
6
|
+
const blocked = record.readiness === 'blocked' || (record.failedProofObligationIds ?? []).length > 0;
|
|
7
|
+
const reviewRequired = blocked || record.readiness !== 'ready' || record.risk !== 'low' || requiredSteps > 0 || missing.length > 0;
|
|
8
|
+
const score = semanticImpactMergeScore(record, missing, requiredSteps, blocked);
|
|
9
|
+
const status = semanticImpactMergeStatus(record, score, blocked);
|
|
10
|
+
return {
|
|
11
|
+
status,
|
|
12
|
+
score,
|
|
13
|
+
missing,
|
|
14
|
+
reviewRequired,
|
|
15
|
+
queryKeys: semanticImpactMergeQueryKeys(record, status, missing, reviewRequired)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function summarizeSemanticImpactMergeSignals(records) {
|
|
20
|
+
const signals = (records ?? []).map((record) => record.mergeSignal).filter(Boolean);
|
|
21
|
+
return {
|
|
22
|
+
byMergeSignal: countBy(signals.map((signal) => signal.status)),
|
|
23
|
+
weakMergeSignals: signals.filter((signal) => signal.status === 'weak' || signal.status === 'blocked').length,
|
|
24
|
+
reviewRequiredMergeSignals: signals.filter((signal) => signal.reviewRequired).length,
|
|
25
|
+
mergeSignalQueryKeys: uniqueStrings(signals.flatMap((signal) => signal.queryKeys)),
|
|
26
|
+
mergeSignalScores: semanticImpactMergeScoreSummary(signals)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function semanticImpactMissingSignals(record) {
|
|
31
|
+
const missing = [];
|
|
32
|
+
if ((record.evidenceIds ?? []).length === 0) missing.push('evidence');
|
|
33
|
+
if ((record.sourceMapMappingIds ?? []).length === 0) missing.push('source-map');
|
|
34
|
+
if ((record.sourcePreservationRecordIds ?? []).length === 0) missing.push('source-preservation');
|
|
35
|
+
if (record.readiness !== 'ready' && (record.patchHintIds ?? []).length === 0) missing.push('patch-hint');
|
|
36
|
+
if ((record.openProofObligationIds ?? []).length > 0) missing.push('proof-obligations');
|
|
37
|
+
if (record.confidence === 'estimated-source-region' || record.confidence === 'review-required') missing.push('source-confidence');
|
|
38
|
+
return uniqueStrings(missing);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function semanticImpactMergeScore(record, missing, requiredSteps, blocked) {
|
|
42
|
+
let score = 100;
|
|
43
|
+
if (blocked) score -= 70;
|
|
44
|
+
else if (record.risk === 'high') score -= 35;
|
|
45
|
+
else if (record.risk === 'medium') score -= 18;
|
|
46
|
+
if (record.readiness === 'blocked') score -= 30;
|
|
47
|
+
else if (record.readiness === 'needs-review') score -= 18;
|
|
48
|
+
else if (record.readiness === 'ready-with-losses') score -= 8;
|
|
49
|
+
score -= Math.min(requiredSteps * 8, 32);
|
|
50
|
+
score -= Math.min(missing.length * 7, 35);
|
|
51
|
+
if ((record.failedProofObligationIds ?? []).length > 0) score -= 30;
|
|
52
|
+
if ((record.openProofObligationIds ?? []).length > 0) score -= 12;
|
|
53
|
+
if (record.confidence === 'source-exact') score += 5;
|
|
54
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function semanticImpactMergeStatus(record, score, blocked) {
|
|
58
|
+
if (blocked || score < 25) return 'blocked';
|
|
59
|
+
if (score < 50 || record.risk === 'high') return 'weak';
|
|
60
|
+
if (score < 80 || record.risk === 'medium' || record.readiness !== 'ready') return 'partial';
|
|
61
|
+
return 'strong';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function semanticImpactMergeQueryKeys(record, status, missing, reviewRequired) {
|
|
65
|
+
return uniqueStrings([
|
|
66
|
+
`merge-signal:${status}`,
|
|
67
|
+
`risk:${record.risk ?? 'unknown'}`,
|
|
68
|
+
`readiness:${record.readiness ?? 'unknown'}`,
|
|
69
|
+
reviewRequired ? 'review-required' : 'review-not-required',
|
|
70
|
+
...missing.map((item) => `missing:${item}`),
|
|
71
|
+
record.sourcePath ? `source:${record.sourcePath}` : undefined,
|
|
72
|
+
record.ownershipKey ? `ownership:${record.ownershipKey}` : undefined,
|
|
73
|
+
...(record.symbolNames ?? []).map((name) => `symbol-name:${name}`),
|
|
74
|
+
...(record.dependencyPredicates ?? []).map((predicate) => `predicate:${predicate}`)
|
|
75
|
+
].filter(Boolean));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function semanticImpactMergeScoreSummary(signals) {
|
|
79
|
+
if (!signals.length) return { min: 0, max: 0, average: 0 };
|
|
80
|
+
const scores = signals.map((signal) => signal.score);
|
|
81
|
+
return {
|
|
82
|
+
min: Math.min(...scores),
|
|
83
|
+
max: Math.max(...scores),
|
|
84
|
+
average: Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length)
|
|
85
|
+
};
|
|
86
|
+
}
|
package/package.json
CHANGED