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

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.
@@ -0,0 +1,266 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { idFragment, uniqueStrings } from './native-import-utils.js';
3
+ import { replaySemanticEditProjection } from './internal/index-impl/replaySemanticEditProjection.js';
4
+ import { projectionEditRecord } from './internal/index-impl/semanticEditProjectionRecord.js';
5
+ import { applySourceEdits, dedupeSourceEdits, validateSourceEdits } from './internal/index-impl/semanticSourceEditDedupe.js';
6
+ import { createMergeContext } from './js-ts-safe-merge-context.js';
7
+ import { scanJsTsTopLevelLedger } from './js-ts-safe-merge-ledger.js';
8
+ import { createOperationsFromLedgers, sourceEditForOperation } from './js-ts-safe-merge-semantic-artifact-ledger.js';
9
+
10
+ function createJsTsSafeMergeSemanticArtifacts(input = {}, merge = {}) {
11
+ const headSourceText = input.headSourceText;
12
+ const mergedSourceText = merge.mergedSourceText ?? merge.outputSourceText;
13
+ const language = input.language ?? merge.language ?? 'typescript';
14
+ const sourcePath = input.sourcePath ?? merge.sourcePath ?? 'inline.js';
15
+ const id = String(input.id ?? merge.id ?? 'js_ts_safe_merge');
16
+ const baseReasonCodes = [];
17
+ if (typeof headSourceText !== 'string') baseReasonCodes.push('missing-head-source-text');
18
+ if (typeof mergedSourceText !== 'string') baseReasonCodes.push('missing-merged-source-text');
19
+ if (baseReasonCodes.length) return blockedArtifacts({ id, language, sourcePath, reasonCodes: baseReasonCodes, merge });
20
+
21
+ const ledgerContext = createMergeContext({ ...input, id: `${id}_semantic_artifacts`, language, sourcePath });
22
+ const head = scanJsTsTopLevelLedger(headSourceText, 'head', ledgerContext);
23
+ const merged = scanJsTsTopLevelLedger(mergedSourceText, 'merged', ledgerContext);
24
+ if (ledgerContext.conflicts.length) {
25
+ return blockedArtifacts({
26
+ id,
27
+ language,
28
+ sourcePath,
29
+ reasonCodes: uniqueStrings(ledgerContext.conflicts.map((conflict) => conflict.code)),
30
+ merge,
31
+ ledgers: { head, merged }
32
+ });
33
+ }
34
+
35
+ const operations = createOperationsFromLedgers({ id, language, sourcePath, head, merged, input, merge });
36
+ const rawEdits = operations.map((operation, order) => sourceEditForOperation(operation, order, headSourceText, mergedSourceText));
37
+ const deduped = dedupeSourceEdits(rawEdits);
38
+ const reasonCodes = uniqueStrings([
39
+ ...validateSourceEdits(deduped.edits),
40
+ ...deduped.skippedOperationIds.map((operationId) => `source-edit-deduped:${operationId}`)
41
+ ]);
42
+ const sourceText = reasonCodes.length ? undefined : applySourceEdits(headSourceText, deduped.edits);
43
+ if (sourceText !== mergedSourceText) reasonCodes.push('projected-source-mismatch');
44
+ const blocked = reasonCodes.length > 0;
45
+ const script = createScript({ id, language, sourcePath, input, merge, operations, blocked, reasonCodes });
46
+ const projection = createProjection({
47
+ id,
48
+ language,
49
+ sourcePath,
50
+ input,
51
+ merge,
52
+ script,
53
+ edits: blocked ? [] : deduped.edits,
54
+ skippedOperationIds: deduped.skippedOperationIds,
55
+ sourceText: blocked ? undefined : sourceText,
56
+ reasonCodes
57
+ });
58
+ const replay = replaySemanticEditProjection({
59
+ id: `js_ts_safe_merge_replay_${idFragment(id)}`,
60
+ projection,
61
+ currentSourceText: headSourceText,
62
+ currentSourcePath: sourcePath,
63
+ language
64
+ });
65
+ const alreadyAppliedReplay = replaySemanticEditProjection({
66
+ id: `js_ts_safe_merge_replay_already_applied_${idFragment(id)}`,
67
+ projection,
68
+ currentSourceText: mergedSourceText,
69
+ currentSourcePath: sourcePath,
70
+ language
71
+ });
72
+ const replayReady = replay.status === 'accepted-clean' && replay.outputSourceText === mergedSourceText;
73
+ const alreadyAppliedReady = alreadyAppliedReplay.status === 'already-applied';
74
+ const status = !blocked && replayReady && alreadyAppliedReady ? 'verified' : 'blocked';
75
+ const finalReasonCodes = uniqueStrings([
76
+ ...reasonCodes,
77
+ replayReady ? undefined : `semantic-replay-${replay.status}`,
78
+ alreadyAppliedReady ? undefined : `semantic-replay-already-applied-${alreadyAppliedReplay.status}`
79
+ ]);
80
+ const core = {
81
+ kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts',
82
+ version: 1,
83
+ schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1',
84
+ id: `js_ts_safe_merge_semantic_artifacts_${idFragment(id)}`,
85
+ sourcePath,
86
+ language,
87
+ status,
88
+ script,
89
+ projection,
90
+ replay,
91
+ alreadyAppliedReplay,
92
+ admission: {
93
+ status: status === 'verified' ? 'auto-merge-candidate' : 'blocked',
94
+ action: status === 'verified' ? 'apply' : 'human-review',
95
+ reviewRequired: status !== 'verified',
96
+ autoApplyCandidate: status === 'verified',
97
+ autoMergeClaim: false,
98
+ semanticEquivalenceClaim: false,
99
+ reasonCodes: finalReasonCodes
100
+ },
101
+ summary: {
102
+ operations: operations.length,
103
+ edits: projection.edits.length,
104
+ replayStatus: replay.status,
105
+ alreadyAppliedReplayStatus: alreadyAppliedReplay.status,
106
+ projectedSourceMatchesMerged: projection.sourceText === mergedSourceText,
107
+ replayOutputMatchesMerged: replay.outputSourceText === mergedSourceText
108
+ },
109
+ evidence: [{
110
+ id: `evidence_${idFragment(id)}_js_ts_safe_merge_semantic_replay`,
111
+ kind: 'js-ts-safe-merge-semantic-replay',
112
+ status: status === 'verified' ? 'passed' : 'needs-review',
113
+ path: sourcePath,
114
+ summary: status === 'verified'
115
+ ? `JS/TS safe merge replay verified ${operations.length} semantic source edit(s).`
116
+ : `JS/TS safe merge semantic replay requires review: ${finalReasonCodes.join(', ')}.`,
117
+ metadata: {
118
+ scriptId: script.id,
119
+ projectionId: projection.id,
120
+ replayId: replay.id,
121
+ alreadyAppliedReplayId: alreadyAppliedReplay.id,
122
+ autoMergeClaim: false,
123
+ semanticEquivalenceClaim: false
124
+ }
125
+ }],
126
+ metadata: {
127
+ autoMergeClaim: false,
128
+ semanticEquivalenceClaim: false,
129
+ source: 'js-ts-safe-merge-ledger',
130
+ mergeGateIds: (merge.gates ?? []).map((gate) => gate.id)
131
+ }
132
+ };
133
+ return { ...core, hash: hashSemanticValue(core) };
134
+ }
135
+
136
+ function createScript(input) {
137
+ const statuses = countBy(input.operations, (operation) => operation.status);
138
+ const kinds = countBy(input.operations, (operation) => operation.kind);
139
+ const reasonCodes = uniqueStrings(input.reasonCodes);
140
+ const core = {
141
+ kind: 'frontier.lang.semanticEditScript',
142
+ version: 1,
143
+ schema: 'frontier.lang.semanticEditScript.v1',
144
+ id: `js_ts_safe_merge_script_${idFragment(input.id)}`,
145
+ stableId: `js_ts_safe_merge_script_${idFragment([input.id, input.merge.mergedSourceText].join(':'))}`,
146
+ language: input.language,
147
+ sourcePath: input.sourcePath,
148
+ baseHash: input.input.baseHash,
149
+ workerHash: input.input.workerHash,
150
+ headHash: input.input.headHash,
151
+ workerChangeSetId: input.input.workerChangeSetId,
152
+ headChangeSetId: input.input.headChangeSetId,
153
+ operations: input.operations,
154
+ summary: {
155
+ operations: input.operations.length,
156
+ byStatus: statuses,
157
+ byKind: kinds,
158
+ portable: statuses.portable ?? 0,
159
+ alreadyApplied: 0,
160
+ needsPort: 0,
161
+ conflicts: 0,
162
+ stale: 0,
163
+ blocked: input.blocked ? input.operations.length : 0,
164
+ candidates: 0,
165
+ autoMergeCandidates: input.blocked ? 0 : input.operations.length,
166
+ operationContentHashes: input.operations.map((operation) => operation.operationContentHash)
167
+ },
168
+ admission: {
169
+ status: input.blocked ? 'blocked' : 'auto-merge-candidate',
170
+ action: input.blocked ? 'block' : 'run-gates-and-apply',
171
+ reviewRequired: input.blocked,
172
+ autoApplyCandidate: !input.blocked,
173
+ autoMergeClaim: false,
174
+ semanticEquivalenceClaim: false,
175
+ reasonCodes,
176
+ conflictKeys: input.operations.map((operation) => operation.anchor.conflictKey),
177
+ evidenceIds: input.operations.flatMap((operation) => operation.evidenceIds ?? [])
178
+ },
179
+ evidence: [],
180
+ metadata: {
181
+ autoMergeClaim: false,
182
+ semanticEquivalenceClaim: false,
183
+ source: 'js-ts-safe-merge-ledger'
184
+ }
185
+ };
186
+ return { ...core, hash: hashSemanticValue(core) };
187
+ }
188
+
189
+ function createProjection(input) {
190
+ const blocked = input.reasonCodes.length > 0;
191
+ const edits = blocked ? [] : input.edits.map(projectionEditRecord);
192
+ const core = {
193
+ kind: 'frontier.lang.semanticEditProjection',
194
+ version: 1,
195
+ id: `js_ts_safe_merge_projection_${idFragment(input.id)}`,
196
+ scriptId: input.script.id,
197
+ status: blocked ? 'blocked' : 'projected',
198
+ sourcePath: input.sourcePath,
199
+ language: input.language,
200
+ baseHash: input.input.baseHash,
201
+ workerHash: input.input.workerHash,
202
+ headHash: input.input.headHash,
203
+ projectedHash: input.sourceText === undefined ? undefined : hashSemanticValue(input.sourceText),
204
+ appliedOperations: edits.map((edit) => edit.operationId).filter(Boolean),
205
+ skippedOperations: blocked ? input.script.operations.map((operation) => operation.id) : input.skippedOperationIds,
206
+ edits,
207
+ sourceText: input.sourceText,
208
+ admission: {
209
+ status: blocked ? 'blocked' : 'auto-merge-candidate',
210
+ autoMergeClaim: false,
211
+ semanticEquivalenceClaim: false,
212
+ reasonCodes: uniqueStrings(input.reasonCodes)
213
+ },
214
+ metadata: {
215
+ autoMergeClaim: false,
216
+ semanticEquivalenceClaim: false,
217
+ source: 'js-ts-safe-merge-ledger'
218
+ }
219
+ };
220
+ return { ...core, hash: hashSemanticValue(core) };
221
+ }
222
+
223
+ function blockedArtifacts(input) {
224
+ const core = {
225
+ kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts',
226
+ version: 1,
227
+ schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1',
228
+ id: `js_ts_safe_merge_semantic_artifacts_${idFragment(input.id)}`,
229
+ sourcePath: input.sourcePath,
230
+ language: input.language,
231
+ status: 'blocked',
232
+ admission: {
233
+ status: 'blocked',
234
+ action: 'human-review',
235
+ reviewRequired: true,
236
+ autoApplyCandidate: false,
237
+ autoMergeClaim: false,
238
+ semanticEquivalenceClaim: false,
239
+ reasonCodes: uniqueStrings(input.reasonCodes)
240
+ },
241
+ summary: {
242
+ operations: 0,
243
+ edits: 0,
244
+ projectedSourceMatchesMerged: false,
245
+ replayOutputMatchesMerged: false
246
+ },
247
+ evidence: [],
248
+ metadata: {
249
+ autoMergeClaim: false,
250
+ semanticEquivalenceClaim: false,
251
+ source: 'js-ts-safe-merge-ledger'
252
+ }
253
+ };
254
+ return { ...core, hash: hashSemanticValue(core) };
255
+ }
256
+
257
+ function countBy(values, keyFor) {
258
+ const result = {};
259
+ for (const value of values) {
260
+ const key = keyFor(value);
261
+ result[key] = (result[key] ?? 0) + 1;
262
+ }
263
+ return result;
264
+ }
265
+
266
+ export { createJsTsSafeMergeSemanticArtifacts };
@@ -8,6 +8,7 @@ import {
8
8
  } from './js-ts-safe-merge-constants.js';
