@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.105

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.
Files changed (125) hide show
  1. package/README.md +14 -0
  2. package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
  3. package/dist/declarations/bidirectional-target-change.d.ts +10 -0
  4. package/dist/declarations/js-ts-safe-member-merge.d.ts +86 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +131 -0
  6. package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
  7. package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
  8. package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
  9. package/dist/declarations/native-import-losses.d.ts +3 -0
  10. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
  11. package/dist/declarations/semantic-edit-script.d.ts +7 -4
  12. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  13. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  14. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  15. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  16. package/dist/declarations/source-preservation.d.ts +72 -0
  17. package/dist/declarations/universal-capability.d.ts +4 -0
  18. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  19. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  20. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  21. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +3 -0
  24. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  25. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  26. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  27. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  28. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  29. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  30. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  31. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  32. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  33. package/dist/internal/index-impl/diffNativeSymbols.js +3 -3
  34. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  35. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +43 -8
  36. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  37. package/dist/internal/index-impl/replaySemanticEditProjection.js +39 -19
  38. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  39. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  40. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  41. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  42. package/dist/internal/index-impl/semanticEditProjectionRecord.js +29 -0
  43. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  44. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  45. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  46. package/dist/internal/index-impl/semanticEditSourceRanges.js +27 -0
  47. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  48. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +41 -7
  49. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  50. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  51. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  52. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  53. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  54. package/dist/js-ts-safe-member-merge-result.js +158 -0
  55. package/dist/js-ts-safe-member-merge.js +265 -0
  56. package/dist/js-ts-safe-merge-analyze.js +279 -0
  57. package/dist/js-ts-safe-merge-composed.js +170 -0
  58. package/dist/js-ts-safe-merge-constants.js +50 -0
  59. package/dist/js-ts-safe-merge-context.js +118 -0
  60. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  61. package/dist/js-ts-safe-merge-ledger.js +85 -0
  62. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  63. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  64. package/dist/js-ts-safe-merge-plan.js +190 -0
  65. package/dist/js-ts-safe-merge.js +175 -0
  66. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  67. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  68. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  69. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  70. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  71. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  72. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  73. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  74. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  75. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  76. package/dist/js-ts-semantic-merge-member-source.js +82 -0
  77. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  78. package/dist/js-ts-semantic-merge-parse.js +16 -0
  79. package/dist/js-ts-semantic-merge.js +24 -0
  80. package/dist/lightweight-dependency-effects.js +51 -0
  81. package/dist/lightweight-dependency-language.js +12 -1
  82. package/dist/lightweight-dependency-relations.js +14 -27
  83. package/dist/native-region-scanner-core.js +33 -1
  84. package/dist/native-region-scanner-csharp.js +151 -0
  85. package/dist/native-region-scanner-dart.js +91 -0
  86. package/dist/native-region-scanner-dynamic.js +21 -151
  87. package/dist/native-region-scanner-functional.js +40 -13
  88. package/dist/native-region-scanner-java.js +97 -0
  89. package/dist/native-region-scanner-js-class.js +100 -0
  90. package/dist/native-region-scanner-js-helpers.js +28 -86
  91. package/dist/native-region-scanner-js-imports.js +121 -1
  92. package/dist/native-region-scanner-js-nested.js +96 -8
  93. package/dist/native-region-scanner-js-structure.js +27 -0
  94. package/dist/native-region-scanner-js-types.js +99 -0
  95. package/dist/native-region-scanner-js.js +70 -118
  96. package/dist/native-region-scanner-kotlin.js +94 -0
  97. package/dist/native-region-scanner-main.js +15 -181
  98. package/dist/native-region-scanner-php.js +80 -0
  99. package/dist/native-region-scanner-python.js +62 -0
  100. package/dist/native-region-scanner-ruby.js +72 -0
  101. package/dist/native-region-scanner-scala.js +91 -0
  102. package/dist/native-region-scanner-spans.js +74 -0
  103. package/dist/native-region-scanner-swift.js +155 -0
  104. package/dist/native-region-scanner.js +14 -10
  105. package/dist/native-source-ledger-helpers.js +195 -0
  106. package/dist/native-source-ledger.js +306 -0
  107. package/dist/native-source-preservation-scanner.js +4 -0
  108. package/dist/semantic-import-callsite-regions.js +136 -0
  109. package/dist/semantic-import-effect-regions.js +283 -0
  110. package/dist/semantic-import-regions.js +11 -2
  111. package/dist/semantic-import-sidecar-entry.js +16 -2
  112. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  113. package/dist/semantic-sidecar-example.js +68 -0
  114. package/dist/universal-capability-matrix.js +23 -0
  115. package/dist/universal-conversion-artifact-query.js +79 -2
  116. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  117. package/dist/universal-conversion-artifact-summary.js +33 -1
  118. package/dist/universal-conversion-artifacts.js +13 -48
  119. package/dist/universal-conversion-plan-scoring.js +21 -1
  120. package/dist/universal-conversion-plan-summary.js +30 -0
  121. package/dist/universal-conversion-plan.js +25 -9
  122. package/dist/universal-conversion-route-metadata.js +96 -0
  123. package/dist/universal-conversion-route-operations.js +7 -0
  124. package/dist/universal-representation-coverage.js +193 -0
  125. package/package.json +1 -1
