@shapeshift-labs/frontier-lang-compiler 0.2.77 → 0.2.79
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 +16 -0
- package/bench/smoke.mjs +1 -0
- package/bench/source-change-suite.mjs +30 -1
- package/dist/declarations/bidirectional-target-change.d.ts +29 -0
- package/dist/declarations/semantic-history.d.ts +48 -1
- package/dist/index.js +1 -0
- package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +105 -6
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +25 -2
- package/dist/internal/index-impl/semanticHistoryLineageResolution.js +256 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -422,6 +422,22 @@ console.log(targetChange.metadata.semanticEquivalenceClaim); // false
|
|
|
422
422
|
|
|
423
423
|
Bidirectional target-change records are merge-admission evidence, not transpiler proof. They keep target diffs, source anchor matches, optional lineage resolutions, source patch-bundle records, semantic history records, evidence IDs, readiness, and reason codes. They always keep `autoMergeClaim: false` and `semanticEquivalenceClaim: false`; unmatched or deleted anchors block the source-port route, while matched and ambiguous anchors still require human or verifier review.
|
|
424
424
|
|
|
425
|
+
When the target source came from a Frontier/native projection, pass the generated-output `sourceMaps` back into `createBidirectionalTargetChangeRecord`. The record will match target changed regions by generated span or generated name, emit `sourceMapLinks`, include `sourceMapBackedMatches` in the summary/evidence, and carry source-map mapping IDs into the source patch bundle index:
|
|
426
|
+
|
|
427
|
+
```js
|
|
428
|
+
const sourceMapBacked = createBidirectionalTargetChangeRecord({
|
|
429
|
+
source,
|
|
430
|
+
targetLanguage: 'rust',
|
|
431
|
+
targetPath: 'src/counter.rs',
|
|
432
|
+
baseTarget,
|
|
433
|
+
editedTarget,
|
|
434
|
+
sourceMaps: [projection.sourceMap]
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
console.log(sourceMapBacked.summary.sourceMapBackedMatches);
|
|
438
|
+
console.log(sourceMapBacked.sourcePatchBundle.index.sourceMapMappingIds);
|
|
439
|
+
```
|
|
440
|
+
|
|
425
441
|
Store worker outputs as compact semantic history records when a coordinator needs to compare distributed changes without merging whole files:
|
|
426
442
|
|
|
427
443
|
```js
|
package/bench/smoke.mjs
CHANGED
|
@@ -116,6 +116,7 @@ console.log(JSON.stringify({
|
|
|
116
116
|
semanticLineageInferenceDurationMs: Number(sourceChangeMetrics.semanticLineageInferenceDurationMs.toFixed(2)),
|
|
117
117
|
bidirectionalTargetChanges: sourceChangeMetrics.bidirectionalTargetChanges,
|
|
118
118
|
bidirectionalTargetChangeMatches: sourceChangeMetrics.bidirectionalTargetChangeMatches,
|
|
119
|
+
bidirectionalTargetChangeSourceMapBacked: sourceChangeMetrics.bidirectionalTargetChangeSourceMapBacked,
|
|
119
120
|
bidirectionalTargetChangeBlocked: sourceChangeMetrics.bidirectionalTargetChangeBlocked,
|
|
120
121
|
bidirectionalTargetChangeDurationMs: Number(sourceChangeMetrics.bidirectionalTargetChangeDurationMs.toFixed(2)),
|
|
121
122
|
externalSemanticImports: sourceChangeMetrics.externalSemanticImports,
|
|
@@ -112,6 +112,8 @@ function measureBidirectionalTargetChanges() {
|
|
|
112
112
|
sourcePath: 'src/bidirectional-source.ts',
|
|
113
113
|
sourceText: 'export function advance(frame: number): number { return frame + 1; }\n'
|
|
114
114
|
});
|
|
115
|
+
const sourceSymbol = source.semanticIndex.symbols.find((symbol) => symbol.name === 'advance');
|
|
116
|
+
const sourceMapping = source.sourceMaps[0].mappings.find((mapping) => mapping.semanticSymbolId === sourceSymbol.id);
|
|
115
117
|
const lineage = [createSemanticLineageEvent({
|
|
116
118
|
eventKind: 'moved',
|
|
117
119
|
from: { key: 'source#src/bidirectional-source.ts#body#advance', symbolName: 'advance' },
|
|
@@ -134,18 +136,45 @@ function measureBidirectionalTargetChanges() {
|
|
|
134
136
|
sourcePath: `src/bidirectional-${index}.rs`,
|
|
135
137
|
sourceText: `pub fn advance(frame: i32, delta: i32) -> i32 { frame + delta + ${index} }\n`
|
|
136
138
|
},
|
|
137
|
-
|
|
139
|
+
...(index % 2 === 0
|
|
140
|
+
? { sourceMaps: [rustSourceMap(source, sourceSymbol, sourceMapping, index)] }
|
|
141
|
+
: { sourceAnchorMappings: [{ targetSymbolName: 'advance', sourceSymbolName: 'advance' }] }),
|
|
138
142
|
lineage: index % 4 === 0 ? lineage : []
|
|
139
143
|
}));
|
|
140
144
|
}
|
|
141
145
|
return {
|
|
142
146
|
bidirectionalTargetChanges: records.length,
|
|
143
147
|
bidirectionalTargetChangeMatches: records.reduce((sum, record) => sum + record.summary.sourceAnchorMatches, 0),
|
|
148
|
+
bidirectionalTargetChangeSourceMapBacked: records.reduce((sum, record) => sum + record.summary.sourceMapBackedMatches, 0),
|
|
144
149
|
bidirectionalTargetChangeBlocked: records.filter((record) => record.readiness === 'blocked').length,
|
|
145
150
|
bidirectionalTargetChangeDurationMs: performance.now() - start
|
|
146
151
|
};
|
|
147
152
|
}
|
|
148
153
|
|
|
154
|
+
function rustSourceMap(source, sourceSymbol, sourceMapping, index) {
|
|
155
|
+
const targetPath = `src/bidirectional-${index}.rs`;
|
|
156
|
+
return {
|
|
157
|
+
kind: 'frontier.lang.sourceMap',
|
|
158
|
+
version: 1,
|
|
159
|
+
id: `bench_source_map_bidirectional_${index}`,
|
|
160
|
+
sourcePath: source.sourcePath,
|
|
161
|
+
sourceHash: source.nativeSource.sourceHash,
|
|
162
|
+
target: 'rust',
|
|
163
|
+
targetPath,
|
|
164
|
+
mappings: [{
|
|
165
|
+
id: `bench_map_advance_${index}`,
|
|
166
|
+
semanticSymbolId: sourceSymbol.id,
|
|
167
|
+
nativeAstNodeId: sourceSymbol.nativeAstNodeId,
|
|
168
|
+
sourceSpan: sourceMapping.sourceSpan,
|
|
169
|
+
generatedSpan: { path: targetPath, target: 'rust', targetPath, startLine: 1, startColumn: 1, endLine: 1, endColumn: 80, generatedName: 'advance' },
|
|
170
|
+
target: 'rust',
|
|
171
|
+
generatedName: 'advance',
|
|
172
|
+
precision: 'declaration',
|
|
173
|
+
preservation: 'declaration'
|
|
174
|
+
}]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
149
178
|
function measureExternalSemanticImports() {
|
|
150
179
|
const externalSemanticStart = performance.now();
|
|
151
180
|
const externalSemanticImports = [];
|
|
@@ -2,6 +2,8 @@ import type {
|
|
|
2
2
|
EvidenceRecord,
|
|
3
3
|
FrontierSourceLanguage,
|
|
4
4
|
SemanticMergeReadiness,
|
|
5
|
+
SourceMapMappingRecord,
|
|
6
|
+
SourceMapRecord,
|
|
5
7
|
SourceSpan
|
|
6
8
|
} from '@shapeshift-labs/frontier-lang-kernel';
|
|
7
9
|
import type { ImportNativeSourceOptions, NativeSourceImportResult } from './import-adapter-core.js';
|
|
@@ -27,6 +29,26 @@ export interface BidirectionalTargetChangeSourceAnchorMapping {
|
|
|
27
29
|
readonly sourceSymbolId?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
export interface BidirectionalTargetChangeSourceMapLink {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly sourceMapId?: string;
|
|
35
|
+
readonly sourceMapMappingId?: string;
|
|
36
|
+
readonly sourcePath?: string;
|
|
37
|
+
readonly sourceHash?: string;
|
|
38
|
+
readonly targetPath?: string;
|
|
39
|
+
readonly targetHash?: string;
|
|
40
|
+
readonly semanticSymbolId?: string;
|
|
41
|
+
readonly semanticOccurrenceId?: string;
|
|
42
|
+
readonly semanticNodeId?: string;
|
|
43
|
+
readonly nativeSourceId?: string;
|
|
44
|
+
readonly nativeAstNodeId?: string;
|
|
45
|
+
readonly precision?: string;
|
|
46
|
+
readonly sourceSpan?: SourceSpan;
|
|
47
|
+
readonly generatedSpan?: SourceMapMappingRecord['generatedSpan'];
|
|
48
|
+
readonly regionKey?: string;
|
|
49
|
+
readonly regionKind?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
30
52
|
export interface BidirectionalTargetChangeAnchor {
|
|
31
53
|
readonly id?: string;
|
|
32
54
|
readonly key?: string;
|
|
@@ -47,6 +69,7 @@ export interface BidirectionalTargetChangeSourceAnchorMatch {
|
|
|
47
69
|
readonly targetRegion: Partial<NativeSourceChangeRegion>;
|
|
48
70
|
readonly sourceAnchors: readonly BidirectionalTargetChangeAnchor[];
|
|
49
71
|
readonly lineageResolutions: readonly SemanticLineageResolution[];
|
|
72
|
+
readonly sourceMapLinks: readonly BidirectionalTargetChangeSourceMapLink[];
|
|
50
73
|
readonly status: BidirectionalTargetChangeAnchorStatus;
|
|
51
74
|
readonly confidence?: number;
|
|
52
75
|
readonly reasonCodes: readonly string[];
|
|
@@ -77,6 +100,11 @@ export interface CreateBidirectionalTargetChangeRecordOptions {
|
|
|
77
100
|
readonly after?: NativeSourceImportResult | ImportNativeSourceOptions;
|
|
78
101
|
readonly sourceAnchorMappings?: readonly BidirectionalTargetChangeSourceAnchorMapping[];
|
|
79
102
|
readonly anchorMappings?: readonly BidirectionalTargetChangeSourceAnchorMapping[];
|
|
103
|
+
readonly sourceMaps?: readonly SourceMapRecord[];
|
|
104
|
+
readonly projectionSourceMaps?: readonly SourceMapRecord[];
|
|
105
|
+
readonly targetSourceMaps?: readonly SourceMapRecord[];
|
|
106
|
+
readonly targetProjectionSourceMaps?: readonly SourceMapRecord[];
|
|
107
|
+
readonly targetCompileResult?: { readonly sourceMaps?: readonly SourceMapRecord[]; readonly sourceMap?: SourceMapRecord };
|
|
80
108
|
readonly lineage?: readonly SemanticLineageEvent[];
|
|
81
109
|
readonly lineageEvents?: readonly SemanticLineageEvent[];
|
|
82
110
|
readonly lineageMap?: unknown;
|
|
@@ -114,6 +142,7 @@ export interface BidirectionalTargetChangeRecord {
|
|
|
114
142
|
readonly unmatchedTargetRegions: number;
|
|
115
143
|
readonly deletedSourceAnchors: number;
|
|
116
144
|
readonly sourceChangedRegions: number;
|
|
145
|
+
readonly sourceMapBackedMatches: number;
|
|
117
146
|
};
|
|
118
147
|
readonly metadata: {
|
|
119
148
|
readonly autoMergeClaim: false;
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
SourceSpan
|
|
7
7
|
} from '@shapeshift-labs/frontier-lang-kernel';
|
|
8
8
|
import type { SemanticImportOwnershipRegion } from './semantic-sidecar.js';
|
|
9
|
-
import type { CreateSemanticLineageEventInput, SemanticLineageEvent } from './semantic-lineage.js';
|
|
9
|
+
import type { CreateSemanticLineageEventInput, SemanticAnchor, SemanticLineageEvent, SemanticLineageMap, SemanticLineageResolution } from './semantic-lineage.js';
|
|
10
10
|
|
|
11
11
|
import type { SemanticHistoryAdmissionStatus, SemanticHistoryReviewerStatus, SemanticHistoryReplayLinkKind, SemanticHistoryOverlapKind, SemanticHistoryConflictReason, SemanticHistoryClaimKind, SemanticHistoryClaimStatus, SemanticHistoryProofAttemptStatus, SemanticHistoryMergeDecisionStatus, SemanticHistoryActorRef, SemanticHistoryRecordSourceRef, SemanticHistorySourceRef, SemanticHistoryOwnershipRegionRef, SemanticHistoryCandidateRef, SemanticHistoryClaimRecord, SemanticHistoryImportedParserEvidenceRecord, SemanticHistoryProofAttemptRecord, SemanticHistoryPatchAncestryRecord, SemanticHistoryMergeDecisionRecord, SemanticHistoryClaimInput, SemanticHistoryImportedParserEvidenceInput, SemanticHistoryProofAttemptInput, SemanticHistoryPatchAncestryInput, SemanticHistoryMergeDecisionInput, SemanticHistoryReviewerState, SemanticHistoryAdmissionState, SemanticHistoryReplayLink, SemanticHistoryRecordIndex } from './semantic-history-records.js';
|
|
12
12
|
export type { SemanticHistoryAdmissionStatus, SemanticHistoryReviewerStatus, SemanticHistoryReplayLinkKind, SemanticHistoryOverlapKind, SemanticHistoryConflictReason, SemanticHistoryClaimKind, SemanticHistoryClaimStatus, SemanticHistoryProofAttemptStatus, SemanticHistoryMergeDecisionStatus, SemanticHistoryActorRef, SemanticHistoryRecordSourceRef, SemanticHistorySourceRef, SemanticHistoryOwnershipRegionRef, SemanticHistoryCandidateRef, SemanticHistoryClaimRecord, SemanticHistoryImportedParserEvidenceRecord, SemanticHistoryProofAttemptRecord, SemanticHistoryPatchAncestryRecord, SemanticHistoryMergeDecisionRecord, SemanticHistoryClaimInput, SemanticHistoryImportedParserEvidenceInput, SemanticHistoryProofAttemptInput, SemanticHistoryPatchAncestryInput, SemanticHistoryMergeDecisionInput, SemanticHistoryReviewerState, SemanticHistoryAdmissionState, SemanticHistoryReplayLink, SemanticHistoryRecordIndex } from './semantic-history-records.js';
|
|
@@ -138,6 +138,51 @@ export interface SemanticHistoryOverlapRecord {
|
|
|
138
138
|
readonly reviewer: { readonly left?: SemanticHistoryReviewerStatus; readonly right?: SemanticHistoryReviewerStatus };
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
export type SemanticHistoryLineageAnchorSetKind = 'active' | 'candidate' | 'inactive' | 'deleted' | 'unresolved' | 'blocked';
|
|
142
|
+
|
|
143
|
+
export interface SemanticHistoryLineageAnchorRecord extends Pick<SemanticAnchor, 'key' | 'id' | 'kind' | 'language' | 'sourcePath' | 'symbolId' | 'symbolName'> {
|
|
144
|
+
readonly status: string;
|
|
145
|
+
readonly resolutionId: string;
|
|
146
|
+
readonly reasonCodes: readonly string[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export type SemanticHistoryLineageAnchorInventory = Readonly<Record<SemanticHistoryLineageAnchorSetKind, readonly SemanticHistoryLineageAnchorRecord[]>>;
|
|
150
|
+
|
|
151
|
+
export interface SemanticHistoryRecordLineageResolution {
|
|
152
|
+
readonly kind: 'frontier.lang.semanticHistoryRecordLineageResolution';
|
|
153
|
+
readonly version: 1;
|
|
154
|
+
readonly id: string;
|
|
155
|
+
readonly stableId: string;
|
|
156
|
+
readonly hash: string;
|
|
157
|
+
readonly sourceRecordId?: string;
|
|
158
|
+
readonly sourceRecordStableId?: string;
|
|
159
|
+
readonly sourceRecordHash?: string;
|
|
160
|
+
readonly generatedAt: number | string;
|
|
161
|
+
readonly resolutions: readonly SemanticLineageResolution[];
|
|
162
|
+
readonly anchorInventory: SemanticHistoryLineageAnchorInventory;
|
|
163
|
+
readonly resolvedRecord: SemanticHistoryRecord;
|
|
164
|
+
readonly summary: Record<string, unknown> & {
|
|
165
|
+
readonly anchorCount: number;
|
|
166
|
+
readonly currentAnchorKeys: readonly string[];
|
|
167
|
+
readonly ownershipKeys: readonly string[];
|
|
168
|
+
readonly conflictKeys: readonly string[];
|
|
169
|
+
readonly activeAnchorKeys: readonly string[];
|
|
170
|
+
readonly candidateAnchorKeys: readonly string[];
|
|
171
|
+
readonly inactiveAnchorKeys: readonly string[];
|
|
172
|
+
readonly deletedAnchorKeys: readonly string[];
|
|
173
|
+
readonly unresolvedAnchorKeys: readonly string[];
|
|
174
|
+
readonly blockedAnchorKeys: readonly string[];
|
|
175
|
+
readonly reasonCodes: readonly string[];
|
|
176
|
+
};
|
|
177
|
+
readonly admission: {
|
|
178
|
+
readonly readiness: SemanticMergeReadiness | string;
|
|
179
|
+
readonly reviewRequired: boolean;
|
|
180
|
+
readonly autoMergeClaim: false;
|
|
181
|
+
readonly semanticEquivalenceClaim: false;
|
|
182
|
+
};
|
|
183
|
+
readonly metadata?: Record<string, unknown>;
|
|
184
|
+
}
|
|
185
|
+
|
|
141
186
|
export declare const SemanticHistoryAdmissionStatuses: readonly SemanticHistoryAdmissionStatus[];
|
|
142
187
|
export declare const SemanticHistoryReviewerStatuses: readonly SemanticHistoryReviewerStatus[];
|
|
143
188
|
export declare const SemanticHistoryOverlapKinds: readonly SemanticHistoryOverlapKind[];
|
|
@@ -146,3 +191,5 @@ export declare function createSemanticHistoryRecord(input?: CreateSemanticHistor
|
|
|
146
191
|
export declare function querySemanticHistoryRecordOverlaps(records: SemanticHistoryRecord | readonly SemanticHistoryRecord[], options?: SemanticHistoryOverlapQueryOptions): readonly SemanticHistoryOverlapRecord[];
|
|
147
192
|
export declare function semanticHistoryRecordsOverlap(left: SemanticHistoryRecord, right: SemanticHistoryRecord, options?: SemanticHistoryOverlapQueryOptions): boolean;
|
|
148
193
|
export declare function semanticHistoryRecordsConflict(left: SemanticHistoryRecord, right: SemanticHistoryRecord, options?: SemanticHistoryOverlapQueryOptions): boolean;
|
|
194
|
+
export declare function resolveSemanticHistoryRecordLineage(record: SemanticHistoryRecord, eventsOrMap?: SemanticLineageMap | readonly CreateSemanticLineageEventInput[], options?: { readonly id?: string; readonly generatedAt?: number | string; readonly maxDepth?: number; readonly keepDeletedAnchors?: boolean; readonly keepCandidateAnchors?: boolean; readonly keepBlockedAnchors?: boolean; readonly keepUnresolvedAnchors?: boolean; readonly metadata?: Record<string, unknown> }): SemanticHistoryRecordLineageResolution;
|
|
195
|
+
export declare function resolveSemanticHistoryRecordsLineage(records: readonly SemanticHistoryRecord[] | SemanticHistoryRecord, eventsOrMap?: SemanticLineageMap | readonly CreateSemanticLineageEventInput[], options?: { readonly generatedAt?: number | string; readonly maxDepth?: number; readonly keepDeletedAnchors?: boolean; readonly keepCandidateAnchors?: boolean; readonly keepBlockedAnchors?: boolean; readonly keepUnresolvedAnchors?: boolean; readonly metadata?: Record<string, unknown> }): readonly SemanticHistoryRecordLineageResolution[];
|
package/dist/index.js
CHANGED
|
@@ -73,6 +73,7 @@ export { queryUniversalConversionPlan } from './internal/index-impl/queryUnivers
|
|
|
73
73
|
export { createSemanticAnchor, createSemanticLineageEvent, createSemanticLineageMap, querySemanticLineageEvents, SemanticLineageEventKinds } from './internal/index-impl/semanticLineageRecords.js';
|
|
74
74
|
export { resolveSemanticLineage, resolveSemanticLineageBatch, SemanticLineageResolutionStatuses } from './internal/index-impl/semanticLineageResolutionRecords.js';
|
|
75
75
|
export { createSemanticHistoryRecord, querySemanticHistoryRecordOverlaps, SemanticHistoryAdmissionStatuses, SemanticHistoryConflictReasons, SemanticHistoryOverlapKinds, SemanticHistoryReviewerStatuses, semanticHistoryRecordsConflict, semanticHistoryRecordsOverlap } from './internal/index-impl/semanticHistoryRecords.js';
|
|
76
|
+
export { resolveSemanticHistoryRecordLineage, resolveSemanticHistoryRecordsLineage } from './internal/index-impl/semanticHistoryLineageResolution.js';
|
|
76
77
|
export { readSemanticSliceJson } from './internal/index-impl/readSemanticSliceJson.js';
|
|
77
78
|
export { readUniversalAstJson } from './internal/index-impl/readUniversalAstJson.js';
|
|
78
79
|
export { renderTargetAst } from './internal/index-impl/renderTargetAst.js';
|
|
@@ -8,13 +8,14 @@ import { resolveSemanticLineage } from './semanticLineageResolutionRecords.js';
|
|
|
8
8
|
|
|
9
9
|
export function matchTargetRegion(context) {
|
|
10
10
|
const explicit = explicitMappingForRegion(context.region, context.mappings);
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const mapped = explicit ? { anchors: sourceAnchorsForMapping(explicit, context.sourceAnchors), links: [] }
|
|
12
|
+
: sourceMapAnchorsForRegion(context.region, context.sourceMaps, context.sourceAnchors);
|
|
13
|
+
const candidateAnchors = mapped.anchors.length ? mapped.anchors : sourceAnchorsByName(context.region, context.sourceAnchors);
|
|
14
14
|
const resolvedAnchors = candidateAnchors.flatMap((anchor) => resolveAnchor(anchor, context.lineage));
|
|
15
15
|
const status = matchStatus(candidateAnchors, resolvedAnchors);
|
|
16
16
|
const reasonCodes = uniqueStrings([
|
|
17
|
-
explicit ? 'explicit-anchor-mapping' : 'inferred-by-symbol-name',
|
|
17
|
+
explicit ? 'explicit-anchor-mapping' : mapped.links.length ? 'source-map-generated-anchor' : 'inferred-by-symbol-name',
|
|
18
|
+
mapped.links.length ? 'mapped-by-generated-source-map' : undefined,
|
|
18
19
|
status === 'unmatched' ? 'source-anchor-not-found' : undefined,
|
|
19
20
|
status === 'ambiguous' ? 'source-anchor-ambiguous' : undefined,
|
|
20
21
|
status === 'deleted' ? 'source-anchor-deleted' : undefined,
|
|
@@ -27,8 +28,9 @@ export function matchTargetRegion(context) {
|
|
|
27
28
|
targetRegion: compactRegion(context.region),
|
|
28
29
|
sourceAnchors: resolvedAnchors.map((entry) => entry.anchor),
|
|
29
30
|
lineageResolutions: uniqueRecordsById(resolvedAnchors.map((entry) => entry.resolution).filter(Boolean)),
|
|
31
|
+
sourceMapLinks: mapped.links,
|
|
30
32
|
status,
|
|
31
|
-
confidence: status === 'matched' && explicit ? 0.8 : status === 'matched' ? 0.58 : undefined,
|
|
33
|
+
confidence: status === 'matched' && explicit ? 0.8 : status === 'matched' && mapped.links.length ? 0.72 : status === 'matched' ? 0.58 : undefined,
|
|
32
34
|
reasonCodes,
|
|
33
35
|
reviewRequired: true,
|
|
34
36
|
autoMergeClaim: false,
|
|
@@ -37,6 +39,7 @@ export function matchTargetRegion(context) {
|
|
|
37
39
|
context.region.conflictKey,
|
|
38
40
|
context.region.key,
|
|
39
41
|
...resolvedAnchors.map((entry) => entry.anchor?.key),
|
|
42
|
+
...mapped.links.map((link) => link.sourceMapMappingId && `source-map:${link.sourceMapMappingId}`),
|
|
40
43
|
status === 'unmatched' ? `target:${context.region.sourcePath ?? context.region.language ?? 'unknown'}` : undefined
|
|
41
44
|
])
|
|
42
45
|
};
|
|
@@ -65,6 +68,7 @@ export function sourceRegionsForMatch(match, readiness) {
|
|
|
65
68
|
symbolId: anchor?.symbolId,
|
|
66
69
|
symbolName: anchor?.symbolName,
|
|
67
70
|
sourceSpan: anchor?.sourceSpan,
|
|
71
|
+
sourceMapLinks: match.sourceMapLinks,
|
|
68
72
|
admission: {
|
|
69
73
|
readiness,
|
|
70
74
|
action: 'review-port-from-target-change',
|
|
@@ -75,6 +79,7 @@ export function sourceRegionsForMatch(match, readiness) {
|
|
|
75
79
|
bidirectionalTargetChange: {
|
|
76
80
|
matchId: match.id,
|
|
77
81
|
targetRegion: match.targetRegion,
|
|
82
|
+
sourceMapLinkIds: match.sourceMapLinks.map((link) => link.id),
|
|
78
83
|
lineageResolutionIds: match.lineageResolutions.map((resolution) => resolution.id),
|
|
79
84
|
reviewRequired: true,
|
|
80
85
|
autoMergeClaim: false,
|
|
@@ -97,6 +102,8 @@ export function createBidirectionalEvidence(context) {
|
|
|
97
102
|
targetChangeSetId: context.targetChangeSet.id,
|
|
98
103
|
targetPatchId: context.targetChangeSet.patch?.id,
|
|
99
104
|
sourceAnchorMatchIds: context.sourceAnchorMatches.map((match) => match.id),
|
|
105
|
+
sourceMapBackedMatches: context.sourceAnchorMatches.filter((match) => match.sourceMapLinks.length > 0).length,
|
|
106
|
+
sourceMapLinkIds: context.sourceAnchorMatches.flatMap((match) => match.sourceMapLinks.map((link) => link.id)),
|
|
100
107
|
readiness: context.readiness,
|
|
101
108
|
reasons: context.reasons,
|
|
102
109
|
autoMergeClaim: false,
|
|
@@ -105,6 +112,77 @@ export function createBidirectionalEvidence(context) {
|
|
|
105
112
|
};
|
|
106
113
|
}
|
|
107
114
|
|
|
115
|
+
function sourceMapAnchorsForRegion(region, sourceMaps, sourceAnchors) {
|
|
116
|
+
const links = [];
|
|
117
|
+
const anchors = [];
|
|
118
|
+
for (const sourceMap of sourceMaps ?? []) {
|
|
119
|
+
for (const mapping of sourceMap?.mappings ?? []) {
|
|
120
|
+
if (!mappingMatchesTargetRegion(mapping, sourceMap, region)) continue;
|
|
121
|
+
const link = sourceMapLinkForMapping(sourceMap, mapping);
|
|
122
|
+
links.push(link);
|
|
123
|
+
anchors.push(...anchorsForSourceMapMapping(mapping, link, sourceAnchors));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { anchors: uniqueRecordsById(anchors), links: uniqueRecordsById(links) };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function anchorsForSourceMapMapping(mapping, link, sourceAnchors) {
|
|
130
|
+
const matches = sourceAnchors.filter((anchor) =>
|
|
131
|
+
(mapping.semanticSymbolId && anchor.symbolId === mapping.semanticSymbolId)
|
|
132
|
+
|| (mapping.semanticNodeId && anchor.id === mapping.semanticNodeId)
|
|
133
|
+
|| (mapping.nativeAstNodeId && anchor.metadata?.nativeAstNodeId === mapping.nativeAstNodeId)
|
|
134
|
+
|| (mapping.ownershipRegionKey && anchor.key === mapping.ownershipRegionKey)
|
|
135
|
+
);
|
|
136
|
+
if (matches.length) return matches;
|
|
137
|
+
return [compactRecord({
|
|
138
|
+
id: mapping.semanticSymbolId ?? mapping.semanticNodeId ?? mapping.nativeAstNodeId ?? link.id,
|
|
139
|
+
key: mapping.ownershipRegionKey ?? mapping.semanticSymbolId ?? mapping.semanticNodeId ?? link.id,
|
|
140
|
+
kind: mapping.ownershipRegionKind ?? 'source-map',
|
|
141
|
+
language: link.sourceSpan?.language,
|
|
142
|
+
sourcePath: link.sourcePath,
|
|
143
|
+
sourceHash: link.sourceHash,
|
|
144
|
+
symbolId: mapping.semanticSymbolId,
|
|
145
|
+
symbolName: mapping.generatedName ?? mapping.generatedSpan?.generatedName,
|
|
146
|
+
sourceSpan: mapping.sourceSpan,
|
|
147
|
+
metadata: { sourceMapId: link.sourceMapId, sourceMapMappingId: link.sourceMapMappingId }
|
|
148
|
+
})];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function mappingMatchesTargetRegion(mapping, sourceMap, region) {
|
|
152
|
+
if (!targetPathMatches(mapping, sourceMap, region)) return false;
|
|
153
|
+
if (mapping.generatedName && namesForRegion(region).includes(mapping.generatedName)) return true;
|
|
154
|
+
if (mapping.generatedSpan?.generatedName && namesForRegion(region).includes(mapping.generatedSpan.generatedName)) return true;
|
|
155
|
+
return spansOverlap(mapping.generatedSpan, region.sourceSpan);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function targetPathMatches(mapping, sourceMap, region) {
|
|
159
|
+
const targetPaths = uniqueStrings([mapping.generatedSpan?.targetPath, mapping.generatedSpan?.path, sourceMap?.targetPath]);
|
|
160
|
+
const regionPath = region.sourcePath ?? region.sourceSpan?.path;
|
|
161
|
+
return !regionPath || targetPaths.length === 0 || targetPaths.includes(regionPath);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function sourceMapLinkForMapping(sourceMap, mapping) {
|
|
165
|
+
return compactRecord({
|
|
166
|
+
id: `source_map_link_${idFragment(sourceMap?.id ?? 'map')}_${idFragment(mapping.id ?? mapping.semanticSymbolId ?? 'mapping')}`,
|
|
167
|
+
sourceMapId: sourceMap?.id,
|
|
168
|
+
sourceMapMappingId: mapping.id,
|
|
169
|
+
sourcePath: mapping.sourceSpan?.path ?? sourceMap?.sourcePath,
|
|
170
|
+
sourceHash: mapping.sourceSpan?.sourceId ?? sourceMap?.sourceHash,
|
|
171
|
+
targetPath: mapping.generatedSpan?.targetPath ?? sourceMap?.targetPath,
|
|
172
|
+
targetHash: mapping.generatedSpan?.targetHash ?? sourceMap?.targetHash,
|
|
173
|
+
semanticSymbolId: mapping.semanticSymbolId,
|
|
174
|
+
semanticOccurrenceId: mapping.semanticOccurrenceId,
|
|
175
|
+
semanticNodeId: mapping.semanticNodeId,
|
|
176
|
+
nativeSourceId: mapping.nativeSourceId,
|
|
177
|
+
nativeAstNodeId: mapping.nativeAstNodeId,
|
|
178
|
+
precision: mapping.precision,
|
|
179
|
+
sourceSpan: mapping.sourceSpan,
|
|
180
|
+
generatedSpan: mapping.generatedSpan,
|
|
181
|
+
regionKey: mapping.ownershipRegionKey,
|
|
182
|
+
regionKind: mapping.ownershipRegionKind
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
108
186
|
export function anchorsFromSourceSidecar(sidecar, source) {
|
|
109
187
|
return uniqueRecordsById((sidecar.ownershipRegions ?? []).map((region) => compactRecord({
|
|
110
188
|
id: region.id,
|
|
@@ -148,11 +226,15 @@ function sourceAnchorsForMapping(mapping, sourceAnchors) {
|
|
|
148
226
|
}
|
|
149
227
|
|
|
150
228
|
function sourceAnchorsByName(region, sourceAnchors) {
|
|
151
|
-
const names =
|
|
229
|
+
const names = namesForRegion(region);
|
|
152
230
|
if (names.length === 0) return [];
|
|
153
231
|
return sourceAnchors.filter((anchor) => names.includes(anchor.symbolName));
|
|
154
232
|
}
|
|
155
233
|
|
|
234
|
+
function namesForRegion(region) {
|
|
235
|
+
return uniqueStrings([region.symbolName, region.name, region.metadata?.changedRegionProjection?.region?.symbolName]);
|
|
236
|
+
}
|
|
237
|
+
|
|
156
238
|
function resolveAnchor(anchor, lineage) {
|
|
157
239
|
if (!anchor) return [];
|
|
158
240
|
const resolution = array(lineage).length ? resolveSemanticLineage(lineage, { anchorKey: anchor.key }) : undefined;
|
|
@@ -189,6 +271,23 @@ function compactRegion(region) {
|
|
|
189
271
|
});
|
|
190
272
|
}
|
|
191
273
|
|
|
274
|
+
function spansOverlap(left, right) {
|
|
275
|
+
if (!left || !right) return false;
|
|
276
|
+
if (!sourcePathsCompatible(left, right)) return false;
|
|
277
|
+
const leftStart = Number(left.startLine ?? 0);
|
|
278
|
+
const leftEnd = Number(left.endLine ?? left.startLine ?? 0);
|
|
279
|
+
const rightStart = Number(right.startLine ?? 0);
|
|
280
|
+
const rightEnd = Number(right.endLine ?? right.startLine ?? 0);
|
|
281
|
+
if (!leftStart || !rightStart) return false;
|
|
282
|
+
return leftStart <= rightEnd && rightStart <= leftEnd;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function sourcePathsCompatible(left, right) {
|
|
286
|
+
const leftPath = left.targetPath ?? left.path;
|
|
287
|
+
const rightPath = right.targetPath ?? right.path;
|
|
288
|
+
return !leftPath || !rightPath || leftPath === rightPath;
|
|
289
|
+
}
|
|
290
|
+
|
|
192
291
|
function array(value) {
|
|
193
292
|
return value === undefined || value === null ? [] : Array.isArray(value) ? value : [value];
|
|
194
293
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
idFragment,
|
|
3
|
+
uniqueRecordsById,
|
|
3
4
|
uniqueStrings
|
|
4
5
|
} from '../../native-import-utils.js';
|
|
5
6
|
import { createSemanticImportSidecar } from './createSemanticImportSidecar.js';
|
|
@@ -45,6 +46,14 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
|
|
|
45
46
|
}) : undefined;
|
|
46
47
|
const sourceAnchors = sourceSidecar ? anchorsFromSourceSidecar(sourceSidecar, source) : [];
|
|
47
48
|
const mappings = array(input.sourceAnchorMappings ?? input.anchorMappings);
|
|
49
|
+
const sourceMaps = uniqueRecordsById([
|
|
50
|
+
...array(input.sourceMaps),
|
|
51
|
+
...array(input.projectionSourceMaps),
|
|
52
|
+
...array(input.targetSourceMaps),
|
|
53
|
+
...array(input.targetProjectionSourceMaps),
|
|
54
|
+
...array(input.targetCompileResult?.sourceMaps),
|
|
55
|
+
input.targetCompileResult?.sourceMap
|
|
56
|
+
]);
|
|
48
57
|
const lineage = input.lineage ?? input.lineageEvents ?? input.lineageMap ?? [];
|
|
49
58
|
const sourceAnchorMatches = targetChangeSet.changedRegions.map((region, index) => matchTargetRegion({
|
|
50
59
|
id,
|
|
@@ -53,6 +62,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
|
|
|
53
62
|
source,
|
|
54
63
|
sourceAnchors,
|
|
55
64
|
mappings,
|
|
65
|
+
sourceMaps,
|
|
56
66
|
lineage
|
|
57
67
|
}));
|
|
58
68
|
const readiness = classifyBidirectionalReadiness(targetChangeSet, source, sourceAnchorMatches);
|
|
@@ -86,7 +96,8 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
|
|
|
86
96
|
projectionOnly: true,
|
|
87
97
|
targetChangeSetId: targetChangeSet.id,
|
|
88
98
|
targetPatchId: targetChangeSet.patch?.id,
|
|
89
|
-
targetMergeCandidateId: targetChangeSet.mergeCandidate?.id
|
|
99
|
+
targetMergeCandidateId: targetChangeSet.mergeCandidate?.id,
|
|
100
|
+
sourceMapBackprojection: summarizeSourceMapBackprojection(sourceAnchorMatches)
|
|
90
101
|
}
|
|
91
102
|
}, {
|
|
92
103
|
id: input.sourcePatchBundleId ?? `semantic_patch_bundle_${idFragment(id)}_source_port`,
|
|
@@ -127,6 +138,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
|
|
|
127
138
|
bidirectionalTargetChangeId: id,
|
|
128
139
|
sourcePatchBundleId: sourcePatchBundle.id,
|
|
129
140
|
targetChangeSetId: targetChangeSet.id,
|
|
141
|
+
sourceMapBackprojection: summarizeSourceMapBackprojection(sourceAnchorMatches),
|
|
130
142
|
autoMergeClaim: false,
|
|
131
143
|
semanticEquivalenceClaim: false
|
|
132
144
|
}
|
|
@@ -153,7 +165,8 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
|
|
|
153
165
|
ambiguousMatches: sourceAnchorMatches.filter((match) => match.status === 'ambiguous').length,
|
|
154
166
|
unmatchedTargetRegions: sourceAnchorMatches.filter((match) => match.status === 'unmatched').length,
|
|
155
167
|
deletedSourceAnchors: sourceAnchorMatches.filter((match) => match.status === 'deleted').length,
|
|
156
|
-
sourceChangedRegions: sourceChangedRegions.length
|
|
168
|
+
sourceChangedRegions: sourceChangedRegions.length,
|
|
169
|
+
sourceMapBackedMatches: sourceAnchorMatches.filter((match) => match.sourceMapLinks.length > 0).length
|
|
157
170
|
},
|
|
158
171
|
metadata: {
|
|
159
172
|
autoMergeClaim: false,
|
|
@@ -164,6 +177,16 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
|
|
|
164
177
|
};
|
|
165
178
|
}
|
|
166
179
|
|
|
180
|
+
function summarizeSourceMapBackprojection(matches) {
|
|
181
|
+
const links = matches.flatMap((match) => match.sourceMapLinks ?? []);
|
|
182
|
+
return {
|
|
183
|
+
sourceMapBackedMatches: matches.filter((match) => (match.sourceMapLinks ?? []).length > 0).length,
|
|
184
|
+
sourceMapLinks: links.length,
|
|
185
|
+
sourceMapIds: uniqueStrings(links.map((link) => link.sourceMapId)),
|
|
186
|
+
sourceMapMappingIds: uniqueStrings(links.map((link) => link.sourceMapMappingId))
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
167
190
|
function array(value) {
|
|
168
191
|
return value === undefined || value === null ? [] : Array.isArray(value) ? value : [value];
|
|
169
192
|
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { idFragment, uniqueStrings as uniqueRawStrings } from '../../native-import-utils.js';
|
|
3
|
+
import { resolveSemanticLineage } from './semanticLineageResolutionRecords.js';
|
|
4
|
+
|
|
5
|
+
export function resolveSemanticHistoryRecordLineage(record = {}, eventsOrMap = [], options = {}) {
|
|
6
|
+
const generatedAt = options.generatedAt ?? Date.now();
|
|
7
|
+
const sourceIndex = record.index ?? {};
|
|
8
|
+
const anchorKeys = uniqueStrings([
|
|
9
|
+
...array(sourceIndex.semanticAnchorKeys),
|
|
10
|
+
...array(sourceIndex.ownershipKeys),
|
|
11
|
+
...array(sourceIndex.conflictKeys)
|
|
12
|
+
]);
|
|
13
|
+
const resolutions = anchorKeys.map((anchorKey) => resolveSemanticLineage(eventsOrMap, {
|
|
14
|
+
anchorKey,
|
|
15
|
+
generatedAt
|
|
16
|
+
}, { maxDepth: options.maxDepth }));
|
|
17
|
+
const byAnchorKey = new Map(resolutions.map((resolution) => [resolution.query.anchorKey, resolution]));
|
|
18
|
+
const resolvedIndex = {
|
|
19
|
+
...sourceIndex,
|
|
20
|
+
ownershipKeys: resolveIndexKeys(sourceIndex.ownershipKeys, byAnchorKey, options),
|
|
21
|
+
conflictKeys: resolveIndexKeys(sourceIndex.conflictKeys, byAnchorKey, options),
|
|
22
|
+
semanticAnchorKeys: resolveIndexKeys(sourceIndex.semanticAnchorKeys, byAnchorKey, options),
|
|
23
|
+
sourcePaths: uniqueStrings([
|
|
24
|
+
...array(sourceIndex.sourcePaths),
|
|
25
|
+
...resolutions.flatMap((resolution) => resolution.currentAnchors.map((anchor) => anchor.sourcePath))
|
|
26
|
+
]),
|
|
27
|
+
lineageEventIds: uniqueStrings([
|
|
28
|
+
...array(sourceIndex.lineageEventIds),
|
|
29
|
+
...resolutions.flatMap((resolution) => resolution.traversedEventIds)
|
|
30
|
+
]),
|
|
31
|
+
semanticLineageKinds: uniqueStrings([
|
|
32
|
+
...array(sourceIndex.semanticLineageKinds),
|
|
33
|
+
...resolutions.flatMap((resolution) => resolution.lineageEventKinds)
|
|
34
|
+
]),
|
|
35
|
+
crdtOperationIds: uniqueStrings([
|
|
36
|
+
...array(sourceIndex.crdtOperationIds),
|
|
37
|
+
...resolutions.flatMap((resolution) => resolution.crdtOperationIds)
|
|
38
|
+
]),
|
|
39
|
+
crdtHeads: uniqueStrings([
|
|
40
|
+
...array(sourceIndex.crdtHeads),
|
|
41
|
+
...resolutions.flatMap((resolution) => resolution.crdtHeads)
|
|
42
|
+
]),
|
|
43
|
+
evidenceIds: uniqueStrings([
|
|
44
|
+
...array(sourceIndex.evidenceIds),
|
|
45
|
+
...resolutions.flatMap((resolution) => resolution.evidenceIds)
|
|
46
|
+
]),
|
|
47
|
+
proofIds: uniqueStrings([
|
|
48
|
+
...array(sourceIndex.proofIds),
|
|
49
|
+
...resolutions.flatMap((resolution) => resolution.proofIds)
|
|
50
|
+
])
|
|
51
|
+
};
|
|
52
|
+
const anchorInventory = createAnchorInventory(resolutions);
|
|
53
|
+
const summary = summarizeSemanticHistoryLineageResolutions(resolutions, resolvedIndex, anchorInventory);
|
|
54
|
+
const readiness = summary.cycle > 0 || summary.maxDepth > 0 ? 'blocked' : summary.resolved > 0 || summary.ambiguous > 0 || summary.deleted > 0 || summary.recreated > 0 ? 'needs-review' : 'ready';
|
|
55
|
+
const resolutionCore = {
|
|
56
|
+
kind: 'frontier.lang.semanticHistoryRecordLineageResolution',
|
|
57
|
+
version: 1,
|
|
58
|
+
sourceRecordId: record.id,
|
|
59
|
+
sourceRecordStableId: record.stableId,
|
|
60
|
+
sourceRecordHash: record.hash,
|
|
61
|
+
generatedAt,
|
|
62
|
+
resolutions,
|
|
63
|
+
anchorInventory,
|
|
64
|
+
summary,
|
|
65
|
+
admission: {
|
|
66
|
+
readiness,
|
|
67
|
+
reviewRequired: readiness !== 'ready',
|
|
68
|
+
autoMergeClaim: false,
|
|
69
|
+
semanticEquivalenceClaim: false
|
|
70
|
+
},
|
|
71
|
+
metadata: compactRecord(options.metadata)
|
|
72
|
+
};
|
|
73
|
+
const hash = hashSemanticValue(resolutionCore);
|
|
74
|
+
const id = options.id ?? `semantic_history_lineage_resolution_${idFragment(firstString(record.id, record.stableId, hash))}`;
|
|
75
|
+
const resolvedRecord = createResolvedHistoryRecord(record, resolvedIndex, { id, hash, generatedAt, summary, readiness });
|
|
76
|
+
return {
|
|
77
|
+
...resolutionCore,
|
|
78
|
+
id,
|
|
79
|
+
stableId: `semantic_history_lineage_resolution_${idFragment(hash)}`,
|
|
80
|
+
hash,
|
|
81
|
+
resolvedRecord
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function resolveSemanticHistoryRecordsLineage(records = [], eventsOrMap = [], options = {}) {
|
|
86
|
+
return array(records).map((record) => resolveSemanticHistoryRecordLineage(record, eventsOrMap, options));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function createResolvedHistoryRecord(record, index, resolution) {
|
|
90
|
+
const core = {
|
|
91
|
+
...record,
|
|
92
|
+
id: `${record.id ?? 'semantic_history'}#lineage-resolved`,
|
|
93
|
+
index,
|
|
94
|
+
metadata: {
|
|
95
|
+
...(record.metadata ?? {}),
|
|
96
|
+
semanticHistoryLineageResolution: {
|
|
97
|
+
id: resolution.id,
|
|
98
|
+
sourceRecordId: record.id,
|
|
99
|
+
sourceRecordHash: record.hash,
|
|
100
|
+
readiness: resolution.readiness,
|
|
101
|
+
summary: resolution.summary,
|
|
102
|
+
anchorSummary: {
|
|
103
|
+
activeAnchorKeys: resolution.summary.activeAnchorKeys,
|
|
104
|
+
candidateAnchorKeys: resolution.summary.candidateAnchorKeys,
|
|
105
|
+
inactiveAnchorKeys: resolution.summary.inactiveAnchorKeys,
|
|
106
|
+
blockedAnchorKeys: resolution.summary.blockedAnchorKeys
|
|
107
|
+
},
|
|
108
|
+
autoMergeClaim: false,
|
|
109
|
+
semanticEquivalenceClaim: false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const stableCore = { ...core };
|
|
114
|
+
delete stableCore.hash;
|
|
115
|
+
delete stableCore.stableId;
|
|
116
|
+
const hash = hashSemanticValue(stableCore);
|
|
117
|
+
return {
|
|
118
|
+
...core,
|
|
119
|
+
stableId: `semantic_history_${idFragment(hash)}`,
|
|
120
|
+
hash
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveIndexKeys(values, resolutions, options) {
|
|
125
|
+
return uniqueStrings(array(values).flatMap((value) => {
|
|
126
|
+
const key = String(value);
|
|
127
|
+
const resolution = resolutions.get(key);
|
|
128
|
+
if (!resolution) return [key];
|
|
129
|
+
if (resolution.status === 'deleted' && options.keepDeletedAnchors !== true) return [];
|
|
130
|
+
if (resolution.status === 'cycle' && options.keepBlockedAnchors !== true) return [];
|
|
131
|
+
if (resolution.status === 'max-depth' && options.keepBlockedAnchors !== true) return [];
|
|
132
|
+
if (resolution.status === 'not-found' && options.keepUnresolvedAnchors !== true) return [];
|
|
133
|
+
if (resolution.status === 'ambiguous' && options.keepCandidateAnchors === false) return [];
|
|
134
|
+
const current = resolution.currentAnchors.map((anchor) => anchor.key).filter(Boolean);
|
|
135
|
+
return current.length ? current : [key];
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function summarizeSemanticHistoryLineageResolutions(resolutions, index, anchorInventory) {
|
|
140
|
+
const byStatus = {};
|
|
141
|
+
for (const resolution of resolutions) byStatus[resolution.status] = (byStatus[resolution.status] ?? 0) + 1;
|
|
142
|
+
return {
|
|
143
|
+
anchorCount: resolutions.length,
|
|
144
|
+
unchanged: byStatus.unchanged ?? 0,
|
|
145
|
+
resolved: byStatus.resolved ?? 0,
|
|
146
|
+
ambiguous: byStatus.ambiguous ?? 0,
|
|
147
|
+
deleted: byStatus.deleted ?? 0,
|
|
148
|
+
recreated: byStatus.recreated ?? 0,
|
|
149
|
+
cycle: byStatus.cycle ?? 0,
|
|
150
|
+
maxDepth: byStatus['max-depth'] ?? 0,
|
|
151
|
+
notFound: byStatus['not-found'] ?? 0,
|
|
152
|
+
currentAnchorKeys: uniqueStrings(index.semanticAnchorKeys),
|
|
153
|
+
ownershipKeys: uniqueStrings(index.ownershipKeys),
|
|
154
|
+
conflictKeys: uniqueStrings(index.conflictKeys),
|
|
155
|
+
sourcePaths: uniqueStrings(index.sourcePaths),
|
|
156
|
+
activeAnchorKeys: uniqueStrings(anchorInventory.active.map((anchor) => anchor.key)),
|
|
157
|
+
candidateAnchorKeys: uniqueStrings(anchorInventory.candidate.map((anchor) => anchor.key)),
|
|
158
|
+
inactiveAnchorKeys: uniqueStrings(anchorInventory.inactive.map((anchor) => anchor.key)),
|
|
159
|
+
deletedAnchorKeys: uniqueStrings(anchorInventory.deleted.map((anchor) => anchor.key)),
|
|
160
|
+
unresolvedAnchorKeys: uniqueStrings(anchorInventory.unresolved.map((anchor) => anchor.key)),
|
|
161
|
+
blockedAnchorKeys: uniqueStrings(anchorInventory.blocked.map((anchor) => anchor.key)),
|
|
162
|
+
traversedEventIds: uniqueStrings(resolutions.flatMap((resolution) => resolution.traversedEventIds)),
|
|
163
|
+
terminalEventIds: uniqueStrings(resolutions.flatMap((resolution) => resolution.terminalEventIds)),
|
|
164
|
+
reasonCodes: uniqueStrings(resolutions.flatMap((resolution) => resolution.reasonCodes))
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function createAnchorInventory(resolutions) {
|
|
169
|
+
const inventory = {
|
|
170
|
+
active: [],
|
|
171
|
+
candidate: [],
|
|
172
|
+
inactive: [],
|
|
173
|
+
deleted: [],
|
|
174
|
+
unresolved: [],
|
|
175
|
+
blocked: []
|
|
176
|
+
};
|
|
177
|
+
for (const resolution of resolutions) {
|
|
178
|
+
const start = anchorEntry(resolution.startAnchor ?? queryAnchor(resolution), resolution);
|
|
179
|
+
const current = resolution.currentAnchors.map((anchor) => anchorEntry(anchor, resolution));
|
|
180
|
+
if (resolution.status === 'ambiguous') {
|
|
181
|
+
inventory.candidate.push(...current);
|
|
182
|
+
if (start) inventory.inactive.push(start);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (resolution.status === 'deleted') {
|
|
186
|
+
if (start) {
|
|
187
|
+
inventory.inactive.push(start);
|
|
188
|
+
inventory.deleted.push(start);
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (resolution.status === 'cycle' || resolution.status === 'max-depth') {
|
|
193
|
+
if (start) {
|
|
194
|
+
inventory.inactive.push(start);
|
|
195
|
+
inventory.blocked.push(start);
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (resolution.status === 'not-found') {
|
|
200
|
+
if (start) {
|
|
201
|
+
inventory.inactive.push(start);
|
|
202
|
+
inventory.unresolved.push(start);
|
|
203
|
+
}
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
inventory.active.push(...current);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
active: uniqueAnchorEntries(inventory.active),
|
|
210
|
+
candidate: uniqueAnchorEntries(inventory.candidate),
|
|
211
|
+
inactive: uniqueAnchorEntries(inventory.inactive),
|
|
212
|
+
deleted: uniqueAnchorEntries(inventory.deleted),
|
|
213
|
+
unresolved: uniqueAnchorEntries(inventory.unresolved),
|
|
214
|
+
blocked: uniqueAnchorEntries(inventory.blocked)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function anchorEntry(anchor, resolution) {
|
|
219
|
+
if (!anchor) return undefined;
|
|
220
|
+
return compactRecord({
|
|
221
|
+
key: anchor.key,
|
|
222
|
+
id: anchor.id,
|
|
223
|
+
kind: anchor.kind,
|
|
224
|
+
language: anchor.language,
|
|
225
|
+
sourcePath: anchor.sourcePath,
|
|
226
|
+
symbolId: anchor.symbolId,
|
|
227
|
+
symbolName: anchor.symbolName,
|
|
228
|
+
status: resolution.status,
|
|
229
|
+
resolutionId: resolution.id,
|
|
230
|
+
reasonCodes: uniqueStrings(resolution.reasonCodes)
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function queryAnchor(resolution) {
|
|
235
|
+
return compactRecord({
|
|
236
|
+
key: resolution.query?.anchorKey,
|
|
237
|
+
id: resolution.query?.anchorId,
|
|
238
|
+
sourcePath: resolution.query?.sourcePath,
|
|
239
|
+
symbolName: resolution.query?.symbolName
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function uniqueAnchorEntries(entries) {
|
|
244
|
+
const seen = new Set();
|
|
245
|
+
return entries.filter(Boolean).filter((entry) => {
|
|
246
|
+
const key = entry.key ?? entry.id ?? `${entry.sourcePath ?? ''}:${entry.symbolName ?? ''}`;
|
|
247
|
+
if (!key || seen.has(key)) return false;
|
|
248
|
+
seen.add(key);
|
|
249
|
+
return true;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function array(value) { return value === undefined || value === null ? [] : Array.isArray(value) ? value : [value]; }
|
|
254
|
+
function uniqueStrings(values) { return uniqueRawStrings(array(values).map((value) => String(value ?? '')).filter(Boolean)); }
|
|
255
|
+
function firstString(...values) { return values.map((value) => value === undefined || value === null ? '' : String(value)).find(Boolean); }
|
|
256
|
+
function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined)); }
|
package/package.json
CHANGED