@shapeshift-labs/frontier-lang-compiler 0.2.93 → 0.2.95

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.
@@ -13,6 +13,15 @@ import type { SemanticLineageEvent, SemanticLineageResolution } from './semantic
13
13
  import type { SemanticPatchBundleRecord } from './semantic-patch-bundle.js';
14
14
 
15
15
  export type BidirectionalTargetChangeAnchorStatus = 'matched' | 'unmatched' | 'ambiguous' | 'deleted' | string;
16
+ export type BidirectionalTargetPortabilityStatus = 'portable' | 'needs-port' | 'stale' | 'conflict' | 'blocked' | 'evidence-only' | string;
17
+ export type BidirectionalTargetPortabilityAction =
18
+ | 'port-with-source-map-review'
19
+ | 'human-port'
20
+ | 'refresh-source-map'
21
+ | 'resolve-anchor-conflict'
22
+ | 'block'
23
+ | 'record-evidence'
24
+ | string;
16
25
 
17
26
  export interface BidirectionalTargetChangeSourceAnchorMapping {
18
27
  readonly targetAnchorKey?: string;
@@ -62,6 +71,20 @@ export interface BidirectionalTargetChangeAnchor {
62
71
  readonly metadata?: Record<string, unknown>;
63
72
  }
64
73
 
74
+ export interface BidirectionalTargetMatchPortability {
75
+ readonly status: BidirectionalTargetPortabilityStatus;
76
+ readonly action: BidirectionalTargetPortabilityAction;
77
+ readonly readiness: SemanticMergeReadiness | string;
78
+ readonly confidence?: number;
79
+ readonly reviewRequired: true;
80
+ readonly autoMergeClaim: false;
81
+ readonly semanticEquivalenceClaim: false;
82
+ readonly reasonCodes: readonly string[];
83
+ readonly sourceMapLinkIds: readonly string[];
84
+ readonly sourceMapMappingIds: readonly string[];
85
+ readonly staleSourceMapLinkIds: readonly string[];
86
+ }
87
+
65
88
  export interface BidirectionalTargetChangeSourceAnchorMatch {
66
89
  readonly kind: 'frontier.lang.bidirectionalTargetChangeSourceAnchorMatch';
67
90
  readonly version: 1;
@@ -70,6 +93,7 @@ export interface BidirectionalTargetChangeSourceAnchorMatch {
70
93
  readonly sourceAnchors: readonly BidirectionalTargetChangeAnchor[];
71
94
  readonly lineageResolutions: readonly SemanticLineageResolution[];
72
95
  readonly sourceMapLinks: readonly BidirectionalTargetChangeSourceMapLink[];
96
+ readonly portability?: BidirectionalTargetMatchPortability;
73
97
  readonly status: BidirectionalTargetChangeAnchorStatus;
74
98
  readonly confidence?: number;
75
99
  readonly reasonCodes: readonly string[];
@@ -79,6 +103,31 @@ export interface BidirectionalTargetChangeSourceAnchorMatch {
79
103
  readonly conflictKeys: readonly string[];
80
104
  }
81
105
 
106
+ export interface BidirectionalTargetPortabilityRecord {
107
+ readonly kind: 'frontier.lang.bidirectionalTargetPortability';
108
+ readonly version: 1;
109
+ readonly id?: string;
110
+ readonly status: BidirectionalTargetPortabilityStatus;
111
+ readonly action: BidirectionalTargetPortabilityAction;
112
+ readonly readiness: SemanticMergeReadiness | string;
113
+ readonly confidence?: number;
114
+ readonly reviewRequired: true;
115
+ readonly autoMergeClaim: false;
116
+ readonly semanticEquivalenceClaim: false;
117
+ readonly reasonCodes: readonly string[];
118
+ readonly conflictKeys: readonly string[];
119
+ readonly sourceAnchorMatchIds: readonly string[];
120
+ readonly sourceMapLinkIds: readonly string[];
121
+ readonly sourceMapMappingIds: readonly string[];
122
+ readonly staleSourceMapLinkIds: readonly string[];
123
+ readonly targetChangedRegions: number;
124
+ readonly matchedTargetRegions: number;
125
+ readonly sourceMapBackedRegions: number;
126
+ readonly unmatchedTargetRegions: number;
127
+ readonly ambiguousTargetRegions: number;
128
+ readonly deletedSourceAnchors: number;
129
+ }
130
+
82
131
  export interface CreateBidirectionalTargetChangeRecordOptions {
83
132
  readonly id?: string;
84
133
  readonly source?: NativeSourceImportResult | ImportNativeSourceOptions;
@@ -130,6 +179,7 @@ export interface BidirectionalTargetChangeRecord {
130
179
  readonly sourceImport?: NativeSourceImportResult;
131
180
  readonly targetChangeSet: NativeSourceChangeSet;
132
181
  readonly sourceAnchorMatches: readonly BidirectionalTargetChangeSourceAnchorMatch[];
182
+ readonly targetPortability: BidirectionalTargetPortabilityRecord;
133
183
  readonly sourcePatchBundle: SemanticPatchBundleRecord;
134
184
  readonly historyRecord: SemanticHistoryRecord;
135
185
  readonly evidence: readonly EvidenceRecord[];
@@ -143,6 +193,10 @@ export interface BidirectionalTargetChangeRecord {
143
193
  readonly deletedSourceAnchors: number;
144
194
  readonly sourceChangedRegions: number;
145
195
  readonly sourceMapBackedMatches: number;
196
+ readonly targetPortabilityStatus: BidirectionalTargetPortabilityStatus;
197
+ readonly portableTargetRegions: number;
198
+ readonly staleTargetRegions: number;
199
+ readonly conflictingTargetRegions: number;
146
200
  };
147
201
  readonly metadata: {
148
202
  readonly autoMergeClaim: false;
@@ -36,6 +36,9 @@ export interface SemanticPatchBundleRecordIndex {
36
36
  readonly transformTargetLanguages: readonly string[];
37
37
  readonly transformSourcePaths: readonly string[];
38
38
  readonly transformTargetPaths: readonly string[];
39
+ readonly targetPortabilityStatuses: readonly string[];
40
+ readonly targetPortabilityActions: readonly string[];
41
+ readonly targetPortabilityReasonCodes: readonly string[];
39
42
  readonly patchIds: readonly string[];
40
43
  readonly mergeCandidateIds: readonly string[];
41
44
  readonly readinesses: readonly string[];
@@ -6,6 +6,9 @@ export type SemanticPatchBundleOverlapKind =
6
6
  | 'semantic-edit-key'
7
7
  | 'semantic-identity'
8
8
  | 'source-identity'
9
+ | 'semantic-edit-replay'
10
+ | 'replay-output'
11
+ | 'replay-current'
9
12
  | 'transform-content'
10
13
  | 'semantic-transform'
11
14
  | 'projection-identity'
@@ -27,6 +30,11 @@ export interface SemanticPatchBundleOverlapShared {
27
30
  readonly semanticEditKeys: readonly string[];
28
31
  readonly semanticIdentityHashes: readonly string[];
29
32
  readonly sourceIdentityHashes: readonly string[];
33
+ readonly semanticEditReplayIds: readonly string[];
34
+ readonly semanticEditReplayStatuses: readonly string[];
35
+ readonly semanticEditReplayActions: readonly string[];
36
+ readonly semanticEditReplayCurrentHashes: readonly string[];
37
+ readonly semanticEditReplayOutputHashes: readonly string[];
30
38
  readonly semanticTransformContentHashes: readonly string[];
31
39
  readonly semanticTransformIdentityHashes: readonly string[];
32
40
  readonly projectionIdentityHashes: readonly string[];
@@ -97,6 +105,16 @@ export interface SemanticPatchBundleOverlapQuery {
97
105
  readonly conflictKeys?: readonly string[];
98
106
  readonly semanticEditKey?: string | readonly string[];
99
107
  readonly semanticEditKeys?: readonly string[];
108
+ readonly semanticEditReplayId?: string | readonly string[];
109
+ readonly semanticEditReplayIds?: readonly string[];
110
+ readonly semanticEditReplayStatus?: string | readonly string[];
111
+ readonly semanticEditReplayStatuses?: readonly string[];
112
+ readonly semanticEditReplayAction?: string | readonly string[];
113
+ readonly semanticEditReplayActions?: readonly string[];
114
+ readonly semanticEditReplayCurrentHash?: string | readonly string[];
115
+ readonly semanticEditReplayCurrentHashes?: readonly string[];
116
+ readonly semanticEditReplayOutputHash?: string | readonly string[];
117
+ readonly semanticEditReplayOutputHashes?: readonly string[];
100
118
  readonly semanticTransformIdentityHash?: string | readonly string[];
101
119
  readonly semanticTransformIdentityHashes?: readonly string[];
102
120
  readonly semanticTransformContentHash?: string | readonly string[];
@@ -10,6 +10,7 @@ import type { NativeSourceChangeKind, NativeSourceChangeSet } from './native-dif
10
10
  import type { SemanticEditProjection, SemanticEditReplay, SemanticEditScript } from './semantic-edit-script.js';
11
11
  import type { SemanticPatchBundleRecordIndex } from './semantic-patch-bundle-index.js';
12
12
  import type { SemanticTransformIdentityRecord } from './semantic-transform-identity.js';
13
+ import type { BidirectionalTargetPortabilityRecord } from './bidirectional-target-change.js';
13
14
 
14
15
  export type { SemanticPatchBundleRecordIndex } from './semantic-patch-bundle-index.js';
15
16
 
@@ -56,6 +57,7 @@ export interface SemanticPatchBundleChangedRegion {
56
57
  readonly reasonCodes?: readonly string[];
57
58
  readonly conflictKeys?: readonly string[];
58
59
  };
60
+ readonly metadata?: Record<string, unknown>;
59
61
  }
60
62
 
61
63
  export interface SemanticPatchBundleSourceMapLink {
@@ -185,6 +187,7 @@ export interface CreateSemanticPatchBundleRecordOptions {
185
187
  readonly semanticEditReplays?: readonly SemanticEditReplay[] | SemanticEditReplay;
186
188
  readonly semanticTransformIdentity?: SemanticTransformIdentityRecord | Record<string, unknown>;
187
189
  readonly semanticTransformIdentities?: readonly (SemanticTransformIdentityRecord | Record<string, unknown>)[];
190
+ readonly targetPortability?: BidirectionalTargetPortabilityRecord | Record<string, unknown>;
188
191
  readonly sourceLanguage?: FrontierSourceLanguage | string;
189
192
  readonly targetLanguage?: FrontierSourceLanguage | string;
190
193
  readonly conflictKeys?: readonly string[] | string;
@@ -275,6 +278,12 @@ export interface SemanticPatchBundleRecordQuery {
275
278
  readonly transformSourcePaths?: readonly string[];
276
279
  readonly transformTargetPath?: string | readonly string[];
277
280
  readonly transformTargetPaths?: readonly string[];
281
+ readonly targetPortabilityStatus?: string | readonly string[];
282
+ readonly targetPortabilityStatuses?: readonly string[];
283
+ readonly targetPortabilityAction?: string | readonly string[];
284
+ readonly targetPortabilityActions?: readonly string[];
285
+ readonly targetPortabilityReasonCode?: string | readonly string[];
286
+ readonly targetPortabilityReasonCodes?: readonly string[];
278
287
  readonly readiness?: SemanticMergeReadiness | string | readonly string[];
279
288
  readonly readinesses?: readonly string[];
280
289
  readonly admissionStatus?: SemanticPatchBundleAdmissionStatus | readonly string[];
@@ -55,6 +55,7 @@ export function classifyBidirectionalReadiness(targetChangeSet, source, matches)
55
55
 
56
56
  export function sourceRegionsForMatch(match, readiness) {
57
57
  const anchors = match.sourceAnchors.length ? match.sourceAnchors : [undefined];
58
+ const portability = match.portability;
58
59
  return anchors.map((anchor, index) => compactRecord({
59
60
  id: `source_port_region_${idFragment(match.id)}_${index + 1}`,
60
61
  key: anchor?.key ?? `unmapped-target#${match.targetRegion.key ?? match.targetRegion.id}`,
@@ -70,9 +71,9 @@ export function sourceRegionsForMatch(match, readiness) {
70
71
  sourceSpan: anchor?.sourceSpan,
71
72
  sourceMapLinks: match.sourceMapLinks,
72
73
  admission: {
73
- readiness,
74
- action: 'review-port-from-target-change',
75
- reasonCodes: match.reasonCodes,
74
+ readiness: portability?.readiness ?? readiness,
75
+ action: portability?.action ?? 'review-port-from-target-change',
76
+ reasonCodes: uniqueStrings([...match.reasonCodes, ...array(portability?.reasonCodes)]),
76
77
  conflictKeys: match.conflictKeys
77
78
  },
78
79
  metadata: {
@@ -81,6 +82,7 @@ export function sourceRegionsForMatch(match, readiness) {
81
82
  targetRegion: match.targetRegion,
82
83
  sourceMapLinkIds: match.sourceMapLinks.map((link) => link.id),
83
84
  lineageResolutionIds: match.lineageResolutions.map((resolution) => resolution.id),
85
+ targetPortability: portability,
84
86
  reviewRequired: true,
85
87
  autoMergeClaim: false,
86
88
  semanticEquivalenceClaim: false
@@ -104,6 +106,10 @@ export function createBidirectionalEvidence(context) {
104
106
  sourceAnchorMatchIds: context.sourceAnchorMatches.map((match) => match.id),
105
107
  sourceMapBackedMatches: context.sourceAnchorMatches.filter((match) => match.sourceMapLinks.length > 0).length,
106
108
  sourceMapLinkIds: context.sourceAnchorMatches.flatMap((match) => match.sourceMapLinks.map((link) => link.id)),
109
+ targetPortabilityStatus: context.targetPortability?.status,
110
+ targetPortabilityAction: context.targetPortability?.action,
111
+ targetPortabilityReasonCodes: context.targetPortability?.reasonCodes,
112
+ targetPortabilitySourceMapLinkIds: context.targetPortability?.sourceMapLinkIds,
107
113
  readiness: context.readiness,
108
114
  reasons: context.reasons,
109
115
  autoMergeClaim: false,
@@ -0,0 +1,152 @@
1
+ import { uniqueStrings } from '../../native-import-utils.js';
2
+
3
+ export function classifyBidirectionalTargetPortability(context = {}) {
4
+ const matches = array(context.sourceAnchorMatches);
5
+ const targetRegions = Number(context.targetChangeSet?.changedRegions?.length ?? matches.length);
6
+ const links = matches.flatMap((match) => array(match.sourceMapLinks));
7
+ const sourceMapBackedMatches = matches.filter((match) => array(match.sourceMapLinks).length > 0).length;
8
+ const unmatched = matches.filter((match) => match.status === 'unmatched').length;
9
+ const ambiguous = matches.filter((match) => match.status === 'ambiguous').length;
10
+ const deleted = matches.filter((match) => match.status === 'deleted').length;
11
+ const staleLinks = links.filter((link) => sourceMapLinkIsStale(link, context));
12
+ const status = portabilityStatus({ context, targetRegions, matches, sourceMapBackedMatches, unmatched, ambiguous, deleted, staleLinks });
13
+ return compactRecord({
14
+ kind: 'frontier.lang.bidirectionalTargetPortability',
15
+ version: 1,
16
+ id: context.id ? `target_portability_${context.id}` : undefined,
17
+ status,
18
+ action: portabilityAction(status),
19
+ readiness: status === 'blocked' ? 'blocked' : 'needs-review',
20
+ confidence: portabilityConfidence({ status, targetRegions, sourceMapBackedMatches, unmatched, ambiguous, deleted, staleLinks }),
21
+ reviewRequired: true,
22
+ autoMergeClaim: false,
23
+ semanticEquivalenceClaim: false,
24
+ reasonCodes: portabilityReasons({ context, targetRegions, matches, sourceMapBackedMatches, unmatched, ambiguous, deleted, staleLinks, status }),
25
+ conflictKeys: uniqueStrings(matches.flatMap((match) => array(match.conflictKeys))),
26
+ sourceAnchorMatchIds: uniqueStrings(matches.map((match) => match.id)),
27
+ sourceMapLinkIds: uniqueStrings(links.map((link) => link.id)),
28
+ sourceMapMappingIds: uniqueStrings(links.map((link) => link.sourceMapMappingId)),
29
+ staleSourceMapLinkIds: uniqueStrings(staleLinks.map((link) => link.id)),
30
+ targetChangedRegions: targetRegions,
31
+ matchedTargetRegions: matches.filter((match) => match.status === 'matched').length,
32
+ sourceMapBackedRegions: sourceMapBackedMatches,
33
+ unmatchedTargetRegions: unmatched,
34
+ ambiguousTargetRegions: ambiguous,
35
+ deletedSourceAnchors: deleted
36
+ });
37
+ }
38
+
39
+ export function attachBidirectionalMatchPortability(match = {}, context = {}) {
40
+ return {
41
+ ...match,
42
+ portability: classifyBidirectionalMatchPortability(match, context)
43
+ };
44
+ }
45
+
46
+ export function classifyBidirectionalMatchPortability(match = {}, context = {}) {
47
+ const links = array(match.sourceMapLinks);
48
+ const staleLinks = links.filter((link) => sourceMapLinkIsStale(link, context));
49
+ const status = matchPortabilityStatus(match, links, staleLinks);
50
+ return compactRecord({
51
+ status,
52
+ action: portabilityAction(status),
53
+ readiness: status === 'blocked' ? 'blocked' : 'needs-review',
54
+ confidence: matchPortabilityConfidence(status, links),
55
+ reviewRequired: true,
56
+ autoMergeClaim: false,
57
+ semanticEquivalenceClaim: false,
58
+ reasonCodes: matchPortabilityReasons(match, links, staleLinks, status),
59
+ sourceMapLinkIds: uniqueStrings(links.map((link) => link.id)),
60
+ sourceMapMappingIds: uniqueStrings(links.map((link) => link.sourceMapMappingId)),
61
+ staleSourceMapLinkIds: uniqueStrings(staleLinks.map((link) => link.id))
62
+ });
63
+ }
64
+
65
+ function portabilityStatus(input) {
66
+ if (!input.context.source) return 'blocked';
67
+ if (input.targetRegions === 0 || input.matches.length === 0) return 'evidence-only';
68
+ if (input.deleted > 0) return 'blocked';
69
+ if (input.unmatched === input.targetRegions) return 'blocked';
70
+ if (input.staleLinks.length > 0) return 'stale';
71
+ if (input.ambiguous > 0) return 'conflict';
72
+ if (input.unmatched > 0) return 'needs-port';
73
+ if (input.sourceMapBackedMatches === input.targetRegions) return 'portable';
74
+ return 'needs-port';
75
+ }
76
+
77
+ function portabilityAction(status) {
78
+ if (status === 'portable') return 'port-with-source-map-review';
79
+ if (status === 'needs-port') return 'human-port';
80
+ if (status === 'stale') return 'refresh-source-map';
81
+ if (status === 'conflict') return 'resolve-anchor-conflict';
82
+ if (status === 'blocked') return 'block';
83
+ return 'record-evidence';
84
+ }
85
+
86
+ function matchPortabilityStatus(match, links, staleLinks) {
87
+ if (match.status === 'deleted' || match.status === 'unmatched') return 'blocked';
88
+ if (staleLinks.length > 0) return 'stale';
89
+ if (match.status === 'ambiguous') return 'conflict';
90
+ if (match.status === 'matched' && links.length > 0) return 'portable';
91
+ return 'needs-port';
92
+ }
93
+
94
+ function matchPortabilityConfidence(status, links) {
95
+ if (status === 'portable') return 0.72;
96
+ if (status === 'needs-port') return links.length > 0 ? 0.52 : 0.35;
97
+ return undefined;
98
+ }
99
+
100
+ function matchPortabilityReasons(match, links, staleLinks, status) {
101
+ return uniqueStrings([
102
+ status === 'portable' ? 'target-region-source-map-portable' : undefined,
103
+ links.length > 0 ? 'target-region-source-map-backed' : undefined,
104
+ links.length === 0 ? 'target-region-not-source-map-backed' : undefined,
105
+ match.status === 'unmatched' ? 'target-anchor-unmatched' : undefined,
106
+ match.status === 'ambiguous' ? 'target-anchor-ambiguous' : undefined,
107
+ match.status === 'deleted' ? 'target-anchor-deleted' : undefined,
108
+ staleLinks.length > 0 ? 'target-source-map-stale' : undefined,
109
+ 'target-region-is-portability-evidence-not-proof'
110
+ ]);
111
+ }
112
+
113
+ function portabilityConfidence(input) {
114
+ if (input.status === 'portable') return 0.74;
115
+ if (input.status === 'needs-port') return input.sourceMapBackedMatches > 0 ? 0.52 : 0.35;
116
+ if (input.status === 'evidence-only') return 0.25;
117
+ return undefined;
118
+ }
119
+
120
+ function portabilityReasons(input) {
121
+ const sourceMapBacked = input.sourceMapBackedMatches > 0;
122
+ return uniqueStrings([
123
+ input.context.source ? undefined : 'source-import-missing',
124
+ input.targetRegions === 0 ? 'target-change-empty' : undefined,
125
+ input.status === 'portable' ? 'target-change-source-map-portable' : undefined,
126
+ sourceMapBacked ? 'target-change-source-map-backed' : undefined,
127
+ input.sourceMapBackedMatches < input.targetRegions ? 'target-change-not-fully-source-map-backed' : undefined,
128
+ input.unmatched > 0 ? 'target-anchor-unmatched' : undefined,
129
+ input.ambiguous > 0 ? 'target-anchor-ambiguous' : undefined,
130
+ input.deleted > 0 ? 'target-anchor-deleted' : undefined,
131
+ input.staleLinks.length > 0 ? 'target-source-map-stale' : undefined,
132
+ 'target-change-is-portability-evidence-not-proof',
133
+ ...input.matches.flatMap((match) => array(match.reasonCodes))
134
+ ]);
135
+ }
136
+
137
+ function sourceMapLinkIsStale(link, context) {
138
+ const currentSourceHash = context.currentSourceHash;
139
+ const baseTargetHash = context.targetChangeSet?.beforeHash;
140
+ return Boolean(
141
+ (link.sourceHash && currentSourceHash && link.sourceHash !== currentSourceHash)
142
+ || (link.targetHash && baseTargetHash && link.targetHash !== baseTargetHash)
143
+ );
144
+ }
145
+
146
+ function array(value) {
147
+ return value === undefined || value === null ? [] : Array.isArray(value) ? value : [value];
148
+ }
149
+
150
+ function compactRecord(value) {
151
+ return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
152
+ }
@@ -8,6 +8,7 @@ import { createSemanticPatchBundleRecord } from './semanticPatchBundleRecords.js
8
8
  import { createSemanticHistoryRecord } from './semanticHistoryRecords.js';
9
9
  import { diffNativeSourceImports } from './diffNativeSourceImports.js';
10
10
  import { normalizeNativeDiffImport } from './normalizeNativeDiffImport.js';
11
+ import { attachBidirectionalMatchPortability, classifyBidirectionalTargetPortability } from './bidirectionalTargetPortability.js';
11
12
  import {
12
13
  anchorsFromSourceSidecar,
13
14
  classifyBidirectionalReadiness,
@@ -64,12 +65,24 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
64
65
  mappings,
65
66
  sourceMaps,
66
67
  lineage
68
+ })).map((match) => attachBidirectionalMatchPortability(match, {
69
+ currentSourceHash: sourceHash(source),
70
+ targetChangeSet
67
71
  }));
72
+ const targetPortability = classifyBidirectionalTargetPortability({
73
+ id,
74
+ source,
75
+ currentSourceHash: sourceHash(source),
76
+ targetChangeSet,
77
+ sourceAnchorMatches
78
+ });
68
79
  const readiness = classifyBidirectionalReadiness(targetChangeSet, source, sourceAnchorMatches);
69
80
  const reasons = uniqueStrings([
70
81
  'source-port-review-required',
71
82
  'target-change-is-merge-evidence-not-proof',
83
+ `target-portability:${targetPortability.status}`,
72
84
  ...array(targetChangeSet.reasons),
85
+ ...array(targetPortability.reasonCodes),
73
86
  ...sourceAnchorMatches.flatMap((match) => match.reasonCodes)
74
87
  ]);
75
88
  const evidence = [createBidirectionalEvidence({
@@ -78,6 +91,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
78
91
  source,
79
92
  targetChangeSet,
80
93
  sourceAnchorMatches,
94
+ targetPortability,
81
95
  readiness,
82
96
  reasons
83
97
  })];
@@ -97,6 +111,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
97
111
  targetChangeSetId: targetChangeSet.id,
98
112
  targetPatchId: targetChangeSet.patch?.id,
99
113
  targetMergeCandidateId: targetChangeSet.mergeCandidate?.id,
114
+ targetPortability,
100
115
  sourceMapBackprojection: summarizeSourceMapBackprojection(sourceAnchorMatches)
101
116
  }
102
117
  }, {
@@ -106,6 +121,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
106
121
  admission: { status: readiness === 'blocked' ? 'blocked' : 'needs-review', readiness },
107
122
  metadata: {
108
123
  source: 'createBidirectionalTargetChangeRecord',
124
+ targetPortability,
109
125
  autoMergeClaim: false,
110
126
  semanticEquivalenceClaim: false
111
127
  }
@@ -138,6 +154,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
138
154
  bidirectionalTargetChangeId: id,
139
155
  sourcePatchBundleId: sourcePatchBundle.id,
140
156
  targetChangeSetId: targetChangeSet.id,
157
+ targetPortability,
141
158
  sourceMapBackprojection: summarizeSourceMapBackprojection(sourceAnchorMatches),
142
159
  autoMergeClaim: false,
143
160
  semanticEquivalenceClaim: false
@@ -154,6 +171,7 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
154
171
  sourceImport: source,
155
172
  targetChangeSet,
156
173
  sourceAnchorMatches,
174
+ targetPortability,
157
175
  sourcePatchBundle,
158
176
  historyRecord,
159
177
  evidence,
@@ -166,12 +184,17 @@ export function createBidirectionalTargetChangeRecord(input = {}, options = {})
166
184
  unmatchedTargetRegions: sourceAnchorMatches.filter((match) => match.status === 'unmatched').length,
167
185
  deletedSourceAnchors: sourceAnchorMatches.filter((match) => match.status === 'deleted').length,
168
186
  sourceChangedRegions: sourceChangedRegions.length,
169
- sourceMapBackedMatches: sourceAnchorMatches.filter((match) => match.sourceMapLinks.length > 0).length
187
+ sourceMapBackedMatches: sourceAnchorMatches.filter((match) => match.sourceMapLinks.length > 0).length,
188
+ targetPortabilityStatus: targetPortability.status,
189
+ portableTargetRegions: targetPortability.status === 'portable' ? targetPortability.targetChangedRegions : 0,
190
+ staleTargetRegions: targetPortability.status === 'stale' ? targetPortability.targetChangedRegions : 0,
191
+ conflictingTargetRegions: targetPortability.status === 'conflict' ? targetPortability.ambiguousTargetRegions : 0
170
192
  },
171
193
  metadata: {
172
194
  autoMergeClaim: false,
173
195
  semanticEquivalenceClaim: false,
174
196
  reviewRequired: true,
197
+ targetPortability,
175
198
  ...input.metadata
176
199
  }
177
200
  };
@@ -6,6 +6,9 @@ export const SemanticPatchBundleOverlapKinds=Object.freeze([
6
6
  'semantic-edit-key',
7
7
  'semantic-identity',
8
8
  'source-identity',
9
+ 'semantic-edit-replay',
10
+ 'replay-output',
11
+ 'replay-current',
9
12
  'transform-content',
10
13
  'semantic-transform',
11
14
  'projection-identity',
@@ -27,6 +30,9 @@ const KIND_FIELDS=Object.freeze({
27
30
  'semantic-edit-key':'semanticEditKeys',
28
31
  'semantic-identity':'semanticIdentityHashes',
29
32
  'source-identity':'sourceIdentityHashes',
33
+ 'semantic-edit-replay':'semanticEditReplayIds',
34
+ 'replay-output':'semanticEditReplayOutputHashes',
35
+ 'replay-current':'semanticEditReplayCurrentHashes',
30
36
  'transform-content':'semanticTransformContentHashes',
31
37
  'semantic-transform':'semanticTransformIdentityHashes',
32
38
  'projection-identity':'projectionIdentityHashes',
@@ -55,9 +61,9 @@ export function compareSemanticPatchBundleRecords(left={},right={},options={}){
55
61
  score:overlapScore(admission.status,shared,admission.reasonCodes),
56
62
  summary:{
57
63
  sharedKeys:countShared(shared),
58
- duplicateSignals:shared.operationContentHashes.length+shared.editContentHashes.length+shared.semanticTransformContentHashes.length,
64
+ duplicateSignals:shared.operationContentHashes.length+shared.editContentHashes.length+shared.semanticTransformContentHashes.length+shared.semanticEditReplayIds.length+shared.semanticEditReplayOutputHashes.length,
59
65
  semanticSignals:shared.semanticEditKeys.length+shared.semanticIdentityHashes.length+shared.sourceIdentityHashes.length+shared.semanticTransformIdentityHashes.length+shared.projectionIdentityHashes.length,
60
- sourceSignals:shared.regionKeys.length+shared.conflictKeys.length+shared.sourcePaths.length,
66
+ sourceSignals:shared.regionKeys.length+shared.conflictKeys.length+shared.sourcePaths.length+shared.semanticEditReplayCurrentHashes.length,
61
67
  baseHashMismatch:admission.reasonCodes.includes('base-hash-mismatch'),
62
68
  targetHashMismatch:admission.reasonCodes.includes('target-hash-mismatch')
63
69
  },
@@ -90,6 +96,11 @@ function sharedIndex(left,right,options){
90
96
  semanticEditKeys:intersect(left.semanticEditKeys,right.semanticEditKeys),
91
97
  semanticIdentityHashes:intersect(left.semanticIdentityHashes,right.semanticIdentityHashes),
92
98
  sourceIdentityHashes:intersect(left.sourceIdentityHashes,right.sourceIdentityHashes),
99
+ semanticEditReplayIds:intersect(left.semanticEditReplayIds,right.semanticEditReplayIds),
100
+ semanticEditReplayStatuses:intersect(left.semanticEditReplayStatuses,right.semanticEditReplayStatuses),
101
+ semanticEditReplayActions:intersect(left.semanticEditReplayActions,right.semanticEditReplayActions),
102
+ semanticEditReplayCurrentHashes:intersect(left.semanticEditReplayCurrentHashes,right.semanticEditReplayCurrentHashes),
103
+ semanticEditReplayOutputHashes:intersect(left.semanticEditReplayOutputHashes,right.semanticEditReplayOutputHashes),
93
104
  semanticTransformContentHashes:intersect(left.semanticTransformContentHashes,right.semanticTransformContentHashes),
94
105
  semanticTransformIdentityHashes:intersect(left.semanticTransformIdentityHashes,right.semanticTransformIdentityHashes),
95
106
  projectionIdentityHashes:intersect(left.projectionIdentityHashes,right.projectionIdentityHashes),
@@ -102,9 +113,9 @@ function sharedIndex(left,right,options){
102
113
  }
103
114
 
104
115
  function overlapAdmission(shared,{leftIndex,rightIndex,options}){
105
- const duplicate=shared.operationContentHashes.length||shared.editContentHashes.length||shared.semanticTransformContentHashes.length;
116
+ const duplicate=shared.operationContentHashes.length||shared.editContentHashes.length||shared.semanticTransformContentHashes.length||shared.semanticEditReplayIds.length||shared.semanticEditReplayOutputHashes.length;
106
117
  const semantic=shared.semanticEditKeys.length||shared.semanticIdentityHashes.length||shared.sourceIdentityHashes.length||shared.semanticTransformIdentityHashes.length||shared.projectionIdentityHashes.length;
107
- const source=shared.regionKeys.length||shared.conflictKeys.length||shared.sourcePaths.length;
118
+ const source=shared.regionKeys.length||shared.conflictKeys.length||shared.sourcePaths.length||shared.semanticEditReplayCurrentHashes.length;
108
119
  const status=duplicate?'duplicate':semantic?'semantic-overlap':source?'source-overlap':'independent';
109
120
  const reasonCodes=uniqueStrings([
110
121
  shared.operationContentHashes.length?'same-operation-content':undefined,
@@ -113,6 +124,9 @@ function overlapAdmission(shared,{leftIndex,rightIndex,options}){
113
124
  shared.semanticEditKeys.length?'same-semantic-edit-key':undefined,
114
125
  shared.semanticIdentityHashes.length?'same-semantic-identity':undefined,
115
126
  shared.sourceIdentityHashes.length?'same-source-identity':undefined,
127
+ shared.semanticEditReplayIds.length?'same-semantic-edit-replay':undefined,
128
+ shared.semanticEditReplayOutputHashes.length?'same-replay-output':undefined,
129
+ shared.semanticEditReplayCurrentHashes.length?'same-replay-current':undefined,
116
130
  shared.semanticTransformIdentityHashes.length?'same-semantic-transform':undefined,
117
131
  shared.projectionIdentityHashes.length?'same-projection-identity':undefined,
118
132
  shared.regionKeys.length?'same-region-key':undefined,
@@ -141,6 +155,11 @@ function bundleIndex(record){
141
155
  semanticEditKeys:uniqueStrings(index.semanticEditKeys),
142
156
  semanticIdentityHashes:uniqueStrings(index.semanticIdentityHashes),
143
157
  sourceIdentityHashes:uniqueStrings(index.sourceIdentityHashes),
158
+ semanticEditReplayIds:uniqueStrings(index.semanticEditReplayIds),
159
+ semanticEditReplayStatuses:uniqueStrings(index.semanticEditReplayStatuses),
160
+ semanticEditReplayActions:uniqueStrings(index.semanticEditReplayActions),
161
+ semanticEditReplayCurrentHashes:uniqueStrings(index.semanticEditReplayCurrentHashes),
162
+ semanticEditReplayOutputHashes:uniqueStrings(index.semanticEditReplayOutputHashes),
144
163
  semanticTransformContentHashes:uniqueStrings(index.semanticTransformContentHashes),
145
164
  semanticTransformIdentityHashes:uniqueStrings(index.semanticTransformIdentityHashes),
146
165
  projectionIdentityHashes:uniqueStrings(index.projectionIdentityHashes),
@@ -161,6 +180,11 @@ function matchesOverlap(overlap,query){
161
180
  &&matchAny(queryValues(query.sourcePath,query.sourcePaths),overlap.shared.sourcePaths)
162
181
  &&matchAny(queryValues(query.conflictKey,query.conflictKeys),overlap.shared.conflictKeys)
163
182
  &&matchAny(queryValues(query.semanticEditKey,query.semanticEditKeys),overlap.shared.semanticEditKeys)
183
+ &&matchAny(queryValues(query.semanticEditReplayId,query.semanticEditReplayIds),overlap.shared.semanticEditReplayIds)
184
+ &&matchAny(queryValues(query.semanticEditReplayStatus,query.semanticEditReplayStatuses),overlap.shared.semanticEditReplayStatuses)
185
+ &&matchAny(queryValues(query.semanticEditReplayAction,query.semanticEditReplayActions),overlap.shared.semanticEditReplayActions)
186
+ &&matchAny(queryValues(query.semanticEditReplayCurrentHash,query.semanticEditReplayCurrentHashes),overlap.shared.semanticEditReplayCurrentHashes)
187
+ &&matchAny(queryValues(query.semanticEditReplayOutputHash,query.semanticEditReplayOutputHashes),overlap.shared.semanticEditReplayOutputHashes)
164
188
  &&matchAny(queryValues(query.semanticTransformIdentityHash,query.semanticTransformIdentityHashes),overlap.shared.semanticTransformIdentityHashes)
165
189
  &&matchAny(queryValues(query.semanticTransformContentHash,query.semanticTransformContentHashes),overlap.shared.semanticTransformContentHashes)
166
190
  &&matchAny(queryValues(query.projectionIdentityHash,query.projectionIdentityHashes),overlap.shared.projectionIdentityHashes)
@@ -16,6 +16,7 @@ export function createSemanticPatchBundleRecord(input={},options={}){
16
16
  const transformContext={sourceLanguage:options.sourceLanguage??source.sourceLanguage??source.language,targetLanguage:options.targetLanguage??source.targetLanguage};
17
17
  const semanticTransformIdentities=normalizeSemanticTransformIdentityRecords(semanticTransformInputs(source,options),transformContext);
18
18
  const semanticTransformIndex=semanticTransformRecordIndex(semanticTransformIdentities,source);
19
+ const targetPortability=options.targetPortability??source.targetPortability??source.metadata?.targetPortability??options.metadata?.targetPortability;
19
20
  const regionInputs=array(options.changedRegions??source.changedRegions??source.regions);
20
21
  const sourceMapLinks=normalizeSourceMapLinks([
21
22
  ...array(options.sourceMapLinks??source.sourceMapLinks),
@@ -50,7 +51,7 @@ export function createSemanticPatchBundleRecord(input={},options={}){
50
51
  ??`semantic_patch_bundle_${idFragment(firstString(source.id,patchId,mergeCandidateId,source.sourcePath,source.language,'record'))}`;
51
52
  const language=options.language??source.language??mergeCandidate?.language??sources.find((item)=>item.language)?.language;
52
53
  const sourcePath=options.sourcePath??source.sourcePath??mergeCandidate?.sourcePath??sources.find((item)=>item.sourcePath)?.sourcePath;
53
- const index=recordIndex({baseHash,targetHash,sources,changedRegions,sourceMapLinks,evidenceIds,proofIds,historyIds,semanticOperationIds,patchId,mergeCandidateId,admission,semanticEditIndex,semanticTransformIndex});
54
+ const index=recordIndex({baseHash,targetHash,sources,changedRegions,sourceMapLinks,evidenceIds,proofIds,historyIds,semanticOperationIds,patchId,mergeCandidateId,admission,semanticEditIndex,semanticTransformIndex,targetPortability});
54
55
  return{
55
56
  kind:'frontier.lang.semanticPatchBundleRecord',
56
57
  version:1,
@@ -85,6 +86,7 @@ export function createSemanticPatchBundleRecord(input={},options={}){
85
86
  semanticMergeConflictSummary:source.metadata?.semanticMergeConflictSummary,
86
87
  semanticEditSummary:semanticEditSummary(semanticEditIndex),
87
88
  semanticTransformSummary:semanticTransformSummary(semanticTransformIndex),
89
+ targetPortability,
88
90
  ...options.metadata
89
91
  })
90
92
  };
@@ -154,11 +156,12 @@ function normalizeRegions(regions,context){
154
156
  sourceMapIds:uniqueStrings([...strings(region.sourceMapIds),...links.map((link)=>link.sourceMapId)]),
155
157
  sourceMapMappingIds:uniqueStrings([...strings(region.sourceMapMappingIds),...links.map((link)=>link.sourceMapMappingId)]),
156
158
  admission:compactRecord({
157
- readiness:projection?.admission?.readiness,
158
- action:projection?.admission?.action,
159
- reasonCodes:uniqueStrings(projection?.admission?.reasons),
160
- conflictKeys:uniqueStrings(projection?.admission?.conflictKeys)
161
- })
159
+ readiness:projection?.admission?.readiness??region.admission?.readiness,
160
+ action:projection?.admission?.action??region.admission?.action,
161
+ reasonCodes:uniqueStrings([...strings(region.admission?.reasonCodes),...strings(projection?.admission?.reasons)]),
162
+ conflictKeys:uniqueStrings([...strings(region.admission?.conflictKeys),...strings(projection?.admission?.conflictKeys)])
163
+ }),
164
+ metadata:region.metadata
162
165
  });
163
166
  });
164
167
  }
@@ -229,6 +232,9 @@ function recordIndex(parts){
229
232
  transformTargetLanguages:semanticTransformIndex.transformTargetLanguages,
230
233
  transformSourcePaths:semanticTransformIndex.transformSourcePaths,
231
234
  transformTargetPaths:semanticTransformIndex.transformTargetPaths,
235
+ targetPortabilityStatuses:uniqueStrings([parts.targetPortability?.status]),
236
+ targetPortabilityActions:uniqueStrings([parts.targetPortability?.action]),
237
+ targetPortabilityReasonCodes:uniqueStrings(parts.targetPortability?.reasonCodes),
232
238
  patchIds:uniqueStrings([parts.patchId]),
233
239
  mergeCandidateIds:uniqueStrings([parts.mergeCandidateId]),
234
240
  readinesses:uniqueStrings([parts.admission.readiness,...parts.changedRegions.map((region)=>region.admission?.readiness)]),
@@ -237,7 +243,7 @@ function recordIndex(parts){
237
243
  }
238
244
 
239
245
  function matchesRecord(record,query){
240
- const index=record.index??recordIndex({...record,baseHash:record.baseHash,targetHash:record.targetHash,sources:record.sources??[],changedRegions:record.changedRegions??[],sourceMapLinks:record.sourceMapLinks??[],evidenceIds:record.evidenceIds??[],proofIds:record.proofIds??[],historyIds:record.historyIds??[],semanticOperationIds:record.semanticOperationIds??[],patchId:record.patchId,mergeCandidateId:record.mergeCandidateId,admission:record.admission??{}});
246
+ const index=record.index??recordIndex({...record,baseHash:record.baseHash,targetHash:record.targetHash,sources:record.sources??[],changedRegions:record.changedRegions??[],sourceMapLinks:record.sourceMapLinks??[],evidenceIds:record.evidenceIds??[],proofIds:record.proofIds??[],historyIds:record.historyIds??[],semanticOperationIds:record.semanticOperationIds??[],patchId:record.patchId,mergeCandidateId:record.mergeCandidateId,admission:record.admission??{},targetPortability:record.metadata?.targetPortability});
241
247
  return matchAny(queryValues(query.id,query.ids),[record.id])
242
248
  &&matchAny(queryValues(query.patchId,query.patchIds),index.patchIds)
243
249
  &&matchAny(queryValues(query.mergeCandidateId,query.mergeCandidateIds),index.mergeCandidateIds)
@@ -278,6 +284,9 @@ function matchesRecord(record,query){
278
284
  &&matchAny(queryValues(query.transformTargetLanguage,query.transformTargetLanguages),index.transformTargetLanguages)
279
285
  &&matchAny(queryValues(query.transformSourcePath,query.transformSourcePaths),index.transformSourcePaths)
280
286
  &&matchAny(queryValues(query.transformTargetPath,query.transformTargetPaths),index.transformTargetPaths)
287
+ &&matchAny(queryValues(query.targetPortabilityStatus,query.targetPortabilityStatuses),index.targetPortabilityStatuses)
288
+ &&matchAny(queryValues(query.targetPortabilityAction,query.targetPortabilityActions),index.targetPortabilityActions)
289
+ &&matchAny(queryValues(query.targetPortabilityReasonCode,query.targetPortabilityReasonCodes),index.targetPortabilityReasonCodes)
281
290
  &&matchAny(queryValues(query.readiness,query.readinesses),index.readinesses)
282
291
  &&matchAny(queryValues(query.admissionStatus,query.admissionStatuses),index.admissionStatuses);
283
292
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.93",
3
+ "version": "0.2.95",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",