@shapeshift-labs/frontier-lang-compiler 0.2.145 → 0.2.147

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.
@@ -15,6 +15,29 @@ export function syntaxDeclaration(node, nativeNodeId, input) {
15
15
  if (kind === 'ClassDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'class');
16
16
  if (kind === 'TSInterfaceDeclaration' || kind === 'InterfaceDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'interface');
17
17
  if (kind === 'TSTypeAliasDeclaration' || kind === 'TypeAliasDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'type');
18
+ if (kind === 'TSModuleDeclaration' || kind === 'ModuleDeclaration') return syntaxModuleDeclaration(input, nativeNodeId, node);
18
19
  if (kind === 'VariableDeclarator') return namedDeclaration(input, nativeNodeId, node.id, 'variable');
19
20
  return undefined;
20
21
  }
22
+
23
+ function syntaxModuleDeclaration(input, nativeNodeId, node) {
24
+ const name = syntaxName(node.id ?? node.name);
25
+ if (!name) return undefined;
26
+ return {
27
+ ...declarationRecord(input, nativeNodeId, name, 'module', 'definition'),
28
+ metadata: {
29
+ scan: 'syntax-module-declaration',
30
+ moduleName: name,
31
+ namespace: name
32
+ }
33
+ };
34
+ }
35
+
36
+ function syntaxName(node) {
37
+ if (!node) return undefined;
38
+ if (typeof node === 'string') return node;
39
+ if (typeof node.name === 'string') return node.name;
40
+ if (typeof node.value === 'string') return node.value;
41
+ if (typeof node.text === 'string') return node.text;
42
+ return undefined;
43
+ }
@@ -14,6 +14,7 @@ export function typeScriptDeclaration(node, kind, nativeNodeId, input, options =
14
14
  if (kind === 'ClassDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'class')), exportedEntries, enrich, node);
15
15
  if (kind === 'InterfaceDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'interface')), exportedEntries, enrich, node);
16
16
  if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return declarationWithExports(enrich(namedDeclaration(input, nativeNodeId, node.name, 'type')), exportedEntries, enrich, node);
17
+ if (kind === 'ModuleDeclaration') return declarationWithExports(enrich(moduleDeclaration(input, nativeNodeId, node), node.name ?? node), exportedEntries, enrich, node);
17
18
  if (kind === 'VariableDeclaration') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'variable'));
18
19
  if (kind === 'MethodDeclaration' || kind === 'MethodSignature') return enrich(namedDeclaration(input, nativeNodeId, node.name, 'method'));
19
20
  return undefined;
@@ -25,6 +26,19 @@ function declarationWithExports(declaration, exportedEntries, enrich, node) {
25
26
  return exports.length ? [declaration, ...exports] : declaration;
26
27
  }
27
28
 
29
+ function moduleDeclaration(input, nativeNodeId, node) {
30
+ const name = stringFromTsExpression(node.name);
31
+ if (!name) return undefined;
32
+ return {
33
+ ...declarationRecord(input, nativeNodeId, name, 'module', 'definition'),
34
+ metadata: compactRecord({
35
+ scan: 'typescript-module-declaration',
36
+ moduleName: name,
37
+ namespace: name
38
+ })
39
+ };
40
+ }
41
+
28
42
  function enrichTypeScriptDeclaration(node, symbolNode, declaration, input, options) {
29
43
  if (!declaration) return declaration;
30
44
  const checker = options.typeChecker ?? options.checker ?? options.program?.getTypeChecker?.();
@@ -92,6 +92,7 @@ function exportedSymbolKind(kind) {
92
92
  if (kind === 'ClassDeclaration') return 'class';
93
93
  if (kind === 'InterfaceDeclaration') return 'interface';
94
94
  if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return 'type';
95
+ if (kind === 'ModuleDeclaration') return 'module';
95
96
  return undefined;
96
97
  }
97
98
 
@@ -0,0 +1,112 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import {
3
+ independentTopLevelDeletionOperation,
4
+ independentTopLevelDeletionProjection,
5
+ independentTopLevelDeletionReplay,
6
+ independentTopLevelDeletionScript
7
+ } from './js-ts-safe-merge-independent-deletion-records.js';
8
+ import { idFragment, uniqueStrings } from './native-import-utils.js';
9
+
10
+ function createIndependentTopLevelDeletionArtifacts(input, topLevelResult, deletionPlan) {
11
+ const id = String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge');
12
+ const language = input.language ?? topLevelResult.language ?? 'typescript';
13
+ const sourcePath = input.sourcePath ?? topLevelResult.sourcePath ?? 'inline.ts';
14
+ const operationId = `js_ts_independent_top_level_delete_${idFragment([id, deletionPlan.deletedEntry.key].join(':'))}`;
15
+ const operation = independentTopLevelDeletionOperation({ id, operationId, language, sourcePath, deletionPlan, input });
16
+ const script = independentTopLevelDeletionScript({ id, language, sourcePath, operation, input });
17
+ const projection = independentTopLevelDeletionProjection({ id, language, sourcePath, operation, script, deletionPlan, input });
18
+ const replay = independentTopLevelDeletionReplay({
19
+ id: `${id}_semantic_edit_replay`,
20
+ language,
21
+ sourcePath,
22
+ operation,
23
+ projection,
24
+ deletionPlan,
25
+ currentSourceText: input.headSourceText,
26
+ status: 'accepted-clean',
27
+ editStatus: 'applied',
28
+ reasonCodes: ['head-anchor-matches-base', 'independent-top-level-deletion'],
29
+ outputSourceText: deletionPlan.mergedSourceText
30
+ });
31
+ const alreadyAppliedReplay = independentTopLevelDeletionReplay({
32
+ id: `${id}_semantic_edit_already_applied`,
33
+ language,
34
+ sourcePath,
35
+ operation,
36
+ projection,
37
+ deletionPlan,
38
+ currentSourceText: deletionPlan.mergedSourceText,
39
+ status: 'already-applied',
40
+ editStatus: 'already-applied',
41
+ reasonCodes: ['independent-top-level-deletion-already-applied'],
42
+ outputSourceText: deletionPlan.mergedSourceText
43
+ });
44
+ const status = projection.status === 'projected'
45
+ && replay.status === 'accepted-clean'
46
+ && replay.outputSourceText === deletionPlan.mergedSourceText
47
+ && alreadyAppliedReplay.status === 'already-applied'
48
+ ? 'verified'
49
+ : 'blocked';
50
+ const reasonCodes = status === 'verified'
51
+ ? []
52
+ : uniqueStrings([
53
+ ...(projection.admission?.reasonCodes ?? []),
54
+ ...(replay.admission?.reasonCodes ?? []),
55
+ ...(alreadyAppliedReplay.admission?.reasonCodes ?? [])
56
+ ]);
57
+ const core = {
58
+ kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts',
59
+ version: 1,
60
+ schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1',
61
+ id: `js_ts_safe_merge_semantic_edit_artifacts_${idFragment(id)}`,
62
+ sourcePath,
63
+ language,
64
+ status,
65
+ script,
66
+ projection,
67
+ replay,
68
+ alreadyAppliedReplay,
69
+ admission: {
70
+ status: status === 'verified' ? 'auto-merge-candidate' : 'blocked',
71
+ action: status === 'verified' ? 'apply' : 'human-review',
72
+ reviewRequired: status !== 'verified',
73
+ autoApplyCandidate: status === 'verified',
74
+ autoMergeClaim: false,
75
+ semanticEquivalenceClaim: false,
76
+ reasonCodes
77
+ },
78
+ summary: {
79
+ operations: script.summary.operations,
80
+ edits: projection.edits.length,
81
+ replayStatus: replay.status,
82
+ alreadyAppliedReplayStatus: alreadyAppliedReplay.status,
83
+ projectedSourceMatchesMerged: projection.sourceText === deletionPlan.mergedSourceText,
84
+ replayOutputMatchesMerged: replay.outputSourceText === deletionPlan.mergedSourceText
85
+ },
86
+ evidence: [{
87
+ id: `evidence_${idFragment(id)}_independent_top_level_deletion`,
88
+ kind: 'js-ts-independent-top-level-deletion-replay',
89
+ status: status === 'verified' ? 'passed' : 'needs-review',
90
+ path: sourcePath,
91
+ summary: status === 'verified'
92
+ ? 'JS/TS independent top-level deletion replay verified 1 operation.'
93
+ : `JS/TS independent top-level deletion requires review: ${reasonCodes.join(', ')}.`,
94
+ metadata: {
95
+ autoMergeClaim: false,
96
+ semanticEquivalenceClaim: false,
97
+ deletedKey: deletionPlan.deletedEntry.key,
98
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? []
99
+ }
100
+ }],
101
+ metadata: {
102
+ autoMergeClaim: false,
103
+ semanticEquivalenceClaim: false,
104
+ source: 'independent-top-level-deletion-fallback',
105
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? [],
106
+ deletion: deletionPlan.summary
107
+ }
108
+ };
109
+ return { ...core, hash: hashSemanticValue(core) };
110
+ }
111
+
112
+ export { createIndependentTopLevelDeletionArtifacts };
@@ -0,0 +1,53 @@
1
+ import { JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
2
+ import { createIndependentTopLevelDeletionArtifacts } from './js-ts-safe-merge-independent-deletion-artifacts.js';
3
+ import { createIndependentTopLevelDeletionPlan } from './js-ts-safe-merge-independent-deletion-plan.js';
4
+ import { semanticEditGates } from './js-ts-safe-merge-semantic-edit-gates.js';
5
+
6
+ function independentTopLevelDeletionFallbackResult(input, topLevelResult) {
7
+ const deletionPlan = createIndependentTopLevelDeletionPlan(input, topLevelResult);
8
+ if (!deletionPlan.ok) return undefined;
9
+ const artifacts = createIndependentTopLevelDeletionArtifacts(input, topLevelResult, deletionPlan);
10
+ if (artifacts.status !== 'verified') return undefined;
11
+ const gates = semanticEditGates(artifacts);
12
+ return {
13
+ ...topLevelResult,
14
+ id: String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge'),
15
+ status: JsTsSafeMergeStatuses.merged,
16
+ mergedSourceText: deletionPlan.mergedSourceText,
17
+ outputSourceText: deletionPlan.mergedSourceText,
18
+ conflicts: [],
19
+ gates,
20
+ admission: {
21
+ status: 'auto-merge-candidate',
22
+ action: 'apply',
23
+ reviewRequired: false,
24
+ autoApplyCandidate: true,
25
+ autoMergeClaim: false,
26
+ semanticEquivalenceClaim: false,
27
+ reasonCodes: []
28
+ },
29
+ summary: {
30
+ ...topLevelResult.summary,
31
+ changedExistingDeclarations: 0,
32
+ conflicts: 0,
33
+ gatesPassed: gates.filter((gateRecord) => gateRecord.status === 'passed').length,
34
+ topLevelDeclarationDeletions: 1,
35
+ semanticEditOperations: artifacts.script.summary.operations,
36
+ semanticEditAppliedOperations: artifacts.replay.summary.applied,
37
+ semanticEditReplayStatus: artifacts.replay.status,
38
+ composedPhases: 2
39
+ },
40
+ metadata: {
41
+ ...topLevelResult.metadata,
42
+ composed: {
43
+ phase: 'independent-top-level-deletion-fallback',
44
+ phases: ['top-level-ledger', 'independent-top-level-deletion'],
45
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? [],
46
+ deletion: deletionPlan.summary
47
+ }
48
+ },
49
+ semanticArtifacts: artifacts
50
+ };
51
+ }
52
+
53
+ export { independentTopLevelDeletionFallbackResult };
@@ -0,0 +1,148 @@
1
+ import { JsTsSafeMergeConflictCodes } from './js-ts-safe-merge-constants.js';
2
+ import { sameStatementText } from './js-ts-safe-merge-context.js';
3
+ import { scanJsTsTopLevelLedger } from './js-ts-safe-merge-ledger.js';
4
+ import { uniqueStrings } from './native-import-utils.js';
5
+
6
+ function createIndependentTopLevelDeletionPlan(input, topLevelResult) {
7
+ const originalReasonCodes = topLevelResult?.admission?.reasonCodes ?? [];
8
+ if (!originalReasonCodes.includes(JsTsSafeMergeConflictCodes.topLevelOrderChanged)) {
9
+ return { ok: false, reasonCodes: ['top-level-order-not-deletion-shaped'] };
10
+ }
11
+ const allowedOriginalReasonCodes = new Set([
12
+ JsTsSafeMergeConflictCodes.topLevelOrderChanged,
13
+ JsTsSafeMergeConflictCodes.changedExistingDeclaration,
14
+ JsTsSafeMergeConflictCodes.typeAliasConflict
15
+ ]);
16
+ if (originalReasonCodes.some((reason) => !allowedOriginalReasonCodes.has(reason))) {
17
+ return { ok: false, reasonCodes: ['top-level-deletion-has-unsafe-original-conflict'] };
18
+ }
19
+ if (typeof input.baseSourceText !== 'string'
20
+ || typeof input.workerSourceText !== 'string'
21
+ || typeof input.headSourceText !== 'string') {
22
+ return { ok: false, reasonCodes: ['missing-source-text'] };
23
+ }
24
+
25
+ const context = quietDeletionLedgerContext(input);
26
+ const base = scanJsTsTopLevelLedger(input.baseSourceText, 'base', context);
27
+ const worker = scanJsTsTopLevelLedger(input.workerSourceText, 'worker', context);
28
+ const head = scanJsTsTopLevelLedger(input.headSourceText, 'head', context);
29
+ if (context.conflicts.length) {
30
+ return { ok: false, reasonCodes: uniqueStrings(context.conflicts.map((conflict) => conflict.code)) };
31
+ }
32
+
33
+ const duplicateLedgerReason = firstDuplicateLedgerReason(base, worker, head);
34
+ if (duplicateLedgerReason) return { ok: false, reasonCodes: [duplicateLedgerReason] };
35
+
36
+ const baseByKey = entriesByKey(base.entries);
37
+ const workerByKey = entriesByKey(worker.entries);
38
+ const headByKey = entriesByKey(head.entries);
39
+ const baseKeys = base.entries.map((entry) => entry.key);
40
+ const missingWorkerBaseEntries = base.entries.filter((entry) => !workerByKey.has(entry.key));
41
+ const deletedWorkerEntries = missingWorkerBaseEntries.filter((entry) => entry.kind !== 'import');
42
+ const workerAddedEntries = worker.entries.filter((entry) => !baseByKey.has(entry.key));
43
+ if (missingWorkerBaseEntries.length !== 1
44
+ || deletedWorkerEntries.length !== 1
45
+ || workerAddedEntries.length !== 0) {
46
+ return { ok: false, reasonCodes: ['worker-top-level-deletion-not-isolated'] };
47
+ }
48
+
49
+ const deletedEntry = deletedWorkerEntries[0];
50
+ if (deletedEntry.kind !== 'declaration' || deletedEntry.declarationInfo?.exported === true) {
51
+ return { ok: false, reasonCodes: ['exported-or-unsupported-top-level-deletion'] };
52
+ }
53
+
54
+ const expectedWorkerKeys = base.entries
55
+ .filter((entry) => entry.key !== deletedEntry.key)
56
+ .map((entry) => entry.key);
57
+ if (!sameStringList(worker.entries.map((entry) => entry.key), expectedWorkerKeys)) {
58
+ return { ok: false, reasonCodes: ['worker-top-level-deletion-order-changed'] };
59
+ }
60
+ for (const baseEntry of base.entries) {
61
+ if (baseEntry.key === deletedEntry.key) continue;
62
+ const workerEntry = workerByKey.get(baseEntry.key);
63
+ if (!workerEntry || !sameStatementText(workerEntry.text, baseEntry.text)) {
64
+ return { ok: false, reasonCodes: [`worker-changed-nondeleted-entry:${baseEntry.key}`] };
65
+ }
66
+ }
67
+
68
+ const headProjectedBaseKeys = head.entries
69
+ .filter((entry) => baseByKey.has(entry.key))
70
+ .map((entry) => entry.key);
71
+ if (!sameStringList(headProjectedBaseKeys, baseKeys)) {
72
+ return { ok: false, reasonCodes: ['head-base-order-or-presence-changed'] };
73
+ }
74
+
75
+ const headEntry = headByKey.get(deletedEntry.key);
76
+ if (!headEntry) return { ok: false, reasonCodes: [`head-anchor-missing:${deletedEntry.key}`] };
77
+ if (!sameStatementText(headEntry.text, deletedEntry.text)) {
78
+ return { ok: false, reasonCodes: [`head-anchor-changed-since-base:${deletedEntry.key}`] };
79
+ }
80
+
81
+ const edit = deleteEntryEdit(input.headSourceText, headEntry);
82
+ const deletedText = input.headSourceText.slice(edit.start, edit.end);
83
+ const mergedSourceText = applySourceEdits(input.headSourceText, [edit]);
84
+ return {
85
+ ok: true,
86
+ base,
87
+ worker,
88
+ head,
89
+ deletedEntry,
90
+ headEntry,
91
+ edit,
92
+ deletedText,
93
+ mergedSourceText,
94
+ summary: {
95
+ key: deletedEntry.key,
96
+ name: deletedEntry.names?.[0],
97
+ declarationKind: deletedEntry.declarationInfo?.declarationKind,
98
+ exported: false,
99
+ reasonCodes: ['head-anchor-matches-base', 'independent-top-level-deletion']
100
+ }
101
+ };
102
+ }
103
+
104
+ function quietDeletionLedgerContext(input) {
105
+ return {
106
+ id: String(input.id ?? 'js_ts_safe_merge'),
107
+ sourcePath: input.sourcePath,
108
+ language: input.language ?? 'typescript',
109
+ conflicts: [],
110
+ blockedGateIds: new Set(),
111
+ gateReasonCodes: new Map()
112
+ };
113
+ }
114
+
115
+ function firstDuplicateLedgerReason(...ledgers) {
116
+ for (const ledger of ledgers) {
117
+ const seen = new Set();
118
+ for (const entry of ledger.entries) {
119
+ if (seen.has(entry.key)) return `duplicate-ledger-key:${ledger.label}:${entry.key}`;
120
+ seen.add(entry.key);
121
+ }
122
+ }
123
+ return undefined;
124
+ }
125
+
126
+ function entriesByKey(entries) {
127
+ return new Map(entries.map((entry) => [entry.key, entry]));
128
+ }
129
+
130
+ function sameStringList(left, right) {
131
+ return left.length === right.length && left.every((value, index) => value === right[index]);
132
+ }
133
+
134
+ function deleteEntryEdit(sourceText, entry) {
135
+ const lineEnd = sourceText.indexOf('\n', entry.end);
136
+ const end = lineEnd === -1 ? entry.end : lineEnd + 1;
137
+ const lineStart = sourceText.lastIndexOf('\n', Math.max(0, entry.start - 1)) + 1;
138
+ const start = /^[\t ]*$/.test(sourceText.slice(lineStart, entry.start)) ? lineStart : entry.start;
139
+ return { start, end, text: '' };
140
+ }
141
+
142
+ function applySourceEdits(sourceText, edits) {
143
+ return [...edits]
144
+ .sort((left, right) => right.start - left.start || right.end - left.end)
145
+ .reduce((current, edit) => `${current.slice(0, edit.start)}${edit.text}${current.slice(edit.end)}`, sourceText);
146
+ }
147
+
148
+ export { createIndependentTopLevelDeletionPlan };
@@ -0,0 +1,251 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { idFragment } from './native-import-utils.js';
3
+
4
+ function independentTopLevelDeletionOperation(input) {
5
+ const entry = input.deletionPlan.deletedEntry;
6
+ const name = entry.names?.[0] ?? entry.key;
7
+ const symbolKind = entry.declarationInfo?.declarationKind ?? 'declaration';
8
+ const anchorKey = `source#${input.sourcePath}#declaration#${name}`;
9
+ const operation = {
10
+ id: input.operationId,
11
+ kind: 'jsTsRemoveTopLevelDeclaration',
12
+ changeKind: 'removed',
13
+ anchor: {
14
+ key: anchorKey,
15
+ conflictKey: `declaration:${entry.key}`,
16
+ regionId: anchorKey,
17
+ regionKind: 'declaration',
18
+ granularity: 'js-ts-ledger-entry',
19
+ language: input.language,
20
+ sourcePath: input.sourcePath,
21
+ symbolId: anchorKey,
22
+ symbolName: name,
23
+ symbolKind,
24
+ sourceSpan: { start: input.deletionPlan.headEntry.start, end: input.deletionPlan.headEntry.end }
25
+ },
26
+ spans: {
27
+ base: { start: entry.start, end: entry.end },
28
+ head: { start: input.deletionPlan.headEntry.start, end: input.deletionPlan.headEntry.end }
29
+ },
30
+ hashes: {
31
+ baseSourceHash: input.input.baseHash,
32
+ workerSourceHash: input.input.workerHash,
33
+ headSourceHash: input.input.headHash,
34
+ baseTextHash: hashSemanticValue(entry.text),
35
+ headTextHash: hashSemanticValue(input.deletionPlan.headEntry.text),
36
+ workerTextHash: hashSemanticValue('')
37
+ },
38
+ status: 'portable',
39
+ readiness: 'ready',
40
+ confidence: 1,
41
+ reasonCodes: ['head-anchor-matches-base', 'independent-top-level-deletion'],
42
+ evidenceIds: [`evidence_${idFragment(input.id)}_independent_top_level_deletion`],
43
+ metadata: {
44
+ autoMergeClaim: false,
45
+ semanticEquivalenceClaim: false,
46
+ ledgerKey: entry.key,
47
+ exported: false
48
+ }
49
+ };
50
+ return { ...operation, operationContentHash: hashSemanticValue(operation) };
51
+ }
52
+
53
+ function independentTopLevelDeletionScript(input) {
54
+ const core = {
55
+ kind: 'frontier.lang.semanticEditScript',
56
+ version: 1,
57
+ schema: 'frontier.lang.semanticEditScript.v1',
58
+ id: `${input.id}_semantic_edit`,
59
+ stableId: `semantic_edit_script_${idFragment([input.id, input.operation.operationContentHash].join(':'))}`,
60
+ language: input.language,
61
+ sourcePath: input.sourcePath,
62
+ baseHash: input.input.baseHash,
63
+ workerHash: input.input.workerHash,
64
+ headHash: input.input.headHash,
65
+ operations: [input.operation],
66
+ summary: {
67
+ operations: 1,
68
+ byStatus: { portable: 1 },
69
+ byKind: { jsTsRemoveTopLevelDeclaration: 1 },
70
+ portable: 1,
71
+ alreadyApplied: 0,
72
+ needsPort: 0,
73
+ conflicts: 0,
74
+ stale: 0,
75
+ blocked: 0,
76
+ candidates: 0,
77
+ autoMergeCandidates: 1,
78
+ operationContentHashes: [input.operation.operationContentHash]
79
+ },
80
+ admission: {
81
+ status: 'auto-merge-candidate',
82
+ action: 'run-gates-and-apply',
83
+ reviewRequired: false,
84
+ autoApplyCandidate: true,
85
+ autoMergeClaim: false,
86
+ semanticEquivalenceClaim: false,
87
+ reasonCodes: input.operation.reasonCodes,
88
+ conflictKeys: [input.operation.anchor.conflictKey],
89
+ evidenceIds: input.operation.evidenceIds
90
+ },
91
+ evidence: [{
92
+ id: `evidence_${idFragment(input.id)}_independent_top_level_deletion_script`,
93
+ kind: 'semantic-edit-script',
94
+ status: 'passed',
95
+ path: input.sourcePath,
96
+ summary: 'Created independent top-level deletion script with 1 operation.',
97
+ metadata: {
98
+ autoMergeClaim: false,
99
+ semanticEquivalenceClaim: false
100
+ }
101
+ }],
102
+ metadata: {
103
+ autoMergeClaim: false,
104
+ semanticEquivalenceClaim: false,
105
+ source: 'independent-top-level-deletion-fallback'
106
+ }
107
+ };
108
+ return { ...core, hash: hashSemanticValue(core) };
109
+ }
110
+
111
+ function independentTopLevelDeletionProjection(input) {
112
+ const editContentHash = hashSemanticValue({
113
+ operationId: input.operation.id,
114
+ deletedText: input.deletionPlan.deletedText,
115
+ replacementText: ''
116
+ });
117
+ const edit = {
118
+ operationId: input.operation.id,
119
+ status: 'applied',
120
+ kind: input.operation.kind,
121
+ editKind: 'delete',
122
+ changeKind: 'removed',
123
+ anchorKey: input.operation.anchor.key,
124
+ conflictKey: input.operation.anchor.conflictKey,
125
+ regionId: input.operation.anchor.regionId,
126
+ regionKind: input.operation.anchor.regionKind,
127
+ sourcePath: input.sourcePath,
128
+ symbolId: input.operation.anchor.symbolId,
129
+ symbolName: input.operation.anchor.symbolName,
130
+ symbolKind: input.operation.anchor.symbolKind,
131
+ operationContentHash: input.operation.operationContentHash,
132
+ editContentHash,
133
+ headStart: input.deletionPlan.edit.start,
134
+ headEnd: input.deletionPlan.edit.end,
135
+ editOrder: 0,
136
+ deletedBytes: input.deletionPlan.deletedText.length,
137
+ replacementBytes: 0,
138
+ deletedTextHash: hashSemanticValue(input.deletionPlan.deletedText),
139
+ replacementTextHash: hashSemanticValue(''),
140
+ deletedTextLineEndingStableHash: lineEndingStableTextHash(input.deletionPlan.deletedText),
141
+ replacementTextLineEndingStableHash: lineEndingStableTextHash(''),
142
+ replacementText: ''
143
+ };
144
+ const core = {
145
+ kind: 'frontier.lang.semanticEditProjection',
146
+ version: 1,
147
+ id: `${input.id}_semantic_edit_projection`,
148
+ scriptId: input.script.id,
149
+ status: 'projected',
150
+ sourcePath: input.sourcePath,
151
+ language: input.language,
152
+ baseHash: input.input.baseHash,
153
+ workerHash: input.input.workerHash,
154
+ headHash: input.input.headHash,
155
+ projectedHash: hashSemanticValue(input.deletionPlan.mergedSourceText),
156
+ appliedOperations: [input.operation.id],
157
+ skippedOperations: [],
158
+ edits: [edit],
159
+ sourceText: input.deletionPlan.mergedSourceText,
160
+ admission: {
161
+ status: 'auto-merge-candidate',
162
+ autoMergeClaim: false,
163
+ semanticEquivalenceClaim: false,
164
+ reasonCodes: []
165
+ },
166
+ metadata: {
167
+ autoMergeClaim: false,
168
+ semanticEquivalenceClaim: false,
169
+ source: 'independent-top-level-deletion-fallback'
170
+ }
171
+ };
172
+ return { ...core, hash: hashSemanticValue(core) };
173
+ }
174
+
175
+ function independentTopLevelDeletionReplay(input) {
176
+ const applied = input.editStatus === 'applied';
177
+ const edit = {
178
+ operationId: input.operation.id,
179
+ editKind: 'delete',
180
+ editOrder: 0,
181
+ sourcePath: input.sourcePath,
182
+ symbolName: input.operation.anchor.symbolName,
183
+ symbolKind: input.operation.anchor.symbolKind,
184
+ status: input.editStatus,
185
+ start: applied ? input.deletionPlan.edit.start : undefined,
186
+ end: applied ? input.deletionPlan.edit.end : undefined,
187
+ replacementBytes: 0,
188
+ replacementText: '',
189
+ reasonCodes: input.reasonCodes
190
+ };
191
+ const core = {
192
+ kind: 'frontier.lang.semanticEditReplay',
193
+ version: 1,
194
+ schema: 'frontier.lang.semanticEditReplay.v1',
195
+ id: input.id,
196
+ projectionId: input.projection.id,
197
+ scriptId: input.projection.scriptId,
198
+ sourcePath: input.sourcePath,
199
+ language: input.language,
200
+ currentHash: hashSemanticValue(input.currentSourceText),
201
+ projectedHash: input.projection.projectedHash,
202
+ outputHash: hashSemanticValue(input.outputSourceText),
203
+ status: input.status,
204
+ edits: [edit],
205
+ appliedOperations: applied ? [input.operation.id] : [],
206
+ skippedOperations: applied ? [] : [input.operation.id],
207
+ admission: {
208
+ status: input.status,
209
+ action: applied ? 'apply' : 'skip',
210
+ reviewRequired: false,
211
+ autoApplyCandidate: applied,
212
+ autoMergeClaim: false,
213
+ semanticEquivalenceClaim: false,
214
+ reasonCodes: []
215
+ },
216
+ outputSourceText: input.outputSourceText,
217
+ summary: {
218
+ edits: 1,
219
+ applied: applied ? 1 : 0,
220
+ alreadyApplied: applied ? 0 : 1,
221
+ conflicts: 0,
222
+ stale: 0,
223
+ blocked: 0,
224
+ reasonCodes: []
225
+ },
226
+ metadata: {
227
+ autoMergeClaim: false,
228
+ semanticEquivalenceClaim: false,
229
+ source: 'independent-top-level-deletion-fallback'
230
+ }
231
+ };
232
+ return { ...core, hash: hashSemanticValue(core) };
233
+ }
234
+
235
+ function lineEndingStableTextHash(value) {
236
+ const normalized = lineEndingStableText(value);
237
+ return normalized === undefined ? undefined : hashSemanticValue(normalized);
238
+ }
239
+
240
+ function lineEndingStableText(value) {
241
+ if (typeof value !== 'string') return undefined;
242
+ const normalized = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
243
+ return normalized.length > 1 && normalized.endsWith('\n') ? normalized.slice(0, -1) : normalized;
244
+ }
245
+
246
+ export {
247
+ independentTopLevelDeletionOperation,
248
+ independentTopLevelDeletionProjection,
249
+ independentTopLevelDeletionReplay,
250
+ independentTopLevelDeletionScript
251
+ };
@@ -0,0 +1,82 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { uniqueStrings } from './native-import-utils.js';
3
+
4
+ function normalizeAlreadyAppliedDeleteReplay(input) {
5
+ if (!isProjectedDeleteAlreadyAppliedReplay(input)) return input.alreadyAppliedReplay;
6
+ const { hash: _hash, ...alreadyAppliedReplay } = input.alreadyAppliedReplay;
7
+ const normalizedEdits = input.alreadyAppliedReplay.edits.map((edit) => alreadyAppliedDeleteEdit(edit));
8
+ const reasonCodes = uniqueStrings(normalizedEdits.flatMap((edit) => edit.reasonCodes ?? []));
9
+ const core = {
10
+ ...alreadyAppliedReplay,
11
+ currentHash: input.projection.projectedHash,
12
+ outputHash: input.projection.projectedHash,
13
+ status: 'already-applied',
14
+ edits: normalizedEdits,
15
+ appliedOperations: [],
16
+ skippedOperations: normalizedEdits.map((edit) => edit.operationId).filter(Boolean),
17
+ diagnostics: [],
18
+ admission: {
19
+ status: 'already-applied',
20
+ action: 'skip',
21
+ reviewRequired: false,
22
+ autoApplyCandidate: false,
23
+ autoMergeClaim: false,
24
+ semanticEquivalenceClaim: false,
25
+ reasonCodes
26
+ },
27
+ outputSourceText: input.projection.sourceText,
28
+ summary: {
29
+ edits: normalizedEdits.length,
30
+ applied: 0,
31
+ alreadyApplied: normalizedEdits.length,
32
+ conflicts: 0,
33
+ stale: 0,
34
+ blocked: 0,
35
+ reasonCodes
36
+ },
37
+ metadata: {
38
+ ...input.alreadyAppliedReplay.metadata,
39
+ normalizedAlreadyAppliedReplay: 'projected-delete-anchor-absent'
40
+ }
41
+ };
42
+ return { ...core, hash: hashSemanticValue(core) };
43
+ }
44
+
45
+ function isProjectedDeleteAlreadyAppliedReplay(input) {
46
+ if (input.projection?.status !== 'projected') return false;
47
+ if (input.replay?.status !== 'accepted-clean') return false;
48
+ if (input.replay.outputSourceText !== input.projection.sourceText) return false;
49
+ const replay = input.alreadyAppliedReplay;
50
+ if (replay?.status !== 'stale') return false;
51
+ if (replay.currentHash !== input.projection.projectedHash) return false;
52
+ const edits = replay.edits ?? [];
53
+ if (!edits.length) return false;
54
+ const appliedDeleteIds = new Set((input.replay.edits ?? [])
55
+ .filter((edit) => edit.status === 'applied' && edit.editKind === 'delete')
56
+ .map((edit) => edit.operationId));
57
+ const projectionDeleteIds = new Set((input.projection.edits ?? [])
58
+ .filter((edit) => edit.editKind === 'delete')
59
+ .map((edit) => edit.operationId));
60
+ const staleDeletes = edits.filter(isProjectedDeleteMissingAnchor);
61
+ return staleDeletes.length > 0
62
+ && edits.every((edit) => edit.status === 'already-applied' || isProjectedDeleteMissingAnchor(edit))
63
+ && staleDeletes.every((edit) => appliedDeleteIds.has(edit.operationId) && projectionDeleteIds.has(edit.operationId));
64
+ }
65
+
66
+ function isProjectedDeleteMissingAnchor(edit) {
67
+ return edit.editKind === 'delete'
68
+ && edit.status === 'stale'
69
+ && (edit.reasonCodes ?? []).includes('current-symbol-anchor-missing');
70
+ }
71
+
72
+ function alreadyAppliedDeleteEdit(edit) {
73
+ if (!isProjectedDeleteMissingAnchor(edit)) return edit;
74
+ return {
75
+ ...edit,
76
+ status: 'already-applied',
77
+ reasonCodes: ['projected-delete-anchor-absent'],
78
+ diagnostics: []
79
+ };
80
+ }
81
+
82
+ export { normalizeAlreadyAppliedDeleteReplay };
@@ -3,12 +3,15 @@ import { createSemanticEditScript } from './internal/index-impl/semanticEditScri
3
3
  import { projectSemanticEditScriptToSource } from './internal/index-impl/projectSemanticEditScriptToSource.js';
4
4
  import { replaySemanticEditProjection } from './internal/index-impl/replaySemanticEditProjection.js';
5
5
  import { JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
6
+ import { independentTopLevelDeletionFallbackResult } from './js-ts-safe-merge-independent-deletion-fallback.js';
7
+ import { normalizeAlreadyAppliedDeleteReplay } from './js-ts-safe-merge-semantic-edit-already-applied.js';
6
8
  import {
7
9
  semanticFallbackChangedExistingDeclarations,
8
10
  semanticFallbackConflictCode,
9
11
  semanticFallbackPhase,
10
12
  shouldTrySemanticEditFallback
11
13
  } from './js-ts-safe-merge-semantic-edit-fallback-utils.js';
14
+ import { semanticEditGates } from './js-ts-safe-merge-semantic-edit-gates.js';
12
15
  import {
13
16
  createStagedDeclarationAlreadyAppliedReplay,
14
17
  createStagedDeclarationProjection,
@@ -19,6 +22,8 @@ import { createSourceShapeSemanticFallbackResult } from './js-ts-safe-merge-sour
19
22
  import { idFragment, uniqueStrings } from './native-import-utils.js';
20
23
 
21
24
  function semanticEditFallbackResult(input, topLevelResult) {
25
+ const independentDeletionResult = independentTopLevelDeletionFallbackResult(input, topLevelResult);
26
+ if (independentDeletionResult) return independentDeletionResult;
22
27
  if (!shouldTrySemanticEditFallback(topLevelResult)) return topLevelResult;
23
28
  const stagedFallback = createStagedTopLevelSemanticFallback(input, topLevelResult);
24
29
  const candidates = semanticFallbackCandidates(stagedFallback);
@@ -132,14 +137,18 @@ function createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallba
132
137
  });
133
138
  const alreadyAppliedReplay = stagedDeclarationProjection
134
139
  ? createStagedDeclarationAlreadyAppliedReplay({ id, projection, sourcePath, language })
135
- : replaySemanticEditProjection({
136
- id: `${id}_semantic_edit_already_applied`,
140
+ : normalizeAlreadyAppliedDeleteReplay({
137
141
  projection,
138
- currentSourceText: projection.sourceText,
139
- currentSourcePath: sourcePath,
140
- currentSourceHash: projection.projectedHash,
141
- language,
142
- parser: input.parser
142
+ replay,
143
+ alreadyAppliedReplay: replaySemanticEditProjection({
144
+ id: `${id}_semantic_edit_already_applied`,
145
+ projection,
146
+ currentSourceText: projection.sourceText,
147
+ currentSourcePath: sourcePath,
148
+ currentSourceHash: projection.projectedHash,
149
+ language,
150
+ parser: input.parser
151
+ })
143
152
  });
144
153
  return semanticEditArtifacts({
145
154
  id,
@@ -301,19 +310,4 @@ function semanticEditFallbackBlockedResult(input, topLevelResult, artifacts) {
301
310
  };
302
311
  }
303
312
 
304
- function semanticEditGates(artifacts) {
305
- return [
306
- gate('semantic-edit-script', artifacts.script?.admission?.status === 'auto-merge-candidate', artifacts.script?.admission?.reasonCodes),
307
- gate('semantic-edit-projection', artifacts.projection?.status === 'projected', artifacts.projection?.admission?.reasonCodes),
308
- gate('semantic-edit-replay', artifacts.replay?.status === 'accepted-clean', artifacts.replay?.admission?.reasonCodes),
309
- gate('semantic-edit-already-applied', artifacts.alreadyAppliedReplay?.status === 'already-applied', artifacts.alreadyAppliedReplay?.admission?.reasonCodes)
310
- ];
311
- }
312
-
313
- function gate(id, passed, reasonCodes = []) {
314
- return { id, status: passed ? 'passed' : 'blocked', reasonCodes: passed ? [] : uniqueStrings(reasonCodes) };
315
- }
316
-
317
- export {
318
- semanticEditFallbackResult
319
- };
313
+ export { semanticEditFallbackResult };
@@ -0,0 +1,16 @@
1
+ import { uniqueStrings } from './native-import-utils.js';
2
+
3
+ function semanticEditGates(artifacts) {
4
+ return [
5
+ gate('semantic-edit-script', artifacts.script?.admission?.status === 'auto-merge-candidate', artifacts.script?.admission?.reasonCodes),
6
+ gate('semantic-edit-projection', artifacts.projection?.status === 'projected', artifacts.projection?.admission?.reasonCodes),
7
+ gate('semantic-edit-replay', artifacts.replay?.status === 'accepted-clean', artifacts.replay?.admission?.reasonCodes),
8
+ gate('semantic-edit-already-applied', artifacts.alreadyAppliedReplay?.status === 'already-applied', artifacts.alreadyAppliedReplay?.admission?.reasonCodes)
9
+ ];
10
+ }
11
+
12
+ function gate(id, passed, reasonCodes = []) {
13
+ return { id, status: passed ? 'passed' : 'blocked', reasonCodes: passed ? [] : uniqueStrings(reasonCodes) };
14
+ }
15
+
16
+ export { semanticEditGates };
@@ -1,6 +1,7 @@
1
1
  import { idFragment } from './native-import-utils.js';
2
2
  import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
3
3
  import { compactRecord } from './js-ts-safe-merge-context.js';
4
+ import { augmentProjectSymbolGraphPublicContracts } from './js-ts-safe-project-merge-public-contracts.js';
4
5
  import { createNativeProjectImportResult } from './internal/index-impl/createNativeProjectImportResult.js';
5
6
  import { importNativeSource } from './internal/index-impl/importNativeSource.js';
6
7
  import {
@@ -92,22 +93,26 @@ function createProjectGraphStageArtifacts(input, files, mergeId, stageName, stag
92
93
  ...(stageName === 'output' ? { outputProjectImportSource: projectGraphImportSource } : {})
93
94
  }
94
95
  }, imports);
95
- const edgeLimitConflicts = projectGraphEdgeLimitConflicts(limits, stageName, projectImport.projectSymbolGraph);
96
+ const projectSymbolGraph = augmentProjectSymbolGraphPublicContracts(projectImport.projectSymbolGraph, files, input, stageName);
97
+ const stageProjectImport = projectSymbolGraph === projectImport.projectSymbolGraph
98
+ ? projectImport
99
+ : attachProjectSymbolGraph(projectImport, projectSymbolGraph);
100
+ const edgeLimitConflicts = projectGraphEdgeLimitConflicts(limits, stageName, projectSymbolGraph);
96
101
  const serialized = projectGraphSerializedLimitConflict(limits, stageName, {
97
- projectImport,
98
- projectSymbolGraph: projectImport.projectSymbolGraph
102
+ projectImport: stageProjectImport,
103
+ projectSymbolGraph
99
104
  });
100
105
  const limitConflicts = [...edgeLimitConflicts, serialized.conflict].filter(Boolean);
101
106
  if (limitConflicts.length) {
102
- return limitedProjectGraphStage(stageName, projectGraphImportSource, sourceStats, projectImport.projectSymbolGraph, limitConflicts, serialized.serializedBytes);
107
+ return limitedProjectGraphStage(stageName, projectGraphImportSource, sourceStats, projectSymbolGraph, limitConflicts, serialized.serializedBytes);
103
108
  }
104
109
  return {
105
110
  kind: 'frontier.lang.jsTsProjectGraphStage',
106
111
  version: 1,
107
112
  stage: stageName,
108
- projectImport,
109
- projectSymbolGraph: projectImport.projectSymbolGraph,
110
- summary: projectGraphStageSummary(stageName, projectImport.projectSymbolGraph, projectGraphImportSource, sourceStats, serialized.serializedBytes, [])
113
+ projectImport: stageProjectImport,
114
+ projectSymbolGraph,
115
+ summary: projectGraphStageSummary(stageName, projectSymbolGraph, projectGraphImportSource, sourceStats, serialized.serializedBytes, [])
111
116
  };
112
117
  }
113
118
 
@@ -215,6 +220,24 @@ function stageFile(file, sourceText, input) {
215
220
  });
216
221
  }
217
222
 
223
+ function attachProjectSymbolGraph(projectImport, projectSymbolGraph) {
224
+ return {
225
+ ...projectImport,
226
+ projectSymbolGraph,
227
+ semanticIndex: projectImport.semanticIndex ? {
228
+ ...projectImport.semanticIndex,
229
+ metadata: {
230
+ ...projectImport.semanticIndex.metadata,
231
+ projectSymbolGraph
232
+ }
233
+ } : projectImport.semanticIndex,
234
+ metadata: {
235
+ ...projectImport.metadata,
236
+ projectSymbolGraph
237
+ }
238
+ };
239
+ }
240
+
218
241
  function hashText(sourceText) {
219
242
  return hashSemanticValue(sourceText);
220
243
  }
@@ -0,0 +1,117 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { compactRecord } from './js-ts-safe-merge-context.js';
3
+ import { findContainer, normalizeKind, normalizeMemberText, parseMembers } from './js-ts-semantic-merge-parse.js';
4
+ import { idFragment } from './native-import-utils.js';
5
+
6
+ function augmentProjectSymbolGraphPublicContracts(projectSymbolGraph, files, input, stageName) {
7
+ const existingRegions = Array.isArray(projectSymbolGraph?.publicContractRegions) ? projectSymbolGraph.publicContractRegions : [];
8
+ const seenKeys = new Set(existingRegions.map((region) => region?.key).filter(Boolean));
9
+ const syntheticRegions = [];
10
+ for (const file of files) {
11
+ syntheticRegions.push(...syntheticPublicContractRegions(file, input, stageName, seenKeys));
12
+ }
13
+ if (!syntheticRegions.length) return projectSymbolGraph;
14
+ return {
15
+ ...projectSymbolGraph,
16
+ publicContractRegions: [...existingRegions, ...syntheticRegions]
17
+ };
18
+ }
19
+
20
+ function syntheticPublicContractRegions(file, input, stageName, seenKeys) {
21
+ if (typeof file?.sourceText !== 'string' || !file.sourcePath) return [];
22
+ const records = [];
23
+ for (const region of publicContractPolicyRegionsForPath(input, file.sourcePath)) {
24
+ const kind = normalizeKind(region?.kind);
25
+ if (!['interface', 'type', 'class'].includes(kind) || typeof region?.name !== 'string') continue;
26
+ const container = findContainer(file.sourceText, region);
27
+ if (container.reasonCodes.length || !isExportedContainer(file.sourceText, container.value)) continue;
28
+ const members = parseMembers(container.value.body, kind);
29
+ if (members.reasonCodes.length) continue;
30
+ const key = `source#${file.sourcePath}#export#${region.name}`;
31
+ if (seenKeys.has(key)) continue;
32
+ seenKeys.add(key);
33
+ records.push(syntheticPublicContractRegion(file, input, stageName, kind, region.name, key, members.members));
34
+ }
35
+ return records;
36
+ }
37
+
38
+ function syntheticPublicContractRegion(file, input, stageName, kind, name, key, members) {
39
+ const language = file.language ?? input.language ?? languageForPath(file.sourcePath);
40
+ const memberRecords = members.map((member) => compactRecord({
41
+ key: member.key,
42
+ memberKind: member.memberKind,
43
+ signature: normalizeMemberText(member.text, kind)
44
+ }));
45
+ const signatureHash = hashSemanticValue({
46
+ kind: 'frontier.lang.syntheticPublicContractSignature',
47
+ language,
48
+ sourcePath: file.sourcePath,
49
+ symbolName: name,
50
+ sourceKind: kind,
51
+ members: memberRecords
52
+ });
53
+ const contractHash = hashSemanticValue({
54
+ kind: 'frontier.lang.syntheticPublicContractRegionHash',
55
+ language,
56
+ sourcePath: file.sourcePath,
57
+ key,
58
+ symbolName: name,
59
+ symbolKind: 'export',
60
+ apiSurfaceKind: 'module-export',
61
+ exportedName: name,
62
+ edgeKind: 'export',
63
+ signatureHash
64
+ });
65
+ return compactRecord({
66
+ id: `region_${idFragment(stageName)}_${idFragment(key)}`,
67
+ key,
68
+ regionKind: 'export',
69
+ granularity: 'symbol',
70
+ language,
71
+ sourcePath: file.sourcePath,
72
+ sourceHash: file.sourceHash,
73
+ symbolId: `symbol:${language}:export:${idFragment(name)}`,
74
+ symbolName: name,
75
+ symbolKind: 'export',
76
+ publicContract: true,
77
+ exportedName: name,
78
+ edgeKind: 'export',
79
+ apiSurfaceKind: 'module-export',
80
+ signatureHash,
81
+ contractHash,
82
+ metadata: {
83
+ source: 'js-ts-safe-project-merge-policy',
84
+ projectGraphStage: stageName,
85
+ sourceKind: kind,
86
+ memberKeys: memberRecords.map((member) => member.key)
87
+ }
88
+ });
89
+ }
90
+
91
+ function publicContractPolicyRegionsForPath(input, sourcePath) {
92
+ const policy = input.policyByPath?.[sourcePath]
93
+ ?? input.mergePolicyByPath?.[sourcePath]
94
+ ?? input.policy
95
+ ?? input.mergePolicy;
96
+ const regions = Array.isArray(policy)
97
+ ? policy
98
+ : policy?.unorderedRegions
99
+ ?? policy?.unorderedMemberRegions
100
+ ?? policy?.safeList
101
+ ?? policy?.safeMembers
102
+ ?? [];
103
+ return Array.isArray(regions) ? regions : [];
104
+ }
105
+
106
+ function isExportedContainer(sourceText, container) {
107
+ if (!container) return false;
108
+ return /^\s*export\b/.test(sourceText.slice(container.start, container.openStart));
109
+ }
110
+
111
+ function languageForPath(sourcePath) {
112
+ const path = String(sourcePath ?? '').toLowerCase();
113
+ if (path.endsWith('.js') || path.endsWith('.jsx') || path.endsWith('.mjs') || path.endsWith('.cjs')) return 'javascript';
114
+ return 'typescript';
115
+ }
116
+
117
+ export { augmentProjectSymbolGraphPublicContracts };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.145",
3
+ "version": "0.2.147",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",