@shapeshift-labs/frontier-lang-compiler 0.2.94 → 0.2.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/declarations/bidirectional-target-change.d.ts +54 -0
- package/dist/declarations/semantic-edit-script.d.ts +23 -23
- package/dist/declarations/semantic-patch-bundle-index.d.ts +3 -0
- package/dist/declarations/semantic-patch-bundle.d.ts +9 -0
- package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +9 -3
- package/dist/internal/index-impl/bidirectionalTargetPortability.js +152 -0
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +24 -1
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +130 -11
- package/dist/internal/index-impl/replaySemanticEditProjection.js +35 -1
- package/dist/internal/index-impl/semanticEditInsertionAnchors.js +108 -0
- package/dist/internal/index-impl/semanticEditScripts.js +3 -1
- package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -7
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -28,39 +28,32 @@ export interface SemanticEditScriptOperation {
|
|
|
28
28
|
readonly kind: string;
|
|
29
29
|
readonly changeKind?: string;
|
|
30
30
|
readonly anchor: {
|
|
31
|
-
readonly key?: string;
|
|
32
|
-
readonly
|
|
33
|
-
readonly regionId?: string;
|
|
34
|
-
readonly regionKind?: string;
|
|
35
|
-
readonly granularity?: string;
|
|
31
|
+
readonly key?: string; readonly conflictKey?: string; readonly regionId?: string;
|
|
32
|
+
readonly regionKind?: string; readonly granularity?: string;
|
|
36
33
|
readonly language?: FrontierSourceLanguage | string;
|
|
37
|
-
readonly sourcePath?: string;
|
|
38
|
-
readonly symbolId?: string;
|
|
39
|
-
readonly symbolName?: string;
|
|
40
|
-
readonly symbolKind?: string;
|
|
34
|
+
readonly sourcePath?: string; readonly symbolId?: string; readonly symbolName?: string; readonly symbolKind?: string;
|
|
41
35
|
readonly sourceSpan?: SourceSpan;
|
|
42
36
|
};
|
|
37
|
+
readonly insertion?: {
|
|
38
|
+
readonly mode?: 'before' | 'after' | 'file-start' | 'file-end' | string;
|
|
39
|
+
readonly anchorKey?: string; readonly anchorSymbolId?: string; readonly anchorSymbolName?: string; readonly anchorSymbolKind?: string;
|
|
40
|
+
readonly baseSpan?: SourceSpan; readonly workerAnchorSpan?: SourceSpan; readonly headSpan?: SourceSpan; readonly sourcePath?: string;
|
|
41
|
+
readonly insertedSymbolId?: string; readonly insertedSymbolName?: string; readonly insertedSymbolKind?: string;
|
|
42
|
+
readonly insertedSourceSpan?: SourceSpan; readonly insertedSourcePath?: string;
|
|
43
|
+
readonly reasonCodes?: readonly string[];
|
|
44
|
+
};
|
|
43
45
|
readonly semanticKey?: string;
|
|
44
46
|
readonly semanticIdentityHash?: string;
|
|
45
47
|
readonly sourceIdentityHash?: string;
|
|
46
48
|
readonly operationContentHash?: string;
|
|
47
49
|
readonly spans?: {
|
|
48
|
-
readonly base?: SourceSpan;
|
|
49
|
-
readonly worker?: SourceSpan;
|
|
50
|
-
readonly head?: SourceSpan;
|
|
50
|
+
readonly base?: SourceSpan; readonly worker?: SourceSpan; readonly head?: SourceSpan;
|
|
51
51
|
};
|
|
52
52
|
readonly hashes?: {
|
|
53
|
-
readonly baseSourceHash?: string;
|
|
54
|
-
readonly
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
57
|
-
readonly workerSpanHash?: string;
|
|
58
|
-
readonly headSpanHash?: string;
|
|
59
|
-
readonly baseTextHash?: string;
|
|
60
|
-
readonly workerTextHash?: string;
|
|
61
|
-
readonly headTextHash?: string;
|
|
62
|
-
readonly beforeSignatureHash?: string;
|
|
63
|
-
readonly afterSignatureHash?: string;
|
|
53
|
+
readonly baseSourceHash?: string; readonly workerSourceHash?: string; readonly headSourceHash?: string;
|
|
54
|
+
readonly baseSpanHash?: string; readonly workerSpanHash?: string; readonly headSpanHash?: string;
|
|
55
|
+
readonly baseTextHash?: string; readonly workerTextHash?: string; readonly headTextHash?: string;
|
|
56
|
+
readonly beforeSignatureHash?: string; readonly afterSignatureHash?: string;
|
|
64
57
|
};
|
|
65
58
|
readonly status: SemanticEditScriptOperationStatus;
|
|
66
59
|
readonly reanchor?: {
|
|
@@ -135,6 +128,7 @@ export interface SemanticEditProjectionEdit {
|
|
|
135
128
|
readonly operationId?: string;
|
|
136
129
|
readonly status: 'applied' | 'already-applied';
|
|
137
130
|
readonly kind?: string;
|
|
131
|
+
readonly editKind?: 'replace' | 'insert' | string;
|
|
138
132
|
readonly changeKind?: string;
|
|
139
133
|
readonly anchorKey?: string;
|
|
140
134
|
readonly conflictKey?: string;
|
|
@@ -162,6 +156,11 @@ export interface SemanticEditProjectionEdit {
|
|
|
162
156
|
readonly replacementBytes: number;
|
|
163
157
|
readonly deletedTextHash?: string;
|
|
164
158
|
readonly replacementTextHash?: string;
|
|
159
|
+
readonly replacementSpanTextHash?: string;
|
|
160
|
+
readonly insertionMode?: string;
|
|
161
|
+
readonly insertionAnchorKey?: string;
|
|
162
|
+
readonly insertionAnchorSymbolName?: string;
|
|
163
|
+
readonly insertionAnchorSymbolKind?: string;
|
|
165
164
|
readonly replacementText?: string;
|
|
166
165
|
}
|
|
167
166
|
|
|
@@ -206,6 +205,7 @@ export interface SemanticEditReplayEdit {
|
|
|
206
205
|
readonly semanticIdentityHash?: string;
|
|
207
206
|
readonly sourceIdentityHash?: string;
|
|
208
207
|
readonly editContentHash?: string;
|
|
208
|
+
readonly editKind?: 'replace' | 'insert' | string;
|
|
209
209
|
readonly sourcePath?: string;
|
|
210
210
|
readonly symbolName?: string;
|
|
211
211
|
readonly symbolKind?: string;
|
|
@@ -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[];
|
|
@@ -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
|
};
|
|
@@ -12,13 +12,15 @@ export function projectSemanticEditScriptToSource(input = {}) {
|
|
|
12
12
|
if (typeof workerSourceText !== 'string') reasonCodes.push('missing-worker-source-text');
|
|
13
13
|
if (typeof headSourceText !== 'string') reasonCodes.push('missing-head-source-text');
|
|
14
14
|
const edits = [];
|
|
15
|
-
for (const operation of script.operations ?? []) {
|
|
16
|
-
const edit = sourceEditForOperation(operation, workerSourceText, headSourceText);
|
|
15
|
+
for (const [index, operation] of (script.operations ?? []).entries()) {
|
|
16
|
+
const edit = sourceEditForOperation(operation, workerSourceText, headSourceText, index);
|
|
17
17
|
if (edit.ok) edits.push(edit.value);
|
|
18
18
|
else reasonCodes.push(...edit.reasonCodes);
|
|
19
19
|
}
|
|
20
|
+
const deduped = dedupeSourceEdits(edits);
|
|
21
|
+
reasonCodes.push(...validateSourceEdits(deduped.edits));
|
|
20
22
|
const blocked = reasonCodes.length > 0;
|
|
21
|
-
const sourceText = blocked ? undefined : applySourceEdits(headSourceText, edits);
|
|
23
|
+
const sourceText = blocked ? undefined : applySourceEdits(headSourceText, deduped.edits);
|
|
22
24
|
const core = {
|
|
23
25
|
kind: 'frontier.lang.semanticEditProjection',
|
|
24
26
|
version: 1,
|
|
@@ -31,9 +33,9 @@ export function projectSemanticEditScriptToSource(input = {}) {
|
|
|
31
33
|
workerHash: script.workerHash,
|
|
32
34
|
headHash: script.headHash,
|
|
33
35
|
projectedHash: sourceText === undefined ? undefined : hashSemanticValue(sourceText),
|
|
34
|
-
appliedOperations: blocked ? [] : edits.map((edit) => edit.operationId),
|
|
35
|
-
skippedOperations: blocked ? (script.operations ?? []).map((operation) => operation.id) :
|
|
36
|
-
edits: blocked ? [] : edits.map(projectionEditRecord),
|
|
36
|
+
appliedOperations: blocked ? [] : deduped.edits.map((edit) => edit.operationId),
|
|
37
|
+
skippedOperations: blocked ? (script.operations ?? []).map((operation) => operation.id) : deduped.skippedOperationIds,
|
|
38
|
+
edits: blocked ? [] : deduped.edits.map(projectionEditRecord),
|
|
37
39
|
sourceText,
|
|
38
40
|
admission: {
|
|
39
41
|
status: blocked ? 'blocked' : 'auto-merge-candidate',
|
|
@@ -45,20 +47,24 @@ export function projectSemanticEditScriptToSource(input = {}) {
|
|
|
45
47
|
autoMergeClaim: false,
|
|
46
48
|
semanticEquivalenceClaim: false,
|
|
47
49
|
editCount: edits.length,
|
|
48
|
-
appliedEditCount: edits.filter((edit) => !edit.alreadyApplied).length,
|
|
49
|
-
alreadyAppliedEditCount: edits.filter((edit) => edit.alreadyApplied).length,
|
|
50
|
+
appliedEditCount: deduped.edits.filter((edit) => !edit.alreadyApplied).length,
|
|
51
|
+
alreadyAppliedEditCount: deduped.edits.filter((edit) => edit.alreadyApplied).length,
|
|
52
|
+
dedupedEditCount: deduped.skippedOperationIds.length,
|
|
50
53
|
...input.metadata
|
|
51
54
|
})
|
|
52
55
|
};
|
|
53
56
|
return { ...core, hash: hashSemanticValue(core) };
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
function sourceEditForOperation(operation, workerSourceText, headSourceText) {
|
|
59
|
+
function sourceEditForOperation(operation, workerSourceText, headSourceText, order) {
|
|
57
60
|
const identity = projectionIdentity(operation);
|
|
58
61
|
if (operation.status === 'already-applied') {
|
|
59
|
-
return { ok: true, value: { ...identity, operationId: operation.id, start: 0, end: 0, replacement: '', current: '', alreadyApplied: true } };
|
|
62
|
+
return { ok: true, value: { ...identity, operationId: operation.id, order, start: 0, end: 0, replacement: '', current: '', alreadyApplied: true } };
|
|
60
63
|
}
|
|
61
64
|
if (operation.status !== 'portable') return { ok: false, reasonCodes: [`operation-not-portable:${operation.id}`] };
|
|
65
|
+
if (operation.changeKind === 'added' || String(operation.kind ?? '').startsWith('add')) {
|
|
66
|
+
return insertionEditForOperation(operation, identity, workerSourceText, headSourceText, order);
|
|
67
|
+
}
|
|
62
68
|
const workerOffsets = spanOffsets(workerSourceText, operation.spans?.worker);
|
|
63
69
|
const headOffsets = spanOffsets(headSourceText, operation.spans?.head ?? operation.spans?.base ?? operation.anchor?.sourceSpan);
|
|
64
70
|
const reasons = [];
|
|
@@ -79,7 +85,9 @@ function sourceEditForOperation(operation, workerSourceText, headSourceText) {
|
|
|
79
85
|
ok: true,
|
|
80
86
|
value: {
|
|
81
87
|
operationId: operation.id,
|
|
88
|
+
order,
|
|
82
89
|
...identity,
|
|
90
|
+
editKind: 'replace',
|
|
83
91
|
start: headOffsets.start,
|
|
84
92
|
end: headOffsets.end,
|
|
85
93
|
workerStart: workerOffsets.start,
|
|
@@ -90,6 +98,37 @@ function sourceEditForOperation(operation, workerSourceText, headSourceText) {
|
|
|
90
98
|
};
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
function insertionEditForOperation(operation, identity, workerSourceText, headSourceText, order) {
|
|
102
|
+
const workerOffsets = spanOffsets(workerSourceText, operation.spans?.worker);
|
|
103
|
+
const reasons = [];
|
|
104
|
+
if (!workerOffsets) reasons.push(`worker-span-not-resolvable:${operation.id}`);
|
|
105
|
+
const insertion = insertionOffset(headSourceText, operation.insertion);
|
|
106
|
+
if (!insertion.ok) reasons.push(...insertion.reasonCodes.map((reason) => `${reason}:${operation.id}`));
|
|
107
|
+
if (reasons.length) return { ok: false, reasonCodes: reasons };
|
|
108
|
+
const spanText = workerSourceText.slice(workerOffsets.start, workerOffsets.end);
|
|
109
|
+
if (operation.hashes?.workerTextHash && hashSemanticValue(spanText) !== operation.hashes.workerTextHash) {
|
|
110
|
+
reasons.push(`worker-span-hash-mismatch:${operation.id}`);
|
|
111
|
+
}
|
|
112
|
+
if (reasons.length) return { ok: false, reasonCodes: reasons };
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
value: {
|
|
116
|
+
operationId: operation.id,
|
|
117
|
+
order,
|
|
118
|
+
...identity,
|
|
119
|
+
editKind: 'insert',
|
|
120
|
+
insertion: operation.insertion,
|
|
121
|
+
start: insertion.offset,
|
|
122
|
+
end: insertion.offset,
|
|
123
|
+
workerStart: workerOffsets.start,
|
|
124
|
+
workerEnd: workerOffsets.end,
|
|
125
|
+
replacement: insertionReplacement(spanText, headSourceText, insertion.offset),
|
|
126
|
+
replacementSpanText: spanText,
|
|
127
|
+
current: ''
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
93
132
|
function projectionIdentity(operation) {
|
|
94
133
|
const identity = semanticEditIdentity(operation);
|
|
95
134
|
return { ...identity, sourcePath: operation.reanchor?.toSourcePath ?? identity.sourcePath };
|
|
@@ -103,6 +142,7 @@ function projectionEditRecord(edit) {
|
|
|
103
142
|
operationId: edit.operationId,
|
|
104
143
|
status: edit.alreadyApplied ? 'already-applied' : 'applied',
|
|
105
144
|
kind: edit.kind,
|
|
145
|
+
editKind: edit.editKind,
|
|
106
146
|
changeKind: edit.changeKind,
|
|
107
147
|
anchorKey: edit.anchorKey,
|
|
108
148
|
conflictKey: edit.conflictKey,
|
|
@@ -133,6 +173,11 @@ function projectionEditRecord(edit) {
|
|
|
133
173
|
replacementBytes: edit.replacement.length,
|
|
134
174
|
deletedTextHash,
|
|
135
175
|
replacementTextHash,
|
|
176
|
+
replacementSpanTextHash: hashSemanticValue(edit.replacementSpanText ?? edit.replacement),
|
|
177
|
+
insertionMode: edit.insertion?.mode,
|
|
178
|
+
insertionAnchorKey: edit.insertion?.anchorKey,
|
|
179
|
+
insertionAnchorSymbolName: edit.insertion?.anchorSymbolName,
|
|
180
|
+
insertionAnchorSymbolKind: edit.insertion?.anchorSymbolKind,
|
|
136
181
|
replacementText: edit.replacement
|
|
137
182
|
});
|
|
138
183
|
}
|
|
@@ -164,10 +209,42 @@ function semanticEditIdentity(operation) {
|
|
|
164
209
|
|
|
165
210
|
function applySourceEdits(sourceText, edits) {
|
|
166
211
|
return edits.filter((edit) => !edit.alreadyApplied)
|
|
167
|
-
.sort(
|
|
212
|
+
.sort(sourceEditSort)
|
|
168
213
|
.reduce((text, edit) => text.slice(0, edit.start) + edit.replacement + text.slice(edit.end), sourceText);
|
|
169
214
|
}
|
|
170
215
|
|
|
216
|
+
function dedupeSourceEdits(edits) {
|
|
217
|
+
const seen = new Map();
|
|
218
|
+
const result = [];
|
|
219
|
+
const skippedOperationIds = [];
|
|
220
|
+
for (const edit of edits) {
|
|
221
|
+
const key = duplicateEditKey(edit);
|
|
222
|
+
if (key && seen.has(key)) {
|
|
223
|
+
skippedOperationIds.push(edit.operationId);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (key) seen.set(key, edit.operationId);
|
|
227
|
+
result.push(edit);
|
|
228
|
+
}
|
|
229
|
+
return { edits: result, skippedOperationIds };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function duplicateEditKey(edit) {
|
|
233
|
+
if (edit.editKind !== 'insert') return undefined;
|
|
234
|
+
return [
|
|
235
|
+
'insert',
|
|
236
|
+
edit.start,
|
|
237
|
+
edit.end,
|
|
238
|
+
edit.insertion?.mode,
|
|
239
|
+
edit.insertion?.anchorKey,
|
|
240
|
+
hashSemanticValue(edit.replacementSpanText ?? edit.replacement)
|
|
241
|
+
].join(':');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function sourceEditSort(left, right) {
|
|
245
|
+
return right.start - left.start || right.end - left.end || (right.order ?? 0) - (left.order ?? 0);
|
|
246
|
+
}
|
|
247
|
+
|
|
171
248
|
function projectedSourcePath(script, edits) {
|
|
172
249
|
return edits.map((edit) => edit.sourcePath).find(Boolean) ?? script.sourcePath;
|
|
173
250
|
}
|
|
@@ -189,6 +266,48 @@ function spanOffsets(sourceText, span) {
|
|
|
189
266
|
return { start: start + startColumn, end: endLineStart + endColumn };
|
|
190
267
|
}
|
|
191
268
|
|
|
269
|
+
function insertionOffset(sourceText, insertion) {
|
|
270
|
+
if (typeof sourceText !== 'string') return { ok: false, reasonCodes: ['missing-head-source-text'] };
|
|
271
|
+
const mode = insertion?.mode;
|
|
272
|
+
if (mode === 'file-start') return { ok: true, offset: 0 };
|
|
273
|
+
if (mode === 'file-end') return { ok: true, offset: sourceText.length };
|
|
274
|
+
const range = spanOffsets(sourceText, insertion?.headSpan);
|
|
275
|
+
if (!range) return { ok: false, reasonCodes: ['insertion-anchor-not-resolvable'] };
|
|
276
|
+
if (mode === 'before') return { ok: true, offset: range.start };
|
|
277
|
+
if (mode === 'after') return { ok: true, offset: afterLineOffset(sourceText, range.end) };
|
|
278
|
+
return { ok: false, reasonCodes: ['insertion-mode-unsupported'] };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function insertionReplacement(text, sourceText, offset) {
|
|
282
|
+
let replacement = String(text ?? '');
|
|
283
|
+
if (offset > 0 && sourceText[offset - 1] !== '\n') replacement = `\n${replacement}`;
|
|
284
|
+
if (offset < sourceText.length && !replacement.endsWith('\n')) replacement += '\n';
|
|
285
|
+
if (offset === sourceText.length && sourceText && !sourceText.endsWith('\n')) replacement = `\n${replacement}`;
|
|
286
|
+
if (offset === sourceText.length && !replacement.endsWith('\n')) replacement += '\n';
|
|
287
|
+
return replacement;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function afterLineOffset(sourceText, offset) {
|
|
291
|
+
return sourceText[offset] === '\n' ? offset + 1 : offset;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function validateSourceEdits(edits) {
|
|
295
|
+
const reasons = [];
|
|
296
|
+
const ordered = edits.filter((edit) => !edit.alreadyApplied).sort((left, right) => left.start - right.start || left.end - right.end);
|
|
297
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
298
|
+
const previous = ordered[index - 1];
|
|
299
|
+
const current = ordered[index];
|
|
300
|
+
if (editsOverlap(previous, current)) reasons.push(`source-edit-overlap:${previous.operationId}:${current.operationId}`);
|
|
301
|
+
}
|
|
302
|
+
return uniqueStrings(reasons);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function editsOverlap(left, right) {
|
|
306
|
+
if (left.start === left.end) return right.start < left.start && left.start < right.end;
|
|
307
|
+
if (right.start === right.end) return left.start < right.start && right.start < left.end;
|
|
308
|
+
return left.start < right.end && right.start < left.end;
|
|
309
|
+
}
|
|
310
|
+
|
|
192
311
|
function compactRecord(value) {
|
|
193
312
|
return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
|
|
194
313
|
}
|
|
@@ -53,6 +53,7 @@ export function replaySemanticEditProjection(input = {}) {
|
|
|
53
53
|
function replayProjectionEdit(edit, context) {
|
|
54
54
|
if (edit.status === 'already-applied') return replayEditRecord(edit, 'already-applied', undefined, ['projection-edit-already-applied']);
|
|
55
55
|
if (typeof edit.replacementText !== 'string') return replayEditRecord(edit, 'blocked', undefined, ['missing-replacement-text']);
|
|
56
|
+
if (edit.editKind === 'insert') return replayInsertionEdit(edit, context);
|
|
56
57
|
const offset = checkRange(edit, { start: edit.headStart, end: edit.headEnd }, context.currentSourceText, 'head-offset');
|
|
57
58
|
if (offset) return replayEditRecord(edit, offset.status, offset.range, [offset.reason]);
|
|
58
59
|
const symbol = findCurrentSymbol(edit, context.currentSymbols);
|
|
@@ -64,13 +65,27 @@ function replayProjectionEdit(edit, context) {
|
|
|
64
65
|
]);
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
function replayInsertionEdit(edit, context) {
|
|
69
|
+
const inserted = findCurrentSymbol(edit, context.currentSymbols);
|
|
70
|
+
const insertedRange = spanOffsets(context.currentSourceText, inserted?.sourceSpan);
|
|
71
|
+
const already = checkRange(edit, insertedRange, context.currentSourceText, 'current-inserted-symbol');
|
|
72
|
+
if (already?.status === 'already-applied') return replayEditRecord(edit, 'already-applied', already.range, [already.reason]);
|
|
73
|
+
const anchor = findInsertionAnchorSymbol(edit, context.currentSymbols);
|
|
74
|
+
const range = insertionRange(edit, anchor, context.currentSourceText);
|
|
75
|
+
if (range) return replayEditRecord(edit, 'applied', range, [anchor ? 'current-insertion-anchor' : `current-${edit.insertionMode}`]);
|
|
76
|
+
return replayEditRecord(edit, anchor ? 'conflict' : 'stale', undefined, [
|
|
77
|
+
anchor ? 'current-insertion-anchor-unusable' : 'current-insertion-anchor-missing'
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
|
|
67
81
|
function checkRange(edit, range, sourceText, label) {
|
|
68
82
|
if (!range || range.end < range.start) return undefined;
|
|
69
83
|
const current = sourceText.slice(range.start, range.end);
|
|
70
84
|
const currentHash = hashSemanticValue(current);
|
|
71
|
-
if (edit.
|
|
85
|
+
if (edit.replacementSpanTextHash && currentHash === edit.replacementSpanTextHash) return { status: 'already-applied', range, reason: `${label}-matches-replacement-span` };
|
|
72
86
|
if (edit.replacementTextHash && currentHash === edit.replacementTextHash) return { status: 'already-applied', range, reason: `${label}-matches-replacement` };
|
|
73
87
|
if (current === edit.replacementText) return { status: 'already-applied', range, reason: `${label}-matches-replacement-text` };
|
|
88
|
+
if (edit.deletedTextHash && currentHash === edit.deletedTextHash) return { status: 'applied', range, reason: `${label}-matches-deleted` };
|
|
74
89
|
return undefined;
|
|
75
90
|
}
|
|
76
91
|
|
|
@@ -81,6 +96,7 @@ function replayEditRecord(edit, status, range, reasonCodes) {
|
|
|
81
96
|
semanticIdentityHash: edit.semanticIdentityHash,
|
|
82
97
|
sourceIdentityHash: edit.sourceIdentityHash,
|
|
83
98
|
editContentHash: edit.editContentHash,
|
|
99
|
+
editKind: edit.editKind,
|
|
84
100
|
sourcePath: edit.targetSourcePath ?? edit.sourcePath,
|
|
85
101
|
symbolName: edit.targetSymbolName ?? edit.symbolName,
|
|
86
102
|
symbolKind: edit.targetSymbolKind ?? edit.symbolKind,
|
|
@@ -115,6 +131,24 @@ function findCurrentSymbol(edit, symbols) {
|
|
|
115
131
|
return symbols.find((symbol) => symbol.name === name && (!kind || symbol.kind === kind));
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
function findInsertionAnchorSymbol(edit, symbols) {
|
|
135
|
+
return symbols.find((symbol) => [symbol.ownershipKey, symbol.key, symbol.id].some((key) => key && key === edit.insertionAnchorKey))
|
|
136
|
+
?? symbols.find((symbol) => symbol.name === edit.insertionAnchorSymbolName && (!edit.insertionAnchorSymbolKind || symbol.kind === edit.insertionAnchorSymbolKind));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function insertionRange(edit, anchor, sourceText) {
|
|
140
|
+
if (edit.insertionMode === 'file-start') return { start: 0, end: 0 };
|
|
141
|
+
if (edit.insertionMode === 'file-end') return { start: sourceText.length, end: sourceText.length };
|
|
142
|
+
const anchorRange = spanOffsets(sourceText, anchor?.sourceSpan);
|
|
143
|
+
if (!anchorRange) return undefined;
|
|
144
|
+
if (edit.insertionMode === 'before') return { start: anchorRange.start, end: anchorRange.start };
|
|
145
|
+
if (edit.insertionMode === 'after') {
|
|
146
|
+
const offset = sourceText[anchorRange.end] === '\n' ? anchorRange.end + 1 : anchorRange.end;
|
|
147
|
+
return { start: offset, end: offset };
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
|
|
118
152
|
function replayStatus(reasonCodes, edits, projection) {
|
|
119
153
|
if (reasonCodes.some((reason) => reason !== 'current-source-hash-mismatch')) return 'blocked';
|
|
120
154
|
if (!edits.length && !(projection.edits ?? []).length) return 'evidence-only';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export function semanticEditInsertionAnchor(region, context) {
|
|
2
|
+
if (region.changeKind !== 'added') return undefined;
|
|
3
|
+
const workerSymbol = symbolForRegion(context.workerSymbols, region);
|
|
4
|
+
if (!workerSymbol?.sourceSpan) return fallbackInsertion(region, context, 'worker-symbol-span-missing');
|
|
5
|
+
const workers = uniqueSymbols(context.workerSymbols)
|
|
6
|
+
.filter((symbol) => symbol.id !== workerSymbol.id && symbol.key !== workerSymbol.key)
|
|
7
|
+
.filter((symbol) => hasSymbol(context.baseSymbols, symbol));
|
|
8
|
+
const before = nearestBefore(workers, workerSymbol);
|
|
9
|
+
const after = nearestAfter(workers, workerSymbol);
|
|
10
|
+
const anchor = before
|
|
11
|
+
? insertionFromSymbol('after', before, context, 'nearest-previous-base-symbol')
|
|
12
|
+
: after
|
|
13
|
+
? insertionFromSymbol('before', after, context, 'nearest-next-base-symbol')
|
|
14
|
+
: fallbackInsertion(region, context, 'no-neighbor-base-symbol');
|
|
15
|
+
return compactRecord({
|
|
16
|
+
...anchor,
|
|
17
|
+
insertedSymbolId: workerSymbol.id,
|
|
18
|
+
insertedSymbolName: workerSymbol.name,
|
|
19
|
+
insertedSymbolKind: workerSymbol.kind,
|
|
20
|
+
insertedSourceSpan: workerSymbol.sourceSpan,
|
|
21
|
+
insertedSourcePath: workerSymbol.sourcePath
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function insertionFromSymbol(mode, symbol, context, reasonCode) {
|
|
26
|
+
const headSymbol = symbolForExisting(context.headSymbols, symbol);
|
|
27
|
+
return compactRecord({
|
|
28
|
+
mode,
|
|
29
|
+
anchorKey: symbol.key ?? symbol.ownershipKey ?? symbol.id,
|
|
30
|
+
anchorSymbolId: symbol.id,
|
|
31
|
+
anchorSymbolName: symbol.name,
|
|
32
|
+
anchorSymbolKind: symbol.kind,
|
|
33
|
+
baseSpan: symbolForExisting(context.baseSymbols, symbol)?.sourceSpan,
|
|
34
|
+
workerAnchorSpan: symbol.sourceSpan,
|
|
35
|
+
headSpan: headSymbol?.sourceSpan,
|
|
36
|
+
sourcePath: headSymbol?.sourcePath ?? symbol.sourcePath,
|
|
37
|
+
reasonCodes: [headSymbol ? reasonCode : `${reasonCode}:head-anchor-missing`]
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function fallbackInsertion(region, context, reasonCode) {
|
|
42
|
+
const mode = region.regionKind === 'import' ? 'file-start' : 'file-end';
|
|
43
|
+
return compactRecord({
|
|
44
|
+
mode,
|
|
45
|
+
sourcePath: region.sourcePath ?? context.workerChangeSet.sourcePath,
|
|
46
|
+
reasonCodes: [reasonCode, `fallback-${mode}`]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function nearestBefore(symbols, target) {
|
|
51
|
+
return symbols
|
|
52
|
+
.filter((symbol) => spanEndLine(symbol.sourceSpan) <= spanStartLine(target.sourceSpan))
|
|
53
|
+
.sort((left, right) => spanEndLine(right.sourceSpan) - spanEndLine(left.sourceSpan))[0];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function nearestAfter(symbols, target) {
|
|
57
|
+
return symbols
|
|
58
|
+
.filter((symbol) => spanStartLine(symbol.sourceSpan) >= spanEndLine(target.sourceSpan))
|
|
59
|
+
.sort((left, right) => spanStartLine(left.sourceSpan) - spanStartLine(right.sourceSpan))[0];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function symbolForRegion(symbols, region) {
|
|
63
|
+
return symbolForKeys(symbols, [region.key, region.symbolId, region.symbolName].filter(Boolean));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function symbolForExisting(symbols, symbol) {
|
|
67
|
+
return symbolForKeys(symbols, symbolKeys(symbol));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hasSymbol(symbols, symbol) {
|
|
71
|
+
return Boolean(symbolForExisting(symbols, symbol));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function symbolForKeys(symbols, keys) {
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
const symbol = symbols.get(key);
|
|
77
|
+
if (symbol) return symbol;
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function uniqueSymbols(symbols) {
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
const result = [];
|
|
85
|
+
for (const symbol of symbols.values()) {
|
|
86
|
+
const key = symbol.id ?? `${symbol.key}:${symbol.name}`;
|
|
87
|
+
if (seen.has(key)) continue;
|
|
88
|
+
seen.add(key);
|
|
89
|
+
result.push(symbol);
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function symbolKeys(symbol) {
|
|
95
|
+
return [symbol.key, symbol.ownershipKey, symbol.id, symbol.name].filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function spanStartLine(span) {
|
|
99
|
+
return typeof span?.startLine === 'number' ? span.startLine : Number.MAX_SAFE_INTEGER;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function spanEndLine(span) {
|
|
103
|
+
return typeof span?.endLine === 'number' ? span.endLine : spanStartLine(span);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function compactRecord(value) {
|
|
107
|
+
return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
|
|
108
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
semanticEditAdmission,
|
|
13
13
|
summarizeSemanticEditOperations
|
|
14
14
|
} from './semanticEditScriptClassification.js';
|
|
15
|
+
import { semanticEditInsertionAnchor } from './semanticEditInsertionAnchors.js';
|
|
15
16
|
import { sourceTextForSpan } from './sourceTextForSpan.js';
|
|
16
17
|
import { semanticEditIdentityFields, semanticEditOperationContentHash } from './semanticEditIdentityRecords.js';
|
|
17
18
|
|
|
@@ -168,10 +169,11 @@ function semanticEditOperation(region, index, context, input) {
|
|
|
168
169
|
const identityRecord = semanticEditIdentityRecord({ kind, region, anchor });
|
|
169
170
|
const identity = semanticEditIdentityFields(identityRecord);
|
|
170
171
|
return compactRecord({
|
|
171
|
-
id: `semantic_edit_op_${idFragment(
|
|
172
|
+
id: `semantic_edit_op_${idFragment([input.id ?? 'semantic_edit', anchorKey, index].join(':'))}`,
|
|
172
173
|
kind,
|
|
173
174
|
changeKind: region.changeKind,
|
|
174
175
|
anchor,
|
|
176
|
+
insertion: semanticEditInsertionAnchor(region, context),
|
|
175
177
|
...identity,
|
|
176
178
|
spans: compactRecord({
|
|
177
179
|
base: baseSymbol?.sourceSpan ?? region.metadata?.changedRegionProjection?.before?.sourceSpan,
|
|
@@ -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