@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.105
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 +14 -0
- package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
- package/dist/declarations/bidirectional-target-change.d.ts +10 -0
- package/dist/declarations/js-ts-safe-member-merge.d.ts +86 -0
- package/dist/declarations/js-ts-safe-merge.d.ts +131 -0
- package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
- package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
- package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
- package/dist/declarations/native-import-losses.d.ts +3 -0
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
- package/dist/declarations/semantic-edit-script.d.ts +7 -4
- package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
- package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
- package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
- package/dist/declarations/semantic-transform-identity.d.ts +3 -0
- package/dist/declarations/source-preservation.d.ts +72 -0
- package/dist/declarations/universal-capability.d.ts +4 -0
- package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
- package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
- package/dist/declarations/universal-conversion-plan.d.ts +6 -1
- package/dist/declarations/universal-representation-coverage.d.ts +90 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
- package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
- package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
- package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
- package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
- package/dist/internal/index-impl/diffNativeSymbols.js +3 -3
- package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +43 -8
- package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
- package/dist/internal/index-impl/replaySemanticEditProjection.js +39 -19
- package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
- package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
- package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
- package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
- package/dist/internal/index-impl/semanticEditProjectionRecord.js +29 -0
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
- package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
- package/dist/internal/index-impl/semanticEditScripts.js +4 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +27 -0
- package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
- package/dist/internal/index-impl/semanticPatchBundleAdmission.js +41 -7
- package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
- package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
- package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
- package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
- package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
- package/dist/js-ts-safe-member-merge-result.js +158 -0
- package/dist/js-ts-safe-member-merge.js +265 -0
- package/dist/js-ts-safe-merge-analyze.js +279 -0
- package/dist/js-ts-safe-merge-composed.js +170 -0
- package/dist/js-ts-safe-merge-constants.js +50 -0
- package/dist/js-ts-safe-merge-context.js +118 -0
- package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
- package/dist/js-ts-safe-merge-ledger.js +85 -0
- package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
- package/dist/js-ts-safe-merge-parse-statements.js +155 -0
- package/dist/js-ts-safe-merge-plan.js +190 -0
- package/dist/js-ts-safe-merge.js +175 -0
- package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
- package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
- package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
- package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
- package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
- package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
- package/dist/js-ts-semantic-merge-contracts.js +217 -0
- package/dist/js-ts-semantic-merge-member-containers.js +100 -0
- package/dist/js-ts-semantic-merge-member-keys.js +142 -0
- package/dist/js-ts-semantic-merge-member-segments.js +185 -0
- package/dist/js-ts-semantic-merge-member-source.js +82 -0
- package/dist/js-ts-semantic-merge-member-utils.js +18 -0
- package/dist/js-ts-semantic-merge-parse.js +16 -0
- package/dist/js-ts-semantic-merge.js +24 -0
- package/dist/lightweight-dependency-effects.js +51 -0
- package/dist/lightweight-dependency-language.js +12 -1
- package/dist/lightweight-dependency-relations.js +14 -27
- package/dist/native-region-scanner-core.js +33 -1
- package/dist/native-region-scanner-csharp.js +151 -0
- package/dist/native-region-scanner-dart.js +91 -0
- package/dist/native-region-scanner-dynamic.js +21 -151
- package/dist/native-region-scanner-functional.js +40 -13
- package/dist/native-region-scanner-java.js +97 -0
- package/dist/native-region-scanner-js-class.js +100 -0
- package/dist/native-region-scanner-js-helpers.js +28 -86
- package/dist/native-region-scanner-js-imports.js +121 -1
- package/dist/native-region-scanner-js-nested.js +96 -8
- package/dist/native-region-scanner-js-structure.js +27 -0
- package/dist/native-region-scanner-js-types.js +99 -0
- package/dist/native-region-scanner-js.js +70 -118
- package/dist/native-region-scanner-kotlin.js +94 -0
- package/dist/native-region-scanner-main.js +15 -181
- package/dist/native-region-scanner-php.js +80 -0
- package/dist/native-region-scanner-python.js +62 -0
- package/dist/native-region-scanner-ruby.js +72 -0
- package/dist/native-region-scanner-scala.js +91 -0
- package/dist/native-region-scanner-spans.js +74 -0
- package/dist/native-region-scanner-swift.js +155 -0
- package/dist/native-region-scanner.js +14 -10
- package/dist/native-source-ledger-helpers.js +195 -0
- package/dist/native-source-ledger.js +306 -0
- package/dist/native-source-preservation-scanner.js +4 -0
- package/dist/semantic-import-callsite-regions.js +136 -0
- package/dist/semantic-import-effect-regions.js +283 -0
- package/dist/semantic-import-regions.js +11 -2
- package/dist/semantic-import-sidecar-entry.js +16 -2
- package/dist/semantic-import-sidecar-types.d.ts +2 -0
- package/dist/semantic-sidecar-example.js +68 -0
- package/dist/universal-capability-matrix.js +23 -0
- package/dist/universal-conversion-artifact-query.js +79 -2
- package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
- package/dist/universal-conversion-artifact-summary.js +33 -1
- package/dist/universal-conversion-artifacts.js +13 -48
- package/dist/universal-conversion-plan-scoring.js +21 -1
- package/dist/universal-conversion-plan-summary.js +30 -0
- package/dist/universal-conversion-plan.js +25 -9
- package/dist/universal-conversion-route-metadata.js +96 -0
- package/dist/universal-conversion-route-operations.js +7 -0
- package/dist/universal-representation-coverage.js +193 -0
- package/package.json +1 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { analyzeVariantLedger, validateIndependentAdditions } from './js-ts-safe-merge-analyze.js';
|
|
2
|
+
import { addConflict, blockedResult, compactRecord, createMergeContext, gatesFor } from './js-ts-safe-merge-context.js';
|
|
3
|
+
import {
|
|
4
|
+
JsTsSafeMergeConflictCodes,
|
|
5
|
+
JsTsSafeMergeGateIds,
|
|
6
|
+
JsTsSafeMergeStatuses,
|
|
7
|
+
jsTsSafeMergeGateOrder
|
|
8
|
+
} from './js-ts-safe-merge-constants.js';
|
|
9
|
+
import { indexBaseLedger, scanJsTsTopLevelLedger, validateLedgerUniqueness } from './js-ts-safe-merge-ledger.js';
|
|
10
|
+
import { applySourceMergePlan, createSourceMergePlan } from './js-ts-safe-merge-plan.js';
|
|
11
|
+
|
|
12
|
+
export { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds, JsTsSafeMergeStatuses };
|
|
13
|
+
|
|
14
|
+
export function safeMergeJsTsImportsAndDeclarations(input = {}) {
|
|
15
|
+
const context = createMergeContext(input);
|
|
16
|
+
const baseSourceText = input.baseSourceText;
|
|
17
|
+
const workerSourceText = input.workerSourceText;
|
|
18
|
+
const headSourceText = input.headSourceText;
|
|
19
|
+
|
|
20
|
+
if (typeof baseSourceText !== 'string' || typeof workerSourceText !== 'string' || typeof headSourceText !== 'string') {
|
|
21
|
+
addConflict(context, {
|
|
22
|
+
code: JsTsSafeMergeConflictCodes.invalidInput,
|
|
23
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
24
|
+
message: 'baseSourceText, workerSourceText, and headSourceText must be strings.',
|
|
25
|
+
details: {
|
|
26
|
+
baseSourceText: typeof baseSourceText,
|
|
27
|
+
workerSourceText: typeof workerSourceText,
|
|
28
|
+
headSourceText: typeof headSourceText
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return blockedResult(context);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
validateStaleSourceHashes(input, context);
|
|
35
|
+
validateSourceLedgerSpans(input, context);
|
|
36
|
+
if (context.conflicts.length) return blockedResult(context);
|
|
37
|
+
|
|
38
|
+
const base = scanJsTsTopLevelLedger(baseSourceText, 'base', context);
|
|
39
|
+
const worker = scanJsTsTopLevelLedger(workerSourceText, 'worker', context);
|
|
40
|
+
const head = scanJsTsTopLevelLedger(headSourceText, 'head', context);
|
|
41
|
+
if (context.conflicts.length) return blockedResult(context, { base, worker, head });
|
|
42
|
+
|
|
43
|
+
validateLedgerUniqueness(base, context);
|
|
44
|
+
validateLedgerUniqueness(worker, context);
|
|
45
|
+
validateLedgerUniqueness(head, context);
|
|
46
|
+
if (context.conflicts.length) return blockedResult(context, { base, worker, head });
|
|
47
|
+
|
|
48
|
+
const baseIndex = indexBaseLedger(base, context);
|
|
49
|
+
if (context.conflicts.length) return blockedResult(context, { base, worker, head });
|
|
50
|
+
|
|
51
|
+
const workerPlan = analyzeVariantLedger(base, worker, baseIndex, 'worker', context);
|
|
52
|
+
const headPlan = analyzeVariantLedger(base, head, baseIndex, 'head', context);
|
|
53
|
+
validateIndependentAdditions(base, workerPlan, headPlan, context);
|
|
54
|
+
if (context.conflicts.length) return blockedResult(context, { base, worker, head });
|
|
55
|
+
|
|
56
|
+
const mergePlan = createSourceMergePlan(base, worker, head, workerPlan, headPlan, context);
|
|
57
|
+
if (context.conflicts.length) return blockedResult(context, { base, worker, head });
|
|
58
|
+
|
|
59
|
+
const mergedSourceText = applySourceMergePlan(headSourceText, mergePlan);
|
|
60
|
+
const merged = scanJsTsTopLevelLedger(mergedSourceText, 'merged', context);
|
|
61
|
+
if (!context.conflicts.length) validateLedgerUniqueness(merged, context);
|
|
62
|
+
if (context.conflicts.length) return blockedResult(context, { base, worker, head, merged });
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
kind: 'frontier.lang.jsTsSafeMerge',
|
|
66
|
+
version: 1,
|
|
67
|
+
schema: 'frontier.lang.jsTsSafeMerge.v1',
|
|
68
|
+
id: context.id,
|
|
69
|
+
status: JsTsSafeMergeStatuses.merged,
|
|
70
|
+
sourcePath: context.sourcePath,
|
|
71
|
+
language: context.language,
|
|
72
|
+
mergedSourceText,
|
|
73
|
+
outputSourceText: mergedSourceText,
|
|
74
|
+
conflicts: [],
|
|
75
|
+
gates: gatesFor(context),
|
|
76
|
+
admission: {
|
|
77
|
+
status: 'auto-merge-candidate',
|
|
78
|
+
action: 'apply',
|
|
79
|
+
reviewRequired: false,
|
|
80
|
+
autoApplyCandidate: true,
|
|
81
|
+
autoMergeClaim: false,
|
|
82
|
+
semanticEquivalenceClaim: false,
|
|
83
|
+
reasonCodes: []
|
|
84
|
+
},
|
|
85
|
+
summary: {
|
|
86
|
+
importSpecifierAdditions: mergePlan.importSpecifierAdditions,
|
|
87
|
+
topLevelDeclarationAdditions: mergePlan.topLevelDeclarationAdditions,
|
|
88
|
+
changedExistingDeclarations: 0,
|
|
89
|
+
conflicts: 0,
|
|
90
|
+
gatesPassed: jsTsSafeMergeGateOrder.length
|
|
91
|
+
},
|
|
92
|
+
metadata: compactRecord({
|
|
93
|
+
workerChangeSetId: input.workerChangeSetId,
|
|
94
|
+
headChangeSetId: input.headChangeSetId,
|
|
95
|
+
baseHash: input.baseHash,
|
|
96
|
+
workerHash: input.workerHash,
|
|
97
|
+
headHash: input.headHash,
|
|
98
|
+
expectedSourceHash: input.expectedSourceHash,
|
|
99
|
+
currentSourceHash: input.currentSourceHash
|
|
100
|
+
})
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function validateStaleSourceHashes(input, context) {
|
|
105
|
+
for (const pair of [
|
|
106
|
+
['expectedSourceHash', 'currentSourceHash', 'head'],
|
|
107
|
+
['expectedHeadHash', 'headHash', 'head'],
|
|
108
|
+
['expectedBaseHash', 'baseHash', 'base'],
|
|
109
|
+
['expectedWorkerHash', 'workerHash', 'worker']
|
|
110
|
+
]) {
|
|
111
|
+
const [expectedField, currentField, side] = pair;
|
|
112
|
+
const expected = input[expectedField];
|
|
113
|
+
const current = input[currentField];
|
|
114
|
+
if (typeof expected !== 'string' || typeof current !== 'string' || expected === current) continue;
|
|
115
|
+
addConflict(context, {
|
|
116
|
+
code: JsTsSafeMergeConflictCodes.staleSourceHash,
|
|
117
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
118
|
+
side,
|
|
119
|
+
message: `${side} source hash is stale for safe merge anchors.`,
|
|
120
|
+
details: { expectedField, currentField, expected, current }
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function validateSourceLedgerSpans(input, context) {
|
|
126
|
+
if (!input.requireSourceLedgerSpans && !input.sourceLedgers && !input.sourceLedger) return;
|
|
127
|
+
for (const side of ['base', 'worker', 'head']) {
|
|
128
|
+
const ledger = sourceLedgerForSide(input, side);
|
|
129
|
+
if (!ledger) {
|
|
130
|
+
addConflict(context, {
|
|
131
|
+
code: JsTsSafeMergeConflictCodes.missingSourceLedgerSpan,
|
|
132
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
133
|
+
side,
|
|
134
|
+
message: `${side} source is missing source ledger span evidence.`,
|
|
135
|
+
details: { side, missing: 'source-ledger' }
|
|
136
|
+
});
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const spans = Array.isArray(ledger.spans) ? ledger.spans : Array.isArray(ledger.entries) ? ledger.entries : [];
|
|
140
|
+
if (!spans.length) {
|
|
141
|
+
addConflict(context, {
|
|
142
|
+
code: JsTsSafeMergeConflictCodes.missingSourceLedgerSpan,
|
|
143
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
144
|
+
side,
|
|
145
|
+
message: `${side} source ledger does not include span entries.`,
|
|
146
|
+
details: { side, missing: 'spans' }
|
|
147
|
+
});
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const missingIndex = spans.findIndex((entry) => !hasLedgerSpan(entry));
|
|
151
|
+
if (missingIndex >= 0) {
|
|
152
|
+
addConflict(context, {
|
|
153
|
+
code: JsTsSafeMergeConflictCodes.missingSourceLedgerSpan,
|
|
154
|
+
gateId: JsTsSafeMergeGateIds.parseLedger,
|
|
155
|
+
side,
|
|
156
|
+
message: `${side} source ledger contains an entry without a source span.`,
|
|
157
|
+
details: { side, index: missingIndex, id: spans[missingIndex]?.id, kind: spans[missingIndex]?.kind }
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function sourceLedgerForSide(input, side) {
|
|
164
|
+
return input.sourceLedgers?.[side]
|
|
165
|
+
?? input[`${side}SourceLedger`]
|
|
166
|
+
?? (input.sourceLedgerSide === side ? input.sourceLedger : undefined);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function hasLedgerSpan(entry) {
|
|
170
|
+
const span = entry?.span ?? entry?.sourceSpan;
|
|
171
|
+
if (!span) return false;
|
|
172
|
+
const hasOffsets = Number.isFinite(span.start) && Number.isFinite(span.end);
|
|
173
|
+
const hasLines = Number.isFinite(span.startLine) && Number.isFinite(span.endLine);
|
|
174
|
+
return hasOffsets || hasLines;
|
|
175
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export const JsTsSemanticConflictSidecarClasses = Object.freeze([
|
|
2
|
+
'same-region',
|
|
3
|
+
'delete-modify',
|
|
4
|
+
'duplicate-export',
|
|
5
|
+
'duplicate-member',
|
|
6
|
+
'ordered-list-conflict',
|
|
7
|
+
'parser-ledger-loss',
|
|
8
|
+
'stale-source-hash',
|
|
9
|
+
'unsupported-syntax'
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
export const classDefaults = Object.freeze({
|
|
13
|
+
'same-region': {
|
|
14
|
+
severity: 'error',
|
|
15
|
+
risk: 'high',
|
|
16
|
+
readiness: 'blocked',
|
|
17
|
+
reasonCodes: ['same-region-concurrent-edit'],
|
|
18
|
+
suggestedOutcome: 'manual-merge-required'
|
|
19
|
+
},
|
|
20
|
+
'delete-modify': {
|
|
21
|
+
severity: 'error',
|
|
22
|
+
risk: 'high',
|
|
23
|
+
readiness: 'blocked',
|
|
24
|
+
reasonCodes: ['delete-modify-same-region'],
|
|
25
|
+
suggestedOutcome: 'choose-delete-or-port-modification'
|
|
26
|
+
},
|
|
27
|
+
'duplicate-export': {
|
|
28
|
+
severity: 'error',
|
|
29
|
+
risk: 'high',
|
|
30
|
+
readiness: 'blocked',
|
|
31
|
+
reasonCodes: ['duplicate-export-name'],
|
|
32
|
+
suggestedOutcome: 'rename-or-remove-duplicate-export'
|
|
33
|
+
},
|
|
34
|
+
'duplicate-member': {
|
|
35
|
+
severity: 'error',
|
|
36
|
+
risk: 'high',
|
|
37
|
+
readiness: 'blocked',
|
|
38
|
+
reasonCodes: ['duplicate-member-name'],
|
|
39
|
+
suggestedOutcome: 'rename-or-remove-duplicate-member'
|
|
40
|
+
},
|
|
41
|
+
'ordered-list-conflict': {
|
|
42
|
+
severity: 'warning',
|
|
43
|
+
risk: 'medium',
|
|
44
|
+
readiness: 'needs-review',
|
|
45
|
+
reasonCodes: ['ordered-list-concurrent-position'],
|
|
46
|
+
suggestedOutcome: 'preserve-intended-order-with-human-review'
|
|
47
|
+
},
|
|
48
|
+
'parser-ledger-loss': {
|
|
49
|
+
severity: 'error',
|
|
50
|
+
risk: 'high',
|
|
51
|
+
readiness: 'blocked',
|
|
52
|
+
reasonCodes: ['parser-or-ledger-loss'],
|
|
53
|
+
suggestedOutcome: 'rerun-parser-and-ledger-before-merge'
|
|
54
|
+
},
|
|
55
|
+
'stale-source-hash': {
|
|
56
|
+
severity: 'error',
|
|
57
|
+
risk: 'high',
|
|
58
|
+
readiness: 'blocked',
|
|
59
|
+
reasonCodes: ['stale-source-hash'],
|
|
60
|
+
suggestedOutcome: 'rerun-semantic-import-before-merge'
|
|
61
|
+
},
|
|
62
|
+
'unsupported-syntax': {
|
|
63
|
+
severity: 'error',
|
|
64
|
+
risk: 'high',
|
|
65
|
+
readiness: 'blocked',
|
|
66
|
+
reasonCodes: ['unsupported-js-ts-syntax'],
|
|
67
|
+
suggestedOutcome: 'fall-back-to-textual-review'
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export const severityRank = Object.freeze({ info: 1, warning: 2, error: 3 });
|
|
72
|
+
export const riskRank = Object.freeze({ low: 1, medium: 2, high: 3 });
|
|
73
|
+
export const orderedChangeKinds = /^(?:insert|add|move|reorder|replace|modify|update)$/i;
|
|
74
|
+
export const deleteChangeKinds = /^(?:delete|remove|removed|deleted)$/i;
|
|
75
|
+
export const modifyChangeKinds = /^(?:modify|modified|replace|update|edit|move|reorder|insert|add)$/i;
|
|
76
|
+
export const parserLedgerLossPattern = /(?:parser|parse|ledger|source[-_ ]?map|anchor|projection).*(?:loss|lost|missing|failed|error)|(?:loss|lost|missing).*(?:parser|ledger|source[-_ ]?map|anchor|projection)/i;
|
|
77
|
+
export const unsupportedSyntaxPattern = /unsupported|unhandled|unknown syntax|syntax unsupported/i;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { idFragment, normalizeSemanticMergeReadiness, uniqueStrings } from './native-import-utils.js';
|
|
2
|
+
import { classDefaults, JsTsSemanticConflictSidecarClasses, orderedChangeKinds, parserLedgerLossPattern, unsupportedSyntaxPattern } from './js-ts-semantic-conflict-sidecar-constants.js';
|
|
3
|
+
import { affectedFromEntries, normalizeAffected, normalizeChange, normalizeDeclaration, sameChangeRegion } from './js-ts-semantic-conflict-sidecar-normalize.js';
|
|
4
|
+
import { array, changePairs, compactRecord, duplicateGroups, firstString, isDeleteChange, isInsertChange, isModifyChange, lossReasonCodes, lossText, normalizeRisk, normalizeSeverity, strings } from './js-ts-semantic-conflict-sidecar-utils.js';
|
|
5
|
+
|
|
6
|
+
export function explicitSidecars(input, context) {
|
|
7
|
+
return [
|
|
8
|
+
...array(input.conflicts),
|
|
9
|
+
...array(input.sidecars),
|
|
10
|
+
...array(input.conflictSidecars)
|
|
11
|
+
]
|
|
12
|
+
.filter((record) => JsTsSemanticConflictSidecarClasses.includes(record?.class))
|
|
13
|
+
.map((record) => conflictSidecar(record.class, record, context));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function sameRegionSidecars(changes, context) {
|
|
17
|
+
const records = [];
|
|
18
|
+
for (const [left, right] of changePairs(changes)) {
|
|
19
|
+
if (!sameChangeRegion(left, right)) continue;
|
|
20
|
+
records.push(conflictSidecar('same-region', {
|
|
21
|
+
id: `js_ts_conflict_${idFragment(left.id)}_${idFragment(right.id)}_same_region`,
|
|
22
|
+
affected: affectedFromEntries([left, right], context),
|
|
23
|
+
reasonCodes: ['same-region-concurrent-edit', ...left.reasonCodes, ...right.reasonCodes],
|
|
24
|
+
metadata: { leftChangeId: left.id, rightChangeId: right.id, leftChangeKind: left.changeKind, rightChangeKind: right.changeKind }
|
|
25
|
+
}, context));
|
|
26
|
+
}
|
|
27
|
+
return records;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function deleteModifySidecars(changes, context) {
|
|
31
|
+
const records = [];
|
|
32
|
+
for (const [left, right] of changePairs(changes)) {
|
|
33
|
+
if (!sameChangeRegion(left, right)) continue;
|
|
34
|
+
if (!(isDeleteChange(left) && isModifyChange(right)) && !(isDeleteChange(right) && isModifyChange(left))) continue;
|
|
35
|
+
const deleted = isDeleteChange(left) ? left : right;
|
|
36
|
+
const modified = deleted === left ? right : left;
|
|
37
|
+
records.push(conflictSidecar('delete-modify', {
|
|
38
|
+
id: `js_ts_conflict_${idFragment(deleted.id)}_${idFragment(modified.id)}_delete_modify`,
|
|
39
|
+
affected: affectedFromEntries([deleted, modified], context),
|
|
40
|
+
reasonCodes: ['delete-modify-same-region', ...deleted.reasonCodes, ...modified.reasonCodes],
|
|
41
|
+
metadata: { deletedChangeId: deleted.id, modifiedChangeId: modified.id, deletedChangeKind: deleted.changeKind, modifiedChangeKind: modified.changeKind }
|
|
42
|
+
}, context));
|
|
43
|
+
}
|
|
44
|
+
return records;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function duplicateDeclarationSidecars(input, changes, context) {
|
|
48
|
+
const declarations = [
|
|
49
|
+
...array(input.declarations),
|
|
50
|
+
...array(input.exports).map((entry) => ({ ...entry, exported: true })),
|
|
51
|
+
...array(input.members).map((entry) => ({ ...entry, member: true })),
|
|
52
|
+
...array(input.symbols),
|
|
53
|
+
...changes.filter((change) => isInsertChange(change) && (change.memberName || change.exportName || change.symbolName))
|
|
54
|
+
].map((entry, index) => normalizeDeclaration(entry, index, context)).filter((entry) => entry.name);
|
|
55
|
+
return [
|
|
56
|
+
...duplicateGroups(declarations.filter((entry) => entry.exported), (entry) => [entry.sourcePath, entry.name].join('|'))
|
|
57
|
+
.map((group) => duplicateRecord('duplicate-export', group, context)),
|
|
58
|
+
...duplicateGroups(declarations.filter((entry) => entry.member || entry.containerKey), (entry) => [entry.sourcePath, entry.containerKey, entry.name].join('|'))
|
|
59
|
+
.map((group) => duplicateRecord('duplicate-member', group, context))
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function orderedListSidecars(input, changes, context) {
|
|
64
|
+
const explicitLists = array(input.orderedLists).flatMap((list, listIndex) =>
|
|
65
|
+
array(list.changes ?? list.operations ?? list.edits).map((change, changeIndex) => normalizeChange({
|
|
66
|
+
...change,
|
|
67
|
+
listKey: change.listKey ?? list.key ?? list.id,
|
|
68
|
+
orderedListKey: change.orderedListKey ?? list.key ?? list.id,
|
|
69
|
+
sourcePath: change.sourcePath ?? list.sourcePath,
|
|
70
|
+
sourceSpan: change.sourceSpan ?? list.sourceSpan,
|
|
71
|
+
id: change.id ?? `${list.id ?? list.key ?? 'ordered_list'}_${changeIndex}`,
|
|
72
|
+
side: change.side ?? change.author ?? `list_${listIndex}`
|
|
73
|
+
}, context, `ordered_list_${listIndex}_${changeIndex}`))
|
|
74
|
+
);
|
|
75
|
+
const orderedChanges = [...changes, ...explicitLists].filter((change) =>
|
|
76
|
+
change.listKey && orderedChangeKinds.test(change.changeKind)
|
|
77
|
+
);
|
|
78
|
+
const groups = duplicateGroups(orderedChanges, (change) => [
|
|
79
|
+
change.sourcePath,
|
|
80
|
+
change.listKey,
|
|
81
|
+
firstString(change.index, change.position, change.orderKey, change.beforeKey, change.afterKey, change.anchorKey)
|
|
82
|
+
].join('|'));
|
|
83
|
+
return groups.map((group) => conflictSidecar('ordered-list-conflict', {
|
|
84
|
+
id: `js_ts_conflict_${idFragment(group[0].listKey)}_${idFragment(firstString(group[0].index, group[0].position, group[0].orderKey, 'position'))}_ordered_list`,
|
|
85
|
+
affected: affectedFromEntries(group, context),
|
|
86
|
+
reasonCodes: ['ordered-list-concurrent-position', ...group.flatMap((entry) => entry.reasonCodes)],
|
|
87
|
+
metadata: {
|
|
88
|
+
listKey: group[0].listKey,
|
|
89
|
+
position: firstString(group[0].index, group[0].position, group[0].orderKey, group[0].beforeKey, group[0].afterKey),
|
|
90
|
+
changeIds: group.map((entry) => entry.id)
|
|
91
|
+
}
|
|
92
|
+
}, context));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function parserLedgerLossSidecars(input, context) {
|
|
96
|
+
const losses = [
|
|
97
|
+
...array(input.parserLosses),
|
|
98
|
+
...array(input.ledgerLosses),
|
|
99
|
+
...array(input.losses),
|
|
100
|
+
...array(input.diagnostics)
|
|
101
|
+
].filter((entry) => parserLedgerLossPattern.test(lossText(entry)) || entry?.kind === 'parser-ledger-loss');
|
|
102
|
+
if (!losses.length) return [];
|
|
103
|
+
return [conflictSidecar('parser-ledger-loss', {
|
|
104
|
+
id: `js_ts_conflict_${idFragment(context.sourcePath ?? context.language ?? 'source')}_parser_ledger_loss`,
|
|
105
|
+
severity: losses.some((entry) => entry.severity === 'error' || entry.status === 'failed') ? 'error' : undefined,
|
|
106
|
+
affected: affectedFromEntries(losses, context),
|
|
107
|
+
reasonCodes: ['parser-or-ledger-loss', ...losses.flatMap(lossReasonCodes)],
|
|
108
|
+
metadata: { lossIds: uniqueStrings(losses.map((entry) => entry.id)), lossKinds: uniqueStrings(losses.map((entry) => entry.kind ?? entry.code)) }
|
|
109
|
+
}, context)];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function staleSourceHashSidecars(input, context) {
|
|
113
|
+
if (!context.expectedSourceHash || !context.currentSourceHash) return [];
|
|
114
|
+
if (context.expectedSourceHash === context.currentSourceHash && input.staleSourceHash !== true) return [];
|
|
115
|
+
return [conflictSidecar('stale-source-hash', {
|
|
116
|
+
id: `js_ts_conflict_${idFragment(context.sourcePath ?? context.language ?? 'source')}_stale_source_hash`,
|
|
117
|
+
affected: affectedFromEntries([{ sourcePath: context.sourcePath, sourceHash: context.currentSourceHash, key: context.sourcePath }], context),
|
|
118
|
+
reasonCodes: ['stale-source-hash', 'expected-source-hash-mismatch'],
|
|
119
|
+
metadata: { expectedSourceHash: context.expectedSourceHash, currentSourceHash: context.currentSourceHash }
|
|
120
|
+
}, context)];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function unsupportedSyntaxSidecars(input, context) {
|
|
124
|
+
const entries = [
|
|
125
|
+
...array(input.unsupportedSyntax),
|
|
126
|
+
...array(input.syntaxUnsupported),
|
|
127
|
+
...array(input.syntaxLosses),
|
|
128
|
+
...array(input.losses).filter((entry) => unsupportedSyntaxPattern.test(lossText(entry)))
|
|
129
|
+
];
|
|
130
|
+
if (!entries.length) return [];
|
|
131
|
+
return entries.map((entry, index) => conflictSidecar('unsupported-syntax', {
|
|
132
|
+
id: entry.id ?? `js_ts_conflict_${idFragment(context.sourcePath ?? context.language ?? 'source')}_${index + 1}_unsupported_syntax`,
|
|
133
|
+
affected: affectedFromEntries([entry], context),
|
|
134
|
+
reasonCodes: ['unsupported-js-ts-syntax', ...lossReasonCodes(entry)],
|
|
135
|
+
metadata: compactRecord({
|
|
136
|
+
syntaxKind: entry.kind ?? entry.syntaxKind,
|
|
137
|
+
parser: entry.parser,
|
|
138
|
+
message: entry.message ?? entry.summary
|
|
139
|
+
})
|
|
140
|
+
}, context));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function duplicateRecord(conflictClass, group, context) {
|
|
144
|
+
return conflictSidecar(conflictClass, {
|
|
145
|
+
id: `js_ts_conflict_${idFragment(group[0].sourcePath ?? context.sourcePath ?? 'source')}_${idFragment(group[0].containerKey ?? 'export')}_${idFragment(group[0].name)}_${conflictClass.replace(/-/g, '_')}`,
|
|
146
|
+
affected: affectedFromEntries(group, context),
|
|
147
|
+
reasonCodes: [conflictClass === 'duplicate-member' ? 'duplicate-member-name' : 'duplicate-export-name'],
|
|
148
|
+
metadata: {
|
|
149
|
+
duplicateName: group[0].name,
|
|
150
|
+
containerKey: group[0].containerKey,
|
|
151
|
+
declarationIds: group.map((entry) => entry.id)
|
|
152
|
+
}
|
|
153
|
+
}, context);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function conflictSidecar(conflictClass, input, context) {
|
|
157
|
+
const defaults = classDefaults[conflictClass] ?? classDefaults['same-region'];
|
|
158
|
+
const affected = normalizeAffected(input.affected ?? input, context);
|
|
159
|
+
const reasonCodes = uniqueStrings([
|
|
160
|
+
...defaults.reasonCodes,
|
|
161
|
+
...strings(input.reasonCode),
|
|
162
|
+
...strings(input.reasonCodes)
|
|
163
|
+
]);
|
|
164
|
+
const severity = normalizeSeverity(input.severity) ?? defaults.severity;
|
|
165
|
+
const risk = normalizeRisk(input.risk) ?? defaults.risk;
|
|
166
|
+
const readiness = normalizeSemanticMergeReadiness(input.readiness) ?? defaults.readiness;
|
|
167
|
+
const suggestedOutcome = input.suggestedOutcome ?? input.outcome ?? defaults.suggestedOutcome;
|
|
168
|
+
const id = input.id ?? `js_ts_conflict_${idFragment(conflictClass)}_${idFragment([
|
|
169
|
+
...affected.keys,
|
|
170
|
+
...affected.sourcePaths,
|
|
171
|
+
...reasonCodes
|
|
172
|
+
].join('_'))}`;
|
|
173
|
+
return {
|
|
174
|
+
kind: 'frontier.lang.jsTsSemanticMergeConflictSidecar',
|
|
175
|
+
version: 1,
|
|
176
|
+
schema: 'frontier.lang.jsTsSemanticMergeConflictSidecar.v1',
|
|
177
|
+
id,
|
|
178
|
+
class: conflictClass,
|
|
179
|
+
severity,
|
|
180
|
+
risk,
|
|
181
|
+
readiness,
|
|
182
|
+
affected,
|
|
183
|
+
reasonCodes,
|
|
184
|
+
suggestedOutcome,
|
|
185
|
+
explanation: {
|
|
186
|
+
class: conflictClass,
|
|
187
|
+
severity,
|
|
188
|
+
risk,
|
|
189
|
+
affected,
|
|
190
|
+
reasonCodes,
|
|
191
|
+
suggestedOutcome
|
|
192
|
+
},
|
|
193
|
+
metadata: compactRecord(input.metadata)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { uniqueStrings } from './native-import-utils.js';
|
|
2
|
+
import { array, compactRecord, firstString, keysForAffected, keysForChange, normalizeChangeKind, numberOrUndefined, numberValue, strings } from './js-ts-semantic-conflict-sidecar-utils.js';
|
|
3
|
+
|
|
4
|
+
export function normalizedChanges(input, context) {
|
|
5
|
+
return [
|
|
6
|
+
...array(input.changes),
|
|
7
|
+
...array(input.operations),
|
|
8
|
+
...array(input.edits),
|
|
9
|
+
...array(input.left?.changes).map((change) => ({ ...change, side: change.side ?? 'left' })),
|
|
10
|
+
...array(input.left?.operations).map((change) => ({ ...change, side: change.side ?? 'left' })),
|
|
11
|
+
...array(input.right?.changes).map((change) => ({ ...change, side: change.side ?? 'right' })),
|
|
12
|
+
...array(input.right?.operations).map((change) => ({ ...change, side: change.side ?? 'right' }))
|
|
13
|
+
].map((change, index) => normalizeChange(change, context, `change_${index + 1}`));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function normalizeChange(change, context, fallbackId) {
|
|
17
|
+
const anchor = change.anchor ?? {};
|
|
18
|
+
const insertion = change.insertion ?? {};
|
|
19
|
+
const spans = change.spans ?? {};
|
|
20
|
+
const sourcePath = firstString(
|
|
21
|
+
change.sourcePath,
|
|
22
|
+
change.originalSourcePath,
|
|
23
|
+
change.targetSourcePath,
|
|
24
|
+
anchor.sourcePath,
|
|
25
|
+
insertion.sourcePath,
|
|
26
|
+
insertion.insertedSourcePath,
|
|
27
|
+
context.sourcePath
|
|
28
|
+
);
|
|
29
|
+
const span = normalizeSpan(
|
|
30
|
+
change.sourceSpan
|
|
31
|
+
?? change.span
|
|
32
|
+
?? change.range
|
|
33
|
+
?? spans.head
|
|
34
|
+
?? spans.worker
|
|
35
|
+
?? spans.base
|
|
36
|
+
?? anchor.sourceSpan
|
|
37
|
+
?? insertion.insertedSourceSpan
|
|
38
|
+
?? insertion.baseSpan
|
|
39
|
+
?? insertion.headSpan,
|
|
40
|
+
sourcePath
|
|
41
|
+
);
|
|
42
|
+
const key = firstString(
|
|
43
|
+
change.conflictKey,
|
|
44
|
+
change.regionKey,
|
|
45
|
+
change.key,
|
|
46
|
+
change.semanticKey,
|
|
47
|
+
change.anchorKey,
|
|
48
|
+
anchor.conflictKey,
|
|
49
|
+
anchor.key,
|
|
50
|
+
anchor.regionId,
|
|
51
|
+
insertion.anchorKey
|
|
52
|
+
);
|
|
53
|
+
const listKey = firstString(change.listKey, change.orderedListKey, change.containerKey, change.parentKey, change.orderingKey);
|
|
54
|
+
return {
|
|
55
|
+
raw: change,
|
|
56
|
+
id: firstString(change.id, change.operationId, fallbackId),
|
|
57
|
+
side: firstString(change.side, change.author, change.branch, change.source, fallbackId),
|
|
58
|
+
changeKind: normalizeChangeKind(change.changeKind ?? change.kind ?? change.editKind ?? change.op),
|
|
59
|
+
sourcePath,
|
|
60
|
+
span,
|
|
61
|
+
key,
|
|
62
|
+
regionKey: firstString(change.regionKey, anchor.regionId, key),
|
|
63
|
+
conflictKey: firstString(change.conflictKey, anchor.conflictKey, key),
|
|
64
|
+
symbolId: firstString(change.symbolId, anchor.symbolId, insertion.insertedSymbolId),
|
|
65
|
+
symbolName: firstString(change.symbolName, anchor.symbolName, insertion.insertedSymbolName),
|
|
66
|
+
symbolKind: firstString(change.symbolKind, anchor.symbolKind, insertion.insertedSymbolKind),
|
|
67
|
+
memberName: firstString(change.memberName, change.name, change.symbolName, insertion.insertedSymbolName),
|
|
68
|
+
exportName: firstString(change.exportName, change.exportedName),
|
|
69
|
+
containerKey: firstString(change.containerKey, change.parentKey, anchor.regionId),
|
|
70
|
+
listKey,
|
|
71
|
+
orderedListKey: firstString(change.orderedListKey, listKey),
|
|
72
|
+
index: firstString(change.index, change.position, change.orderIndex),
|
|
73
|
+
position: firstString(change.position, change.index),
|
|
74
|
+
orderKey: firstString(change.orderKey, change.order, change.sortKey),
|
|
75
|
+
beforeKey: firstString(change.beforeKey, change.before, change.previousKey),
|
|
76
|
+
afterKey: firstString(change.afterKey, change.after, change.nextKey),
|
|
77
|
+
anchorKey: firstString(change.anchorKey, insertion.anchorKey, anchor.key),
|
|
78
|
+
sourceHash: firstString(change.sourceHash, change.currentSourceHash, change.targetHash),
|
|
79
|
+
reasonCodes: uniqueStrings([...strings(change.reasonCode), ...strings(change.reasonCodes)])
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function normalizeDeclaration(entry, index, context) {
|
|
84
|
+
const change = normalizeChange(entry, context, `declaration_${index + 1}`);
|
|
85
|
+
const name = firstString(entry.name, entry.memberName, entry.exportName, entry.symbolName, change.memberName, change.exportName, change.symbolName);
|
|
86
|
+
const exported = entry.exported === true || entry.isExport === true || Boolean(entry.exportName) || /export/i.test(String(entry.kind ?? entry.symbolKind ?? ''));
|
|
87
|
+
const member = entry.member === true || entry.isMember === true || Boolean(entry.memberName || entry.containerKey || entry.parentKey) || /member|method|field|property/i.test(String(entry.kind ?? entry.symbolKind ?? ''));
|
|
88
|
+
return {
|
|
89
|
+
...change,
|
|
90
|
+
name,
|
|
91
|
+
exported,
|
|
92
|
+
member,
|
|
93
|
+
containerKey: firstString(entry.containerKey, entry.parentKey, entry.classKey, entry.ownerKey, change.containerKey),
|
|
94
|
+
key: firstString(entry.key, entry.semanticKey, change.key, name)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function sameChangeRegion(left, right) {
|
|
99
|
+
if (left.id === right.id) return false;
|
|
100
|
+
if (left.side && right.side && left.side === right.side) return false;
|
|
101
|
+
const leftKeys = keysForChange(left);
|
|
102
|
+
const rightKeys = keysForChange(right);
|
|
103
|
+
if (leftKeys.some((key) => rightKeys.includes(key))) return true;
|
|
104
|
+
return spansOverlap(left.span, right.span);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function spansOverlap(left, right) {
|
|
108
|
+
if (!left || !right) return false;
|
|
109
|
+
if (left.sourcePath && right.sourcePath && left.sourcePath !== right.sourcePath) return false;
|
|
110
|
+
const leftStart = numberValue(left.startOffset, left.start);
|
|
111
|
+
const leftEnd = numberValue(left.endOffset, left.end);
|
|
112
|
+
const rightStart = numberValue(right.startOffset, right.start);
|
|
113
|
+
const rightEnd = numberValue(right.endOffset, right.end);
|
|
114
|
+
if (Number.isFinite(leftStart) && Number.isFinite(leftEnd) && Number.isFinite(rightStart) && Number.isFinite(rightEnd)) {
|
|
115
|
+
return leftStart < rightEnd && rightStart < leftEnd;
|
|
116
|
+
}
|
|
117
|
+
const leftStartLine = numberValue(left.startLine, left.line);
|
|
118
|
+
const leftEndLine = numberValue(left.endLine, leftStartLine);
|
|
119
|
+
const rightStartLine = numberValue(right.startLine, right.line);
|
|
120
|
+
const rightEndLine = numberValue(right.endLine, rightStartLine);
|
|
121
|
+
if (Number.isFinite(leftStartLine) && Number.isFinite(leftEndLine) && Number.isFinite(rightStartLine) && Number.isFinite(rightEndLine)) {
|
|
122
|
+
return leftStartLine <= rightEndLine && rightStartLine <= leftEndLine;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function affectedFromEntries(entries, context) {
|
|
128
|
+
return normalizeAffected({
|
|
129
|
+
sourcePath: uniqueStrings(entries.map((entry) => entry.sourcePath)),
|
|
130
|
+
sourceSpan: entries.map((entry) => entry.span ?? entry.sourceSpan).filter(Boolean),
|
|
131
|
+
key: entries.flatMap((entry) => keysForAffected(entry)),
|
|
132
|
+
symbolId: entries.map((entry) => entry.symbolId),
|
|
133
|
+
symbolName: entries.map((entry) => entry.symbolName ?? entry.name),
|
|
134
|
+
memberKey: entries.map((entry) => entry.memberName ? `${entry.containerKey ?? entry.sourcePath ?? context.sourcePath ?? 'member'}#${entry.memberName}` : undefined),
|
|
135
|
+
exportName: entries.map((entry) => entry.exportName ?? (entry.exported ? entry.name : undefined)),
|
|
136
|
+
orderedListKey: entries.map((entry) => entry.listKey ?? entry.orderedListKey),
|
|
137
|
+
sourceHash: entries.map((entry) => entry.sourceHash)
|
|
138
|
+
}, context);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function normalizeAffected(value, context) {
|
|
142
|
+
const sourcePaths = uniqueStrings([
|
|
143
|
+
context.sourcePath,
|
|
144
|
+
...strings(value.sourcePath),
|
|
145
|
+
...strings(value.sourcePaths),
|
|
146
|
+
...array(value.spans).map((span) => span?.sourcePath),
|
|
147
|
+
...array(value.sourceSpan).map((span) => span?.sourcePath)
|
|
148
|
+
]);
|
|
149
|
+
const spans = uniqueSpans([
|
|
150
|
+
...array(value.sourceSpan),
|
|
151
|
+
...array(value.span),
|
|
152
|
+
...array(value.spans)
|
|
153
|
+
].map((span) => normalizeSpan(span, sourcePaths[0])).filter(Boolean));
|
|
154
|
+
const keys = uniqueStrings([
|
|
155
|
+
...strings(value.key),
|
|
156
|
+
...strings(value.keys),
|
|
157
|
+
...strings(value.conflictKey),
|
|
158
|
+
...strings(value.conflictKeys),
|
|
159
|
+
...strings(value.regionKey),
|
|
160
|
+
...strings(value.regionKeys),
|
|
161
|
+
...strings(value.semanticKey),
|
|
162
|
+
...strings(value.anchorKey)
|
|
163
|
+
]);
|
|
164
|
+
return {
|
|
165
|
+
sourcePaths,
|
|
166
|
+
spans,
|
|
167
|
+
keys,
|
|
168
|
+
regionKeys: uniqueStrings([...strings(value.regionKey), ...strings(value.regionKeys)]),
|
|
169
|
+
symbolIds: uniqueStrings([...strings(value.symbolId), ...strings(value.symbolIds)]),
|
|
170
|
+
symbolNames: uniqueStrings([...strings(value.symbolName), ...strings(value.symbolNames)]),
|
|
171
|
+
memberKeys: uniqueStrings([...strings(value.memberKey), ...strings(value.memberKeys)]),
|
|
172
|
+
exportNames: uniqueStrings([...strings(value.exportName), ...strings(value.exportNames)]),
|
|
173
|
+
orderedListKeys: uniqueStrings([...strings(value.orderedListKey), ...strings(value.orderedListKeys), ...strings(value.listKey)]),
|
|
174
|
+
sourceHashes: uniqueStrings([...strings(value.sourceHash), ...strings(value.sourceHashes)])
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function normalizeSpan(span, sourcePath) {
|
|
179
|
+
if (!span || typeof span !== 'object') return undefined;
|
|
180
|
+
return compactRecord({
|
|
181
|
+
sourcePath: span.sourcePath ?? span.path ?? sourcePath,
|
|
182
|
+
start: numberOrUndefined(span.start),
|
|
183
|
+
end: numberOrUndefined(span.end),
|
|
184
|
+
startOffset: numberOrUndefined(span.startOffset ?? span.offset),
|
|
185
|
+
endOffset: numberOrUndefined(span.endOffset),
|
|
186
|
+
startLine: numberOrUndefined(span.startLine ?? span.line),
|
|
187
|
+
startColumn: numberOrUndefined(span.startColumn ?? span.column),
|
|
188
|
+
endLine: numberOrUndefined(span.endLine ?? span.line),
|
|
189
|
+
endColumn: numberOrUndefined(span.endColumn)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function uniqueSpans(spans) {
|
|
194
|
+
const seen = new Set();
|
|
195
|
+
const result = [];
|
|
196
|
+
for (const span of spans) {
|
|
197
|
+
const key = JSON.stringify(span);
|
|
198
|
+
if (seen.has(key)) continue;
|
|
199
|
+
seen.add(key);
|
|
200
|
+
result.push(span);
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|