9
9
  import { indexBaseLedger, scanJsTsTopLevelLedger, validateLedgerUniqueness } from './js-ts-safe-merge-ledger.js';
10
10
  import { applySourceMergePlan, createSourceMergePlan } from './js-ts-safe-merge-plan.js';
11
+ import { createJsTsSafeMergeSemanticArtifacts } from './js-ts-safe-merge-semantic-artifacts.js';
11
12
 
12
13
  export { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds, JsTsSafeMergeStatuses };
13
14
 
@@ -61,7 +62,7 @@ export function safeMergeJsTsImportsAndDeclarations(input = {}) {
61
62
  if (!context.conflicts.length) validateLedgerUniqueness(merged, context);
62
63
  if (context.conflicts.length) return blockedResult(context, { base, worker, head, merged });
63
64
 
64
- return {
65
+ const result = {
65
66
  kind: 'frontier.lang.jsTsSafeMerge',
66
67
  version: 1,
67
68
  schema: 'frontier.lang.jsTsSafeMerge.v1',
@@ -99,6 +100,10 @@ export function safeMergeJsTsImportsAndDeclarations(input = {}) {
99
100
  currentSourceHash: input.currentSourceHash
100
101
  })
101
102
  };
103
+ return {
104
+ ...result,
105
+ semanticArtifacts: createJsTsSafeMergeSemanticArtifacts(input, result)
106
+ };
102
107
  }
103
108
 
104
109
  function validateStaleSourceHashes(input, context) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.105",
3
+ "version": "0.2.107",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",