@shapeshift-labs/frontier-lang-compiler 0.2.102 → 0.2.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
- package/dist/declarations/bidirectional-target-change.d.ts +10 -0
- package/dist/declarations/js-ts-safe-member-merge.d.ts +58 -0
- package/dist/declarations/js-ts-safe-merge.d.ts +120 -0
- package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
- package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
- package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
- package/dist/declarations/native-import-losses.d.ts +3 -0
- package/dist/declarations/native-project-admission-semantic-evidence.d.ts +34 -0
- package/dist/declarations/native-project-admission.d.ts +6 -10
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
- package/dist/declarations/semantic-edit-script.d.ts +10 -4
- package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
- package/dist/declarations/semantic-patch-bundle-overlaps.d.ts +1 -0
- package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
- package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
- package/dist/declarations/semantic-transform-identity.d.ts +3 -0
- package/dist/declarations/source-preservation.d.ts +72 -0
- package/dist/declarations/universal-capability.d.ts +4 -0
- package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
- package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
- package/dist/declarations/universal-conversion-plan.d.ts +6 -1
- package/dist/declarations/universal-representation-coverage.d.ts +90 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
- package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
- package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
- package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
- package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
- package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +14 -2
- package/dist/internal/index-impl/diffNativeSymbols.js +82 -1
- package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
- package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +1 -1
- package/dist/internal/index-impl/projectImportAdmissionSemanticWarnings.js +178 -0
- package/dist/internal/index-impl/projectImportAdmissionSummaries.js +22 -3
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +54 -69
- package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
- package/dist/internal/index-impl/replaySemanticEditProjection.js +78 -78
- package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
- package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
- package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
- package/dist/internal/index-impl/semanticEditImportProjection.js +53 -0
- package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
- package/dist/internal/index-impl/semanticEditProjectionRecord.js +108 -0
- package/dist/internal/index-impl/semanticEditReplayAnchors.js +63 -0
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
- package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
- package/dist/internal/index-impl/semanticEditScripts.js +4 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +32 -0
- package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
- package/dist/internal/index-impl/semanticPatchBundleAdmission.js +92 -9
- package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +33 -16
- package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
- package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
- package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
- package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
- package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
- package/dist/js-ts-safe-member-merge-result.js +158 -0
- package/dist/js-ts-safe-member-merge.js +202 -0
- package/dist/js-ts-safe-merge-analyze.js +279 -0
- package/dist/js-ts-safe-merge-constants.js +50 -0
- package/dist/js-ts-safe-merge-context.js +118 -0
- package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
- package/dist/js-ts-safe-merge-ledger.js +85 -0
- package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
- package/dist/js-ts-safe-merge-parse-statements.js +155 -0
- package/dist/js-ts-safe-merge-plan.js +190 -0
- package/dist/js-ts-safe-merge.js +175 -0
- package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
- package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
- package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
- package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
- package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
- package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
- package/dist/js-ts-semantic-merge-contracts.js +217 -0
- package/dist/js-ts-semantic-merge-member-containers.js +100 -0
- package/dist/js-ts-semantic-merge-member-keys.js +142 -0
- package/dist/js-ts-semantic-merge-member-segments.js +185 -0
- package/dist/js-ts-semantic-merge-member-source.js +64 -0
- package/dist/js-ts-semantic-merge-member-utils.js +18 -0
- package/dist/js-ts-semantic-merge-parse.js +15 -0
- package/dist/js-ts-semantic-merge.js +21 -0
- package/dist/lightweight-dependency-effects.js +51 -0
- package/dist/lightweight-dependency-language.js +12 -1
- package/dist/lightweight-dependency-relations.js +14 -27
- package/dist/native-region-scanner-core.js +33 -1
- package/dist/native-region-scanner-csharp.js +151 -0
- package/dist/native-region-scanner-dart.js +91 -0
- package/dist/native-region-scanner-dynamic.js +21 -151
- package/dist/native-region-scanner-functional.js +40 -13
- package/dist/native-region-scanner-java.js +97 -0
- package/dist/native-region-scanner-js-class.js +100 -0
- package/dist/native-region-scanner-js-helpers.js +28 -86
- package/dist/native-region-scanner-js-imports.js +121 -1
- package/dist/native-region-scanner-js-nested.js +96 -8
- package/dist/native-region-scanner-js-structure.js +27 -0
- package/dist/native-region-scanner-js-types.js +99 -0
- package/dist/native-region-scanner-js.js +70 -118
- package/dist/native-region-scanner-kotlin.js +94 -0
- package/dist/native-region-scanner-main.js +15 -181
- package/dist/native-region-scanner-php.js +80 -0
- package/dist/native-region-scanner-python.js +62 -0
- package/dist/native-region-scanner-ruby.js +72 -0
- package/dist/native-region-scanner-scala.js +91 -0
- package/dist/native-region-scanner-spans.js +74 -0
- package/dist/native-region-scanner-swift.js +155 -0
- package/dist/native-region-scanner.js +14 -10
- package/dist/native-source-ledger-helpers.js +195 -0
- package/dist/native-source-ledger.js +306 -0
- package/dist/native-source-preservation-scanner.js +4 -0
- package/dist/semantic-import-callsite-regions.js +136 -0
- package/dist/semantic-import-effect-regions.js +283 -0
- package/dist/semantic-import-regions.js +11 -2
- package/dist/semantic-import-sidecar-entry.js +16 -2
- package/dist/semantic-import-sidecar-types.d.ts +2 -0
- package/dist/semantic-sidecar-example.js +68 -0
- package/dist/universal-capability-matrix.js +23 -0
- package/dist/universal-conversion-artifact-query.js +79 -2
- package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
- package/dist/universal-conversion-artifact-summary.js +33 -1
- package/dist/universal-conversion-artifacts.js +13 -48
- package/dist/universal-conversion-plan-scoring.js +21 -1
- package/dist/universal-conversion-plan-summary.js +30 -0
- package/dist/universal-conversion-plan.js +25 -9
- package/dist/universal-conversion-route-metadata.js +96 -0
- package/dist/universal-conversion-route-operations.js +7 -0
- package/dist/universal-representation-coverage.js +193 -0
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { spanOffsets } from './semanticEditSourceRanges.js';
|
|
3
|
+
|
|
4
|
+
export function explicitSourceReplacementEditForOperation(operation, identity, headSourceText, order) {
|
|
5
|
+
const backprojection = operation.metadata?.sourceBackprojection;
|
|
6
|
+
if (backprojection?.mode !== 'cross-language-explicit-source-replacement') return undefined;
|
|
7
|
+
const replacement = backprojection.sourceReplacementText;
|
|
8
|
+
const range = spanOffsets(headSourceText, backprojection.sourceEditSpan ?? operation.spans?.head ?? operation.spans?.base);
|
|
9
|
+
const anchorRange = spanOffsets(headSourceText, operation.anchor?.sourceSpan);
|
|
10
|
+
const reasons = [];
|
|
11
|
+
if (typeof replacement !== 'string') reasons.push(`source-replacement-text-missing:${operation.id}`);
|
|
12
|
+
if (!range) reasons.push(`head-span-not-resolvable:${operation.id}`);
|
|
13
|
+
if (reasons.length) return { ok: false, reasonCodes: reasons };
|
|
14
|
+
const current = headSourceText.slice(range.start, range.end);
|
|
15
|
+
const currentHash = hashSemanticValue(current);
|
|
16
|
+
const replacementHash = hashSemanticValue(replacement);
|
|
17
|
+
const expectedCurrentHash = backprojection.sourceEditTextHash ?? operation.hashes?.headTextHash ?? operation.hashes?.baseTextHash;
|
|
18
|
+
if (expectedCurrentHash && currentHash !== expectedCurrentHash) reasons.push(`head-span-hash-mismatch:${operation.id}`);
|
|
19
|
+
if (backprojection.sourceReplacementTextHash && replacementHash !== backprojection.sourceReplacementTextHash) {
|
|
20
|
+
reasons.push(`source-replacement-text-hash-mismatch:${operation.id}`);
|
|
21
|
+
}
|
|
22
|
+
if (reasons.length) return { ok: false, reasonCodes: reasons };
|
|
23
|
+
return {
|
|
24
|
+
ok: true,
|
|
25
|
+
value: {
|
|
26
|
+
operationId: operation.id,
|
|
27
|
+
order,
|
|
28
|
+
...identity,
|
|
29
|
+
editKind: 'replace',
|
|
30
|
+
sourceRangeKind: 'cross-language-explicit-source-replacement',
|
|
31
|
+
start: range.start,
|
|
32
|
+
end: range.end,
|
|
33
|
+
headAnchorStart: anchorRange?.start,
|
|
34
|
+
headAnchorEnd: anchorRange?.end,
|
|
35
|
+
replacement,
|
|
36
|
+
replacementSpanText: replacement,
|
|
37
|
+
current
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { spanOffsets } from './semanticEditSourceRanges.js';
|
|
3
|
+
|
|
4
|
+
export function alreadyAppliedImportEditForOperation(operation, identity, spanText, headSourceText, workerOffsets, order, context) {
|
|
5
|
+
if (!isAddImportOperation(operation) || typeof headSourceText !== 'string') return undefined;
|
|
6
|
+
const match = findHeadImportSymbol(operation, context.headSymbols);
|
|
7
|
+
const range = spanOffsets(headSourceText, match?.symbol?.sourceSpan);
|
|
8
|
+
if (!range) return undefined;
|
|
9
|
+
const current = headSourceText.slice(range.start, range.end);
|
|
10
|
+
if (!headImportMatchesOperation(operation, spanText, current, match.symbol)) return undefined;
|
|
11
|
+
return {
|
|
12
|
+
operationId: operation.id,
|
|
13
|
+
order,
|
|
14
|
+
...identity,
|
|
15
|
+
start: range.start,
|
|
16
|
+
end: range.end,
|
|
17
|
+
workerStart: workerOffsets.start,
|
|
18
|
+
workerEnd: workerOffsets.end,
|
|
19
|
+
replacement: current,
|
|
20
|
+
replacementSpanText: spanText,
|
|
21
|
+
current,
|
|
22
|
+
alreadyApplied: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function findHeadImportSymbol(operation, symbols) {
|
|
27
|
+
const symbolList = Array.isArray(symbols) ? symbols : [];
|
|
28
|
+
const exactKeys = [
|
|
29
|
+
operation.anchor?.key,
|
|
30
|
+
operation.anchor?.symbolId,
|
|
31
|
+
operation.insertion?.insertedSymbolId
|
|
32
|
+
].filter(Boolean);
|
|
33
|
+
const exact = symbolList.find((symbol) => [symbol.ownershipKey, symbol.key, symbol.id].some((key) => key && exactKeys.includes(key)));
|
|
34
|
+
if (exact) return { symbol: exact, exact: true };
|
|
35
|
+
const name = operation.insertion?.insertedSymbolName ?? operation.anchor?.symbolName;
|
|
36
|
+
const kind = operation.insertion?.insertedSymbolKind ?? operation.anchor?.symbolKind;
|
|
37
|
+
const semantic = symbolList.find((symbol) => symbol.name === name && (!kind || symbol.kind === kind));
|
|
38
|
+
return semantic ? { symbol: semantic, exact: false } : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function headImportMatchesOperation(operation, spanText, current, symbol) {
|
|
42
|
+
const workerTextHash = operation.hashes?.workerTextHash ?? hashSemanticValue(spanText);
|
|
43
|
+
const workerSpanHash = operation.hashes?.workerSpanHash ?? workerTextHash;
|
|
44
|
+
const currentHash = hashSemanticValue(current);
|
|
45
|
+
if ([workerTextHash, workerSpanHash].includes(currentHash)) return true;
|
|
46
|
+
if ([workerTextHash, workerSpanHash].includes(symbol?.spanHash)) return true;
|
|
47
|
+
const signatureHash = operation.hashes?.afterSignatureHash;
|
|
48
|
+
return Boolean(signatureHash && symbol?.signatureHash === signatureHash);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isAddImportOperation(operation) {
|
|
52
|
+
return operation?.kind === 'addImport' || (operation?.changeKind === 'added' && operation?.anchor?.regionKind === 'import');
|
|
53
|
+
}
|
|
@@ -7,14 +7,16 @@ export function markCoveredSemanticEditOperations(operations, context) {
|
|
|
7
7
|
worker: nativeImportSourceText(context.worker)
|
|
8
8
|
};
|
|
9
9
|
return (operations ?? []).map((operation) => {
|
|
10
|
-
const
|
|
10
|
+
const childCoverage = coveredByChildOperations(operation, operations, sourceText);
|
|
11
|
+
const parentCoverage = childCoverage.length ? [] : coveredByParentOperations(operation, operations, sourceText);
|
|
12
|
+
const coveredBy = childCoverage.length ? childCoverage : parentCoverage;
|
|
11
13
|
if (!coveredBy.length) return operation;
|
|
12
14
|
return {
|
|
13
15
|
...operation,
|
|
14
16
|
status: 'covered',
|
|
15
17
|
readiness: 'ready',
|
|
16
18
|
confidence: Math.max(operation.confidence ?? 0, 0.82),
|
|
17
|
-
reasonCodes: uniqueStrings([...(operation.reasonCodes ?? []),
|
|
19
|
+
reasonCodes: uniqueStrings([...(operation.reasonCodes ?? []), coverageReason(childCoverage)]),
|
|
18
20
|
evidenceIds: uniqueStrings(operation.evidenceIds ?? []),
|
|
19
21
|
metadata: {
|
|
20
22
|
...(operation.metadata ?? {}),
|
|
@@ -24,6 +26,10 @@ export function markCoveredSemanticEditOperations(operations, context) {
|
|
|
24
26
|
});
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
function coverageReason(childCoverage) {
|
|
30
|
+
return childCoverage.length ? 'container-covered-by-child-edits' : 'child-covered-by-container-edit';
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
function coveredByChildOperations(container, operations, sourceText) {
|
|
28
34
|
if (!isCoverableContainer(container)) return [];
|
|
29
35
|
const containerBase = spanOffsets(sourceText.base, container.spans?.base);
|
|
@@ -43,7 +49,31 @@ function isCoverableContainer(operation) {
|
|
|
43
49
|
if (operation.changeKind !== 'modified') return false;
|
|
44
50
|
if (!operation.spans?.base || !operation.spans?.worker) return false;
|
|
45
51
|
const kind = String(operation.anchor?.regionKind ?? operation.regionKind ?? '');
|
|
46
|
-
return
|
|
52
|
+
return ['type', 'config', 'content', 'route', 'property', 'controlFlow', 'effect', 'mutation'].includes(kind);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function coveredByParentOperations(child, operations, sourceText) {
|
|
56
|
+
if (child.changeKind !== 'added' && child.changeKind !== 'removed') return [];
|
|
57
|
+
if (!isSemanticFactRegion(child)) return [];
|
|
58
|
+
const side = child.changeKind === 'removed' ? 'base' : 'worker';
|
|
59
|
+
const childRange = spanOffsets(sourceText[side], child.spans?.[side]);
|
|
60
|
+
if (!childRange) return [];
|
|
61
|
+
return (operations ?? []).filter((parent) => parent.id !== child.id
|
|
62
|
+
&& parent.changeKind === child.changeKind
|
|
63
|
+
&& ['portable', 'already-applied'].includes(parent.status)
|
|
64
|
+
&& parent.anchor?.regionKind === 'body'
|
|
65
|
+
&& sameSourcePath(parent, child)
|
|
66
|
+
&& contained(childRange, spanOffsets(sourceText[side], parent.spans?.[side])));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isSemanticFactRegion(operation) {
|
|
70
|
+
return ['controlFlow', 'effect', 'mutation', 'call'].includes(String(operation.anchor?.regionKind ?? ''));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sameSourcePath(left, right) {
|
|
74
|
+
const leftPath = left.anchor?.sourcePath ?? left.insertion?.sourcePath;
|
|
75
|
+
const rightPath = right.anchor?.sourcePath ?? right.insertion?.sourcePath;
|
|
76
|
+
return !leftPath || !rightPath || leftPath === rightPath;
|
|
47
77
|
}
|
|
48
78
|
|
|
49
79
|
function childEdit(operation, sourceText, containerBase) {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { semanticEditIdentityFields } from './semanticEditIdentityRecords.js';
|
|
3
|
+
|
|
4
|
+
export function projectionEditRecord(edit) {
|
|
5
|
+
const deletedTextHash = hashSemanticValue(edit.current);
|
|
6
|
+
const replacementTextHash = hashSemanticValue(edit.replacement);
|
|
7
|
+
const replacementSpanText = edit.replacementSpanText ?? edit.replacement;
|
|
8
|
+
const identity = semanticEditIdentityFields(edit);
|
|
9
|
+
const sourceIdentity = sourceIdentityAnchorFields(edit);
|
|
10
|
+
return compactRecord({
|
|
11
|
+
operationId: edit.operationId,
|
|
12
|
+
status: edit.alreadyApplied ? 'already-applied' : 'applied',
|
|
13
|
+
kind: edit.kind,
|
|
14
|
+
editKind: edit.editKind,
|
|
15
|
+
changeKind: edit.changeKind,
|
|
16
|
+
anchorKey: edit.anchorKey,
|
|
17
|
+
conflictKey: edit.conflictKey,
|
|
18
|
+
regionId: edit.regionId,
|
|
19
|
+
regionKind: edit.regionKind,
|
|
20
|
+
sourcePath: edit.sourcePath,
|
|
21
|
+
originalSourcePath: edit.originalSourcePath,
|
|
22
|
+
targetAnchorKey: edit.targetAnchorKey,
|
|
23
|
+
targetSourcePath: edit.targetSourcePath,
|
|
24
|
+
targetSymbolName: edit.targetSymbolName,
|
|
25
|
+
targetSymbolKind: edit.targetSymbolKind,
|
|
26
|
+
symbolId: edit.symbolId,
|
|
27
|
+
symbolName: edit.symbolName,
|
|
28
|
+
symbolKind: edit.symbolKind,
|
|
29
|
+
...identity,
|
|
30
|
+
...sourceIdentity,
|
|
31
|
+
operationContentHash: edit.operationContentHash,
|
|
32
|
+
editContentHash: hashSemanticValue(compactRecord({
|
|
33
|
+
semanticIdentityHash: identity.semanticIdentityHash,
|
|
34
|
+
sourceIdentityHash: identity.sourceIdentityHash,
|
|
35
|
+
sourceIdentityStatus: sourceIdentity.sourceIdentityStatus,
|
|
36
|
+
sourceIdentityAnchorKey: sourceIdentity.sourceIdentityAnchorKey,
|
|
37
|
+
targetIdentityAnchorKey: sourceIdentity.targetIdentityAnchorKey,
|
|
38
|
+
sourceRangeKind: edit.sourceRangeKind,
|
|
39
|
+
deletedTextHash,
|
|
40
|
+
replacementTextHash,
|
|
41
|
+
status: edit.alreadyApplied ? 'already-applied' : 'applied'
|
|
42
|
+
})),
|
|
43
|
+
sourceRangeKind: edit.sourceRangeKind,
|
|
44
|
+
headStart: edit.start,
|
|
45
|
+
headEnd: edit.end,
|
|
46
|
+
workerStart: edit.workerStart,
|
|
47
|
+
workerEnd: edit.workerEnd,
|
|
48
|
+
editOrder: edit.order,
|
|
49
|
+
headAnchorStart: edit.headAnchorStart,
|
|
50
|
+
headAnchorEnd: edit.headAnchorEnd,
|
|
51
|
+
workerAnchorStart: edit.workerAnchorStart,
|
|
52
|
+
workerAnchorEnd: edit.workerAnchorEnd,
|
|
53
|
+
deletedBytes: edit.current.length,
|
|
54
|
+
replacementBytes: edit.replacement.length,
|
|
55
|
+
deletedTextHash,
|
|
56
|
+
replacementTextHash,
|
|
57
|
+
deletedText: deletedTextForEdit(edit),
|
|
58
|
+
deletedTextLineEndingStableHash: lineEndingStableTextHash(edit.current),
|
|
59
|
+
replacementTextLineEndingStableHash: lineEndingStableTextHash(edit.replacement),
|
|
60
|
+
anchorDeletedTextHash: edit.anchorDeletedTextHash,
|
|
61
|
+
anchorReplacementTextHash: edit.anchorReplacementTextHash,
|
|
62
|
+
replacementSpanTextHash: hashSemanticValue(replacementSpanText),
|
|
63
|
+
replacementSpanTextLineEndingStableHash: lineEndingStableTextHash(replacementSpanText),
|
|
64
|
+
insertionMode: edit.insertion?.mode,
|
|
65
|
+
insertionAnchorKey: edit.insertion?.anchorKey,
|
|
66
|
+
insertionAnchorSymbolName: edit.insertion?.anchorSymbolName,
|
|
67
|
+
insertionAnchorSymbolKind: edit.insertion?.anchorSymbolKind,
|
|
68
|
+
insertionAnchorCandidates: edit.insertion?.anchorCandidates,
|
|
69
|
+
replacementText: edit.replacement
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function deletedTextForEdit(edit) {
|
|
74
|
+
return edit.sourceRangeKind === 'cross-language-explicit-source-replacement' ? edit.current : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function sourceIdentityAnchorFields(edit) {
|
|
78
|
+
const sourceIdentityAnchorKey = edit.sourceIdentityAnchorKey ?? edit.anchorKey;
|
|
79
|
+
const targetIdentityAnchorKey = edit.targetIdentityAnchorKey ?? edit.targetAnchorKey ?? sourceIdentityAnchorKey;
|
|
80
|
+
const sourceIdentitySourcePath = edit.sourceIdentitySourcePath ?? edit.originalSourcePath ?? edit.sourcePath;
|
|
81
|
+
const targetIdentitySourcePath = edit.targetIdentitySourcePath ?? edit.targetSourcePath ?? edit.sourcePath;
|
|
82
|
+
const moved = Boolean(
|
|
83
|
+
(sourceIdentityAnchorKey && targetIdentityAnchorKey && sourceIdentityAnchorKey !== targetIdentityAnchorKey)
|
|
84
|
+
|| (sourceIdentitySourcePath && targetIdentitySourcePath && sourceIdentitySourcePath !== targetIdentitySourcePath)
|
|
85
|
+
);
|
|
86
|
+
return compactRecord({
|
|
87
|
+
sourceIdentityStatus: edit.sourceIdentityStatus ?? (moved ? 'moved-source' : 'same-source'),
|
|
88
|
+
sourceIdentityAnchorKey,
|
|
89
|
+
targetIdentityAnchorKey,
|
|
90
|
+
sourceIdentitySourcePath,
|
|
91
|
+
targetIdentitySourcePath
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function lineEndingStableTextHash(value) {
|
|
96
|
+
const normalized = lineEndingStableText(value);
|
|
97
|
+
return normalized === undefined ? undefined : hashSemanticValue(normalized);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function lineEndingStableText(value) {
|
|
101
|
+
if (typeof value !== 'string') return undefined;
|
|
102
|
+
const normalized = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
103
|
+
return normalized.length > 1 && normalized.endsWith('\n') ? normalized.slice(0, -1) : normalized;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function compactRecord(value) {
|
|
107
|
+
return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
|
|
108
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { afterLineOffset, spanOffsets } from './semanticEditSourceRanges.js';
|
|
2
|
+
|
|
3
|
+
export function findCurrentSymbol(edit, symbols) {
|
|
4
|
+
const exact = symbols.find((symbol) => [symbol.ownershipKey, symbol.key, symbol.id].some((key) => key && [
|
|
5
|
+
edit.anchorKey,
|
|
6
|
+
edit.targetAnchorKey,
|
|
7
|
+
edit.symbolId
|
|
8
|
+
].includes(key)));
|
|
9
|
+
if (exact) return exact;
|
|
10
|
+
const name = edit.targetSymbolName ?? edit.symbolName;
|
|
11
|
+
const kind = edit.targetSymbolKind ?? edit.symbolKind;
|
|
12
|
+
return symbols.find((symbol) => symbol.name === name && (!kind || symbol.kind === kind));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function findInsertionAnchor(edit, symbols) {
|
|
16
|
+
for (const candidate of insertionAnchorCandidates(edit)) {
|
|
17
|
+
const symbol = findInsertionAnchorSymbol(candidate, symbols);
|
|
18
|
+
if (symbol) return { candidate, symbol };
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function insertionAnchorCandidates(edit) {
|
|
24
|
+
const primary = {
|
|
25
|
+
mode: edit.insertionMode,
|
|
26
|
+
anchorKey: edit.insertionAnchorKey,
|
|
27
|
+
anchorSymbolName: edit.insertionAnchorSymbolName,
|
|
28
|
+
anchorSymbolKind: edit.insertionAnchorSymbolKind
|
|
29
|
+
};
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
const result = [];
|
|
32
|
+
for (const candidate of [primary, ...(Array.isArray(edit.insertionAnchorCandidates) ? edit.insertionAnchorCandidates : [])]) {
|
|
33
|
+
if (!candidate || (candidate.mode !== 'before' && candidate.mode !== 'after')) continue;
|
|
34
|
+
const key = [candidate.mode, candidate.anchorKey, candidate.anchorSymbolId, candidate.anchorSymbolName, candidate.anchorSymbolKind].join('\0');
|
|
35
|
+
if (seen.has(key)) continue;
|
|
36
|
+
seen.add(key);
|
|
37
|
+
result.push(candidate);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function hasSymbolAnchorIdentity(candidate) {
|
|
43
|
+
return Boolean(candidate.anchorKey || candidate.anchorSymbolId || candidate.anchorSymbolName);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function insertionRange(edit, candidate, anchor, sourceText) {
|
|
47
|
+
if (edit.insertionMode === 'file-start') return { start: 0, end: 0 };
|
|
48
|
+
if (edit.insertionMode === 'file-end') return { start: sourceText.length, end: sourceText.length };
|
|
49
|
+
const mode = candidate?.mode ?? edit.insertionMode;
|
|
50
|
+
const anchorRange = spanOffsets(sourceText, anchor?.sourceSpan);
|
|
51
|
+
if (!anchorRange) return undefined;
|
|
52
|
+
if (mode === 'before') return { start: anchorRange.start, end: anchorRange.start };
|
|
53
|
+
if (mode === 'after') {
|
|
54
|
+
return { start: afterLineOffset(sourceText, anchorRange.end), end: afterLineOffset(sourceText, anchorRange.end) };
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function findInsertionAnchorSymbol(candidate, symbols) {
|
|
60
|
+
const keys = [candidate.anchorKey, candidate.anchorSymbolId].filter(Boolean);
|
|
61
|
+
return symbols.find((symbol) => [symbol.ownershipKey, symbol.key, symbol.id].some((key) => key && keys.includes(key)))
|
|
62
|
+
?? symbols.find((symbol) => symbol.name === candidate.anchorSymbolName && (!candidate.anchorSymbolKind || symbol.kind === candidate.anchorSymbolKind));
|
|
63
|
+
}
|
|
@@ -8,6 +8,7 @@ export function replayEditDiagnostics(edit, status, range, reasonCodes, sourceTe
|
|
|
8
8
|
status,
|
|
9
9
|
operationId: edit.operationId,
|
|
10
10
|
sourcePath: edit.targetSourcePath ?? edit.sourcePath,
|
|
11
|
+
...sourceIdentityDiagnosticContext(edit),
|
|
11
12
|
symbolName: edit.targetSymbolName ?? edit.symbolName,
|
|
12
13
|
symbolKind: edit.targetSymbolKind ?? edit.symbolKind,
|
|
13
14
|
editKind: edit.editKind,
|
|
@@ -64,6 +65,7 @@ function appendOverlapDiagnostic(overlapDiagnostics, edit, code, operationIds) {
|
|
|
64
65
|
status: 'conflict',
|
|
65
66
|
operationId: edit.operationId,
|
|
66
67
|
sourcePath: edit.sourcePath,
|
|
68
|
+
...sourceIdentityDiagnosticContext(edit),
|
|
67
69
|
symbolName: edit.symbolName,
|
|
68
70
|
symbolKind: edit.symbolKind,
|
|
69
71
|
editKind: edit.editKind,
|
|
@@ -93,6 +95,18 @@ function replayDiagnostic(code, context) {
|
|
|
93
95
|
status: context.status,
|
|
94
96
|
operationId: context.operationId,
|
|
95
97
|
sourcePath: context.sourcePath,
|
|
98
|
+
originalSourcePath: context.originalSourcePath,
|
|
99
|
+
targetSourcePath: context.targetSourcePath,
|
|
100
|
+
anchorKey: context.anchorKey,
|
|
101
|
+
targetAnchorKey: context.targetAnchorKey,
|
|
102
|
+
sourceIdentityStatus: context.sourceIdentityStatus,
|
|
103
|
+
sourceIdentityAnchorKey: context.sourceIdentityAnchorKey,
|
|
104
|
+
targetIdentityAnchorKey: context.targetIdentityAnchorKey,
|
|
105
|
+
sourceIdentitySourcePath: context.sourceIdentitySourcePath,
|
|
106
|
+
targetIdentitySourcePath: context.targetIdentitySourcePath,
|
|
107
|
+
semanticIdentityHash: context.semanticIdentityHash,
|
|
108
|
+
sourceIdentityHash: context.sourceIdentityHash,
|
|
109
|
+
editContentHash: context.editContentHash,
|
|
96
110
|
symbolName: context.symbolName,
|
|
97
111
|
symbolKind: context.symbolKind,
|
|
98
112
|
editKind: context.editKind,
|
|
@@ -105,6 +119,31 @@ function replayDiagnostic(code, context) {
|
|
|
105
119
|
});
|
|
106
120
|
}
|
|
107
121
|
|
|
122
|
+
function sourceIdentityDiagnosticContext(edit) {
|
|
123
|
+
const sourceIdentityAnchorKey = edit.sourceIdentityAnchorKey ?? edit.anchorKey;
|
|
124
|
+
const targetIdentityAnchorKey = edit.targetIdentityAnchorKey ?? edit.targetAnchorKey ?? sourceIdentityAnchorKey;
|
|
125
|
+
const sourceIdentitySourcePath = edit.sourceIdentitySourcePath ?? edit.originalSourcePath ?? edit.sourcePath;
|
|
126
|
+
const targetIdentitySourcePath = edit.targetIdentitySourcePath ?? edit.targetSourcePath ?? edit.sourcePath;
|
|
127
|
+
const moved = Boolean(
|
|
128
|
+
(sourceIdentityAnchorKey && targetIdentityAnchorKey && sourceIdentityAnchorKey !== targetIdentityAnchorKey)
|
|
129
|
+
|| (sourceIdentitySourcePath && targetIdentitySourcePath && sourceIdentitySourcePath !== targetIdentitySourcePath)
|
|
130
|
+
);
|
|
131
|
+
return compactRecord({
|
|
132
|
+
originalSourcePath: edit.originalSourcePath,
|
|
133
|
+
targetSourcePath: edit.targetSourcePath,
|
|
134
|
+
anchorKey: edit.anchorKey,
|
|
135
|
+
targetAnchorKey: edit.targetAnchorKey,
|
|
136
|
+
sourceIdentityStatus: edit.sourceIdentityStatus ?? (moved ? 'moved-source' : 'same-source'),
|
|
137
|
+
sourceIdentityAnchorKey,
|
|
138
|
+
targetIdentityAnchorKey,
|
|
139
|
+
sourceIdentitySourcePath,
|
|
140
|
+
targetIdentitySourcePath,
|
|
141
|
+
semanticIdentityHash: edit.semanticIdentityHash,
|
|
142
|
+
sourceIdentityHash: edit.sourceIdentityHash,
|
|
143
|
+
editContentHash: edit.editContentHash
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
108
147
|
function replayDiagnosticCategory(code, status) {
|
|
109
148
|
if (code.includes('overlap')) return 'overlap';
|
|
110
149
|
if (code.startsWith('missing-current-source') || code.startsWith('missing-head-source') || code.startsWith('missing-worker-source')) return 'missing-source';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function explicitSourceReplacementReplayRange(edit, symbolRange, sourceText) {
|
|
2
|
+
if (edit.sourceRangeKind !== 'cross-language-explicit-source-replacement' || !symbolRange || typeof sourceText !== 'string') {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const deleted = uniqueTextRange(sourceText, symbolRange, edit.deletedText, 'deleted-text');
|
|
6
|
+
if (deleted.status === 'matched') return deleted;
|
|
7
|
+
const replacement = uniqueTextRange(sourceText, symbolRange, edit.replacementText, 'replacement-text');
|
|
8
|
+
if (replacement.status === 'matched') return replacement;
|
|
9
|
+
const relative = relativeAnchorRange(edit, symbolRange);
|
|
10
|
+
return {
|
|
11
|
+
...relative,
|
|
12
|
+
conflictReasonCodes: [deleted.reasonCode, replacement.reasonCode].filter(Boolean)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function uniqueTextRange(sourceText, symbolRange, needle, label) {
|
|
17
|
+
if (typeof needle !== 'string' || needle.length === 0) {
|
|
18
|
+
return { status: 'missing', reasonCode: `current-symbol-explicit-source-replacement-${label}-missing` };
|
|
19
|
+
}
|
|
20
|
+
const symbolText = sourceText.slice(symbolRange.start, symbolRange.end);
|
|
21
|
+
const matches = [];
|
|
22
|
+
for (let index = symbolText.indexOf(needle); index >= 0; index = symbolText.indexOf(needle, index + 1)) {
|
|
23
|
+
const start = symbolRange.start + index;
|
|
24
|
+
if (isCodeOffset(sourceText, start)) matches.push(start);
|
|
25
|
+
}
|
|
26
|
+
if (matches.length !== 1) {
|
|
27
|
+
return { status: matches.length ? 'ambiguous' : 'missing', reasonCode: `current-symbol-explicit-source-replacement-${label}-${matches.length ? 'ambiguous' : 'missing'}` };
|
|
28
|
+
}
|
|
29
|
+
const first = matches[0] - symbolRange.start;
|
|
30
|
+
return {
|
|
31
|
+
status: 'matched',
|
|
32
|
+
range: { start: symbolRange.start + first, end: symbolRange.start + first + needle.length },
|
|
33
|
+
reasonCode: `current-symbol-explicit-source-replacement-${label}`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isCodeOffset(sourceText, offset) {
|
|
38
|
+
let state = 'code';
|
|
39
|
+
for (let index = 0; index < offset; index += 1) {
|
|
40
|
+
const char = sourceText[index];
|
|
41
|
+
const next = sourceText[index + 1];
|
|
42
|
+
if (state === 'line-comment') {
|
|
43
|
+
if (char === '\n' || char === '\r') state = 'code';
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (state === 'block-comment') {
|
|
47
|
+
if (char === '*' && next === '/') {
|
|
48
|
+
index += 1;
|
|
49
|
+
state = 'code';
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (state === 'single' || state === 'double' || state === 'template') {
|
|
54
|
+
if (char === '\\') {
|
|
55
|
+
index += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if ((state === 'single' && char === "'") || (state === 'double' && char === '"') || (state === 'template' && char === '`')) state = 'code';
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (char === '/' && next === '/') {
|
|
62
|
+
index += 1;
|
|
63
|
+
state = 'line-comment';
|
|
64
|
+
} else if (char === '#') {
|
|
65
|
+
state = 'line-comment';
|
|
66
|
+
} else if (char === '/' && next === '*') {
|
|
67
|
+
index += 1;
|
|
68
|
+
state = 'block-comment';
|
|
69
|
+
} else if (char === "'") state = 'single';
|
|
70
|
+
else if (char === '"') state = 'double';
|
|
71
|
+
else if (char === '`') state = 'template';
|
|
72
|
+
}
|
|
73
|
+
return state === 'code';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function relativeAnchorRange(edit, symbolRange) {
|
|
77
|
+
if (!Number.isFinite(edit.headAnchorStart) || !Number.isFinite(edit.headAnchorEnd)) return undefined;
|
|
78
|
+
if (!Number.isFinite(edit.headStart) || !Number.isFinite(edit.headEnd)) return undefined;
|
|
79
|
+
const offset = edit.headStart - edit.headAnchorStart;
|
|
80
|
+
const length = edit.headEnd - edit.headStart;
|
|
81
|
+
if (offset < 0 || length < 0) return undefined;
|
|
82
|
+
const range = { start: symbolRange.start + offset, end: symbolRange.start + offset + length };
|
|
83
|
+
if (range.start < symbolRange.start || range.end > symbolRange.end) return undefined;
|
|
84
|
+
return { range, reasonCode: 'current-symbol-explicit-source-replacement-relative-offset' };
|
|
85
|
+
}
|
|
@@ -226,6 +226,10 @@ function semanticEditOperationKind(region) {
|
|
|
226
226
|
if (kind === 'import') return `${prefix}Import`;
|
|
227
227
|
if (kind === 'type') return `${prefix}TypeDeclaration`;
|
|
228
228
|
if (kind === 'property') return `${prefix}Property`;
|
|
229
|
+
if (kind === 'call') return `${prefix}Callsite`;
|
|
230
|
+
if (kind === 'controlFlow') return `${prefix}ControlFlow`;
|
|
231
|
+
if (kind === 'effect') return `${prefix}Effect`;
|
|
232
|
+
if (kind === 'mutation') return `${prefix}Mutation`;
|
|
229
233
|
return `${prefix}Region`;
|
|
230
234
|
}
|
|
231
235
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
2
|
|
|
3
|
+
const nestedBodyCoveredKinds = new Set(['export', 'call', 'controlFlow', 'effect', 'mutation']);
|
|
4
|
+
|
|
3
5
|
export function projectionCoveredContainerOperationIds(operations, workerSourceText) {
|
|
4
6
|
if (typeof workerSourceText !== 'string') return new Set();
|
|
5
7
|
const result = new Set();
|
|
@@ -7,6 +9,9 @@ export function projectionCoveredContainerOperationIds(operations, workerSourceT
|
|
|
7
9
|
if (!isProjectionCoverableContainer(operation)) continue;
|
|
8
10
|
if (workerContainerCoveredByInsertedChildren(operation, operations, workerSourceText)) result.add(operation.id);
|
|
9
11
|
}
|
|
12
|
+
for (const operation of operations ?? []) {
|
|
13
|
+
if (operationCoveredByBody(operation, operations, workerSourceText)) result.add(operation.id);
|
|
14
|
+
}
|
|
10
15
|
return result;
|
|
11
16
|
}
|
|
12
17
|
|
|
@@ -111,6 +116,28 @@ function workerContainerCoveredByInsertedChildren(container, operations, workerS
|
|
|
111
116
|
return hashSemanticValue(stripped) === container.hashes.baseTextHash;
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
function operationCoveredByBody(operation, operations, workerSourceText) {
|
|
120
|
+
const kind = operation.anchor?.regionKind;
|
|
121
|
+
if (!nestedBodyCoveredKinds.has(kind)) return false;
|
|
122
|
+
if (!['added', 'modified'].includes(operation.changeKind)) return false;
|
|
123
|
+
const range = spanOffsets(workerSourceText, operation.spans?.worker);
|
|
124
|
+
if (!range) return false;
|
|
125
|
+
return (operations ?? []).some((candidate) => (
|
|
126
|
+
candidate.id !== operation.id
|
|
127
|
+
&& ['portable', 'already-applied'].includes(candidate.status)
|
|
128
|
+
&& candidate.anchor?.regionKind === 'body'
|
|
129
|
+
&& (kind !== 'export' || candidate.anchor?.symbolName === operation.anchor?.symbolName)
|
|
130
|
+
&& sameOperationSourcePath(candidate, operation)
|
|
131
|
+
&& containedRange(range, spanOffsets(workerSourceText, candidate.spans?.worker))
|
|
132
|
+
));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function sameOperationSourcePath(left, right) {
|
|
136
|
+
const leftPath = left.anchor?.sourcePath ?? left.insertion?.sourcePath;
|
|
137
|
+
const rightPath = right.anchor?.sourcePath ?? right.insertion?.sourcePath;
|
|
138
|
+
return !leftPath || !rightPath || leftPath === rightPath;
|
|
139
|
+
}
|
|
140
|
+
|
|
114
141
|
function containedRange(inner, outer) {
|
|
115
142
|
return Boolean(inner && outer && outer.start <= inner.start && inner.end <= outer.end);
|
|
116
143
|
}
|
|
@@ -168,6 +195,7 @@ function insertionAnchorResolution(sourceText, insertion, context) {
|
|
|
168
195
|
const range = spanOffsets(sourceText, symbol?.sourceSpan);
|
|
169
196
|
if (range) return { mode: candidate.mode, range };
|
|
170
197
|
}
|
|
198
|
+
if (context.symbolIndexAvailable && candidates.some(hasSymbolAnchorIdentity)) return undefined;
|
|
171
199
|
for (const candidate of candidates) {
|
|
172
200
|
const range = spanOffsets(sourceText, candidate.headSpan);
|
|
173
201
|
if (range) return { mode: candidate.mode, range };
|
|
@@ -175,6 +203,10 @@ function insertionAnchorResolution(sourceText, insertion, context) {
|
|
|
175
203
|
return undefined;
|
|
176
204
|
}
|
|
177
205
|
|
|
206
|
+
function hasSymbolAnchorIdentity(candidate) {
|
|
207
|
+
return Boolean(candidate.anchorKey || candidate.anchorSymbolId || candidate.anchorSymbolName);
|
|
208
|
+
}
|
|
209
|
+
|
|
178
210
|
function insertionAnchorCandidates(insertion) {
|
|
179
211
|
const seen = new Set();
|
|
180
212
|
const result = [];
|
|
@@ -35,6 +35,7 @@ export function semanticIndexFromNativeDeclarations(declarations, input, options
|
|
|
35
35
|
signatureHash: hashSemanticValue([input.language, declaration.nativeNode.kind, declaration.name, declaration.nativeNode.fields ?? {}]),
|
|
36
36
|
definitionSpan: declaration.nativeNode.span,
|
|
37
37
|
metadata: {
|
|
38
|
+
...declaration.nativeNode.metadata,
|
|
38
39
|
ownershipRegionId: ownershipRegion.id,
|
|
39
40
|
ownershipRegionKey: ownershipRegion.key,
|
|
40
41
|
ownershipRegionKind: ownershipRegion.regionKind
|