@@ -0,0 +1,170 @@
1
+ import {
2
+ JsTsSafeMergeConflictCodes,
3
+ JsTsSafeMergeGateIds,
4
+ JsTsSafeMergeStatuses,
5
+ jsTsSafeMergeGateOrder
6
+ } from './js-ts-safe-merge-constants.js';
7
+ import { safeMergeJsTsImportsAndDeclarations } from './js-ts-safe-merge.js';
8
+ import {
9
+ applyJsTsPreparedMemberAdditions,
10
+ neutralizeJsTsSafeMemberMergeSources
11
+ } from './js-ts-safe-member-merge.js';
12
+
13
+ function safeMergeJsTsSource(input = {}) {
14
+ if (!hasMemberMergePolicy(input)) return safeMergeJsTsImportsAndDeclarations(input);
15
+
16
+ const memberNeutralization = neutralizeJsTsSafeMemberMergeSources(input);
17
+ if (!memberNeutralization.ok) {
18
+ return composedBlockedResult(input, 'member-analysis', memberNeutralization.result, memberNeutralization.analysis);
19
+ }
20
+
21
+ const topLevelResult = safeMergeJsTsImportsAndDeclarations({
22
+ ...input,
23
+ baseSourceText: memberNeutralization.baseSourceText,
24
+ workerSourceText: memberNeutralization.workerSourceText,
25
+ headSourceText: memberNeutralization.headSourceText
26
+ });
27
+ if (topLevelResult.status !== JsTsSafeMergeStatuses.merged) {
28
+ return composedBlockedResult(input, 'top-level', topLevelResult, memberNeutralization.analysis);
29
+ }
30
+
31
+ const memberApplication = applyJsTsPreparedMemberAdditions(
32
+ topLevelResult.mergedSourceText,
33
+ memberNeutralization.analysis.preparedRegions,
34
+ ['head', 'worker']
35
+ );
36
+ if (memberApplication.reasonCodes.length) {
37
+ return composedMemberApplicationBlockedResult(input, topLevelResult, memberNeutralization.analysis, memberApplication.reasonCodes);
38
+ }
39
+
40
+ const memberAdditions = memberNeutralization.analysis.preparedRegions.reduce((total, region) => (
41
+ total + region.workerAddedKeys.length + region.headAddedKeys.length
42
+ ), 0);
43
+ return {
44
+ ...topLevelResult,
45
+ id: String(input.id ?? topLevelResult.id),
46
+ mergedSourceText: memberApplication.sourceText,
47
+ outputSourceText: memberApplication.sourceText,
48
+ summary: {
49
+ ...topLevelResult.summary,
50
+ memberRegions: memberNeutralization.analysis.preparedRegions.length,
51
+ memberAdditions,
52
+ composedPhases: 2
53
+ },
54
+ metadata: {
55
+ ...topLevelResult.metadata,
56
+ composed: {
57
+ phases: ['top-level', 'member'],
58
+ memberRegions: memberNeutralization.analysis.preparedRegions.map((region) => ({
59
+ kind: region.kind,
60
+ name: region.name,
61
+ regionKind: region.policy.regionKind,
62
+ workerAddedKeys: region.workerAddedKeys,
63
+ headAddedKeys: region.headAddedKeys
64
+ }))
65
+ }
66
+ }
67
+ };
68
+ }
69
+
70
+ function hasMemberMergePolicy(input) {
71
+ const policy = input.policy ?? input.mergePolicy ?? input;
72
+ const regions = Array.isArray(policy)
73
+ ? policy
74
+ : policy?.unorderedRegions
75
+ ?? policy?.unorderedMemberRegions
76
+ ?? policy?.safeList
77
+ ?? policy?.safeMembers
78
+ ?? [];
79
+ return Array.isArray(regions) && regions.length > 0;
80
+ }
81
+
82
+ function composedBlockedResult(input, phase, result, memberAnalysis) {
83
+ return {
84
+ kind: 'frontier.lang.jsTsSafeMerge',
85
+ version: 1,
86
+ schema: 'frontier.lang.jsTsSafeMerge.v1',
87
+ id: String(input.id ?? result.id ?? 'js_ts_safe_merge'),
88
+ status: JsTsSafeMergeStatuses.blocked,
89
+ sourcePath: input.sourcePath,
90
+ language: input.language ?? 'typescript',
91
+ conflicts: result.conflicts ?? [],
92
+ gates: result.gates ?? [],
93
+ admission: {
94
+ status: 'blocked',
95
+ action: 'human-review',
96
+ reviewRequired: true,
97
+ autoApplyCandidate: false,
98
+ autoMergeClaim: false,
99
+ semanticEquivalenceClaim: false,
100
+ reasonCodes: result.admission?.reasonCodes ?? result.reasonCodes ?? []
101
+ },
102
+ summary: {
103
+ importSpecifierAdditions: 0,
104
+ topLevelDeclarationAdditions: 0,
105
+ changedExistingDeclarations: result.summary?.changedExistingDeclarations ?? 0,
106
+ conflicts: result.conflicts?.length ?? result.summary?.conflicts ?? 0,
107
+ gatesPassed: result.gates?.filter((gate) => gate.status === 'passed').length ?? 0,
108
+ memberRegions: memberAnalysis?.preparedRegions?.length ?? 0,
109
+ memberAdditions: 0,
110
+ composedPhases: phase === 'top-level' ? 2 : 1
111
+ },
112
+ metadata: {
113
+ composed: {
114
+ phase,
115
+ sourceKind: result.kind
116
+ }
117
+ }
118
+ };
119
+ }
120
+
121
+ function composedMemberApplicationBlockedResult(input, topLevelResult, memberAnalysis, reasonCodes) {
122
+ const conflicts = reasonCodes.map((reason) => ({
123
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
124
+ gateId: JsTsSafeMergeGateIds.parseLedger,
125
+ message: 'Composed member application could not find a stable target container.',
126
+ side: 'merged',
127
+ sourcePath: input.sourcePath,
128
+ details: { reason }
129
+ }));
130
+ return {
131
+ ...topLevelResult,
132
+ status: JsTsSafeMergeStatuses.blocked,
133
+ mergedSourceText: undefined,
134
+ outputSourceText: undefined,
135
+ conflicts,
136
+ gates: jsTsSafeMergeGateOrder.map((id, index) => ({
137
+ id,
138
+ status: index === 0 ? 'blocked' : 'skipped',
139
+ reasonCodes: index === 0 ? [JsTsSafeMergeConflictCodes.parserLedgerLoss] : []
140
+ })),
141
+ admission: {
142
+ status: 'blocked',
143
+ action: 'human-review',
144
+ reviewRequired: true,
145
+ autoApplyCandidate: false,
146
+ autoMergeClaim: false,
147
+ semanticEquivalenceClaim: false,
148
+ reasonCodes: [JsTsSafeMergeConflictCodes.parserLedgerLoss]
149
+ },
150
+ summary: {
151
+ ...topLevelResult.summary,
152
+ conflicts: conflicts.length,
153
+ gatesPassed: 0,
154
+ memberRegions: memberAnalysis.preparedRegions.length,
155
+ memberAdditions: 0,
156
+ composedPhases: 2
157
+ },
158
+ metadata: {
159
+ ...topLevelResult.metadata,
160
+ composed: {
161
+ phase: 'member-application',
162
+ reasonCodes
163
+ }
164
+ }
165
+ };
166
+ }
167
+
168
+ export {
169
+ safeMergeJsTsSource
170
+ };
@@ -0,0 +1,50 @@
1
+ export const JsTsSafeMergeStatuses = Object.freeze({
2
+ merged: 'merged',
3
+ blocked: 'blocked'
4
+ });
5
+
6
+ export const JsTsSafeMergeGateIds = Object.freeze({
7
+ parseLedger: 'parse-ledger',
8
+ preserveBaseOrder: 'preserve-base-order',
9
+ stableExistingDeclarations: 'stable-existing-declarations',
10
+ independentImportSpecifiers: 'independent-import-specifiers',
11
+ independentTopLevelDeclarations: 'independent-top-level-declarations',
12
+ uniqueNames: 'unique-names',
13
+ resolvedInsertionAnchors: 'resolved-insertion-anchors'
14
+ });
15
+
16
+ export const JsTsSafeMergeConflictCodes = Object.freeze({
17
+ invalidInput: 'invalid-input',
18
+ parserLedgerLoss: 'parser-ledger-loss',
19
+ malformedSyntax: 'malformed-syntax',
20
+ sideEffectImportReorder: 'side-effect-import-reorder',
21
+ topLevelOrderChanged: 'top-level-order-changed',
22
+ changedExistingDeclaration: 'changed-existing-declaration',
23
+ typeAliasConflict: 'type-alias-conflict',
24
+ importShapeChanged: 'import-shape-changed',
25
+ importSpecifierRemoved: 'import-specifier-removed',
26
+ importSpecifierReordered: 'import-specifier-reordered',
27
+ importFormattingChanged: 'import-formatting-changed',
28
+ newImportDeclaration: 'new-import-declaration',
29
+ duplicateName: 'duplicate-name',
30
+ computedKey: 'computed-key',
31
+ unsupportedDecorator: 'unsupported-decorator-merge-anchor',
32
+ unsupportedOverload: 'unsupported-overload-merge-anchor',
33
+ staleSourceHash: 'stale-source-hash',
34
+ missingSourceLedgerSpan: 'missing-source-ledger-span',
35
+ ambiguousInsertionPoint: 'ambiguous-insertion-point',
36
+ insertionAnchorMissing: 'insertion-anchor-missing'
37
+ });
38
+
39
+ export const jsTsSafeMergeGateOrder = Object.freeze([
40
+ JsTsSafeMergeGateIds.parseLedger,
41
+ JsTsSafeMergeGateIds.preserveBaseOrder,
42
+ JsTsSafeMergeGateIds.stableExistingDeclarations,
43
+ JsTsSafeMergeGateIds.independentImportSpecifiers,
44
+ JsTsSafeMergeGateIds.independentTopLevelDeclarations,
45
+ JsTsSafeMergeGateIds.uniqueNames,
46
+ JsTsSafeMergeGateIds.resolvedInsertionAnchors
47
+ ]);
48
+
49
+ const identifierPattern = '[A-Za-z_$][\\w$]*';
50
+ export const identifierRegExp = new RegExp(`^${identifierPattern}$`);
@@ -0,0 +1,118 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds, JsTsSafeMergeStatuses, jsTsSafeMergeGateOrder } from './js-ts-safe-merge-constants.js';
2
+
3
+ export function createMergeContext(input) {
4
+ return {
5
+ id: String(input.id ?? 'js_ts_safe_merge'),
6
+ sourcePath: input.sourcePath,
7
+ language: input.language ?? 'typescript',
8
+ conflicts: [],
9
+ blockedGateIds: new Set(),
10
+ gateReasonCodes: new Map()
11
+ };
12
+ }
13
+
14
+ export function blockedResult(context, ledgers = {}) {
15
+ const reasonCodes = uniqueStrings(context.conflicts.map((conflict) => conflict.code));
16
+ return {
17
+ kind: 'frontier.lang.jsTsSafeMerge',
18
+ version: 1,
19
+ schema: 'frontier.lang.jsTsSafeMerge.v1',
20
+ id: context.id,
21
+ status: JsTsSafeMergeStatuses.blocked,
22
+ sourcePath: context.sourcePath,
23
+ language: context.language,
24
+ conflicts: context.conflicts,
25
+ gates: gatesFor(context),
26
+ admission: {
27
+ status: 'blocked',
28
+ action: 'human-review',
29
+ reviewRequired: true,
30
+ autoApplyCandidate: false,
31
+ autoMergeClaim: false,
32
+ semanticEquivalenceClaim: false,
33
+ reasonCodes
34
+ },
35
+ summary: {
36
+ importSpecifierAdditions: 0,
37
+ topLevelDeclarationAdditions: 0,
38
+ changedExistingDeclarations: context.conflicts.filter((conflict) => conflict.code === JsTsSafeMergeConflictCodes.changedExistingDeclaration).length,
39
+ conflicts: context.conflicts.length,
40
+ gatesPassed: gatesFor(context).filter((gate) => gate.status === 'passed').length
41
+ },
42
+ metadata: {
43
+ ledgers: compactRecord({
44
+ base: ledgerSummary(ledgers.base),
45
+ worker: ledgerSummary(ledgers.worker),
46
+ head: ledgerSummary(ledgers.head),
47
+ merged: ledgerSummary(ledgers.merged)
48
+ })
49
+ }
50
+ };
51
+ }
52
+
53
+ export function addConflict(context, conflict) {
54
+ const gateId = conflict.gateId ?? JsTsSafeMergeGateIds.parseLedger;
55
+ const record = {
56
+ code: conflict.code,
57
+ gateId,
58
+ message: conflict.message,
59
+ side: conflict.side,
60
+ sourcePath: context.sourcePath,
61
+ details: compactRecord(conflict.details)
62
+ };
63
+ context.conflicts.push(record);
64
+ context.blockedGateIds.add(gateId);
65
+ const gateReasonCodes = context.gateReasonCodes.get(gateId) ?? [];
66
+ gateReasonCodes.push(conflict.code);
67
+ context.gateReasonCodes.set(gateId, uniqueStrings(gateReasonCodes));
68
+ }
69
+
70
+ export function gatesFor(context) {
71
+ let blockedSeen = false;
72
+ return jsTsSafeMergeGateOrder.map((id) => {
73
+ const blocked = context.blockedGateIds.has(id);
74
+ const status = blocked ? 'blocked' : blockedSeen ? 'skipped' : 'passed';
75
+ if (blocked) blockedSeen = true;
76
+ return {
77
+ id,
78
+ status,
79
+ reasonCodes: context.gateReasonCodes.get(id) ?? []
80
+ };
81
+ });
82
+ }
83
+
84
+ export function sameStatementText(left, right) {
85
+ return normalizeLineEndings(String(left ?? '').trim(), '\n') === normalizeLineEndings(String(right ?? '').trim(), '\n');
86
+ }
87
+
88
+ export function normalizeLineEndings(text, lineEnding) {
89
+ return String(text ?? '').replace(/\r\n?/g, '\n').replace(/\n/g, lineEnding);
90
+ }
91
+
92
+ export function detectLineEnding(text) {
93
+ return String(text ?? '').includes('\r\n') ? '\r\n' : '\n';
94
+ }
95
+
96
+ export function arraysEqual(left, right) {
97
+ if (left.length !== right.length) return false;
98
+ return left.every((value, index) => value === right[index]);
99
+ }
100
+
101
+ export function uniqueStrings(values) {
102
+ return [...new Set((values ?? []).filter((value) => typeof value === 'string' && value.length > 0))];
103
+ }
104
+
105
+ export function compactRecord(record) {
106
+ return Object.fromEntries(Object.entries(record ?? {}).filter(([, value]) => value !== undefined));
107
+ }
108
+
109
+ export function ledgerSummary(ledger) {
110
+ if (!ledger) return undefined;
111
+ return {
112
+ label: ledger.label,
113
+ entries: ledger.entries.length,
114
+ imports: ledger.entries.filter((entry) => entry.kind === 'import').length,
115
+ declarations: ledger.entries.filter((entry) => entry.kind === 'declaration').length,
116
+ exports: ledger.entries.filter((entry) => entry.kind === 'export').length
117
+ };
118
+ }
@@ -0,0 +1,92 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
2
+ import { addConflict } from './js-ts-safe-merge-context.js';
3
+
4
+ export function validateLedgerUniqueness(ledger, context) {
5
+ const keyOwners = new Map();
6
+ for (const entry of ledger.entries) {
7
+ const existing = keyOwners.get(entry.key);
8
+ if (existing) {
9
+ addConflict(context, {
10
+ code: JsTsSafeMergeConflictCodes.duplicateName,
11
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
12
+ side: ledger.label,
13
+ message: `${ledger.label} source has duplicate top-level ledger keys.`,
14
+ details: { key: entry.key, firstOffset: existing.start, secondOffset: entry.start }
15
+ });
16
+ } else {
17
+ keyOwners.set(entry.key, entry);
18
+ }
19
+ }
20
+
21
+ const nameOwners = new Map();
22
+ for (const entry of ledger.entries) {
23
+ for (const name of entry.names ?? []) {
24
+ const nameKey = `${entry.kind}:${name}`;
25
+ const existing = nameOwners.get(nameKey);
26
+ if (existing) {
27
+ addConflict(context, {
28
+ code: JsTsSafeMergeConflictCodes.duplicateName,
29
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
30
+ side: ledger.label,
31
+ message: `${ledger.label} source has duplicate top-level names.`,
32
+ details: { name, firstOffset: existing.start, secondOffset: entry.start }
33
+ });
34
+ } else {
35
+ nameOwners.set(nameKey, entry);
36
+ }
37
+ }
38
+ }
39
+
40
+ for (const entry of ledger.entries.filter((item) => item.kind === 'import')) {
41
+ validateUniqueImportSpecifiers(entry, ledger.label, context);
42
+ }
43
+ }
44
+
45
+ function validateUniqueImportSpecifiers(entry, side, context) {
46
+ const localNames = new Map();
47
+ const specifiers = [
48
+ ...(entry.importInfo.defaultLocalName ? [{ localName: entry.importInfo.defaultLocalName, canonical: `default:${entry.importInfo.defaultLocalName}` }] : []),
49
+ ...(entry.importInfo.namespaceLocalName ? [{ localName: entry.importInfo.namespaceLocalName, canonical: `namespace:${entry.importInfo.namespaceLocalName}` }] : []),
50
+ ...entry.importInfo.specifiers
51
+ ];
52
+ for (const specifier of specifiers) {
53
+ const existing = localNames.get(specifier.localName);
54
+ if (existing) {
55
+ addConflict(context, {
56
+ code: JsTsSafeMergeConflictCodes.duplicateName,
57
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
58
+ side,
59
+ message: `${side} source has duplicate import binding names.`,
60
+ details: { importKey: entry.key, localName: specifier.localName, first: existing.canonical, second: specifier.canonical }
61
+ });
62
+ } else {
63
+ localNames.set(specifier.localName, specifier);
64
+ }
65
+ }
66
+ }
67
+
68
+ export function indexBaseLedger(base, context) {
69
+ const entriesByKey = new Map();
70
+ for (const entry of base.entries) entriesByKey.set(entry.key, entry);
71
+ const names = new Set();
72
+ for (const entry of base.entries) {
73
+ for (const name of entry.names ?? []) {
74
+ const nameKey = `${entry.kind}:${name}`;
75
+ if (names.has(nameKey)) {
76
+ addConflict(context, {
77
+ code: JsTsSafeMergeConflictCodes.duplicateName,
78
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
79
+ side: 'base',
80
+ message: 'Base source has duplicate declaration or export names.',
81
+ details: { name }
82
+ });
83
+ }
84
+ names.add(nameKey);
85
+ }
86
+ }
87
+ return {
88
+ entriesByKey,
89
+ orderedKeys: base.entries.map((entry) => entry.key),
90
+ names
91
+ };
92
+ }
@@ -0,0 +1,85 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
2
+ import { addConflict } from './js-ts-safe-merge-context.js';
3
+ import { classifyStatement, importSpecifierCanonical } from './js-ts-safe-merge-parse-declarations.js';
4
+ import { findStatementEnd, skipTopLevelTrivia } from './js-ts-safe-merge-parse-statements.js';
5
+
6
+ export function scanJsTsTopLevelLedger(sourceText, label, context) {
7
+ const entries = [];
8
+ let offset = 0;
9
+ while (offset < sourceText.length) {
10
+ const skipped = skipTopLevelTrivia(sourceText, offset);
11
+ if (skipped.error) {
12
+ addConflict(context, {
13
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
14
+ gateId: JsTsSafeMergeGateIds.parseLedger,
15
+ side: label,
16
+ message: `${label} source contains unterminated top-level trivia.`,
17
+ details: { offset, error: skipped.error }
18
+ });
19
+ return createLedger(label, sourceText, entries);
20
+ }
21
+ offset = skipped.offset;
22
+ if (offset >= sourceText.length) break;
23
+
24
+ if (sourceText[offset] === '@') {
25
+ addConflict(context, {
26
+ code: JsTsSafeMergeConflictCodes.unsupportedDecorator,
27
+ gateId: JsTsSafeMergeGateIds.parseLedger,
28
+ side: label,
29
+ message: `${label} source contains decorator syntax that is not safe for ledger merge anchors.`,
30
+ details: { offset, reasonCode: 'unsupported-js-ts-syntax', preview: sourceText.slice(offset, Math.min(sourceText.length, offset + 80)) }
31
+ });
32
+ return createLedger(label, sourceText, entries);
33
+ }
34
+
35
+ const end = findStatementEnd(sourceText, offset);
36
+ if (end.error) {
37
+ addConflict(context, {
38
+ code: JsTsSafeMergeConflictCodes.malformedSyntax,
39
+ gateId: JsTsSafeMergeGateIds.parseLedger,
40
+ side: label,
41
+ message: `${label} source contains a top-level statement the narrow ledger cannot bound.`,
42
+ details: { offset, error: end.error, preview: sourceText.slice(offset, Math.min(sourceText.length, offset + 80)) }
43
+ });
44
+ return createLedger(label, sourceText, entries);
45
+ }
46
+
47
+ const statementText = sourceText.slice(offset, end.offset);
48
+ const entry = classifyStatement(statementText, offset, end.offset);
49
+ if (entry?.unsupported) {
50
+ addConflict(context, {
51
+ code: entry.unsupported.code,
52
+ gateId: JsTsSafeMergeGateIds.parseLedger,
53
+ side: label,
54
+ message: `${label} source contains syntax that is not safe for automatic JS/TS ledger merge.`,
55
+ details: { offset, ...entry.unsupported.details, statement: statementText.trim().slice(0, 120) }
56
+ });
57
+ return createLedger(label, sourceText, entries);
58
+ }
59
+ if (!entry) {
60
+ addConflict(context, {
61
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
62
+ gateId: JsTsSafeMergeGateIds.parseLedger,
63
+ side: label,
64
+ message: `${label} source contains an unsupported top-level statement.`,
65
+ details: { offset, statement: statementText.trim().slice(0, 120) }
66
+ });
67
+ return createLedger(label, sourceText, entries);
68
+ }
69
+ entries.push(entry);
70
+ offset = end.offset;
71
+ }
72
+ return createLedger(label, sourceText, entries);
73
+ }
74
+
75
+ function createLedger(label, sourceText, entries) {
76
+ return {
77
+ label,
78
+ sourceText,
79
+ entries,
80
+ baseEntries: entries.filter((entry) => entry.kind === 'import' || entry.kind === 'declaration' || entry.kind === 'export')
81
+ };
82
+ }
83
+
84
+ export { importSpecifierCanonical } from './js-ts-safe-merge-parse-declarations.js';
85
+ export { indexBaseLedger, validateLedgerUniqueness } from './js-ts-safe-merge-ledger-validation.js';