@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 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
- sourceAnchorMappings: [{ targetSymbolName: 'advance', sourceSymbolName: 'advance' }],
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 candidateAnchors = explicit
12
- ? sourceAnchorsForMapping(explicit, context.sourceAnchors)
13
- : sourceAnchorsByName(context.region, context.sourceAnchors);
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 = uniqueStrings([region.symbolName, region.name, region.metadata?.changedRegionProjection?.region?.symbolName]);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.77",
3
+ "version": "0.2.79",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",