@shapeshift-labs/frontier-lang-compiler 0.2.91 → 0.2.92

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 CHANGED
@@ -933,6 +933,7 @@ The published Frontier package family is generated from one shared package catal
933
933
  - [`@shapeshift-labs/frontier-realtime-server`](https://www.npmjs.com/package/@shapeshift-labs/frontier-realtime-server): Authoritative realtime room, tick, command validation, rate-limit, session, and snapshot-history runtime.
934
934
  - [`@shapeshift-labs/frontier-realtime-websocket`](https://www.npmjs.com/package/@shapeshift-labs/frontier-realtime-websocket): WebSocket client, wire, and Node room-server transport for Frontier realtime.
935
935
  - [`@shapeshift-labs/frontier-game`](https://www.npmjs.com/package/@shapeshift-labs/frontier-game): Game-facing entity, component, player, room, ownership, spatial interest, rollback, physics, and replication helpers above realtime.
936
+ - [`@shapeshift-labs/loom`](https://www.npmjs.com/package/@shapeshift-labs/loom): Repo-level semantic collaboration CLI for .loom workspaces, including init, scan, status, graph snapshots, projection plans, Frontier Lang delegation, Frontier Swarm delegation, and Frontier Framework delegation.
936
937
 
937
938
  Package source repositories:
938
939
 
@@ -1026,3 +1027,4 @@ Package source repositories:
1026
1027
  - [`siliconjungle/-shapeshift-labs-frontier-realtime-server`](https://github.com/siliconjungle/-shapeshift-labs-frontier-realtime-server)
1027
1028
  - [`siliconjungle/-shapeshift-labs-frontier-realtime-websocket`](https://github.com/siliconjungle/-shapeshift-labs-frontier-realtime-websocket)
1028
1029
  - [`siliconjungle/-shapeshift-labs-frontier-game`](https://github.com/siliconjungle/-shapeshift-labs-frontier-game)
1030
+ - [`siliconjungle/-shapeshift-labs-loom`](https://github.com/siliconjungle/-shapeshift-labs-loom)
@@ -191,6 +191,71 @@ export interface SemanticEditProjection {
191
191
  readonly metadata?: Record<string, unknown>;
192
192
  }
193
193
 
194
+ export type SemanticEditReplayStatus =
195
+ | 'accepted-clean'
196
+ | 'already-applied'
197
+ | 'conflict'
198
+ | 'stale'
199
+ | 'blocked'
200
+ | 'needs-port'
201
+ | 'evidence-only';
202
+
203
+ export interface SemanticEditReplayEdit {
204
+ readonly operationId?: string;
205
+ readonly semanticKey?: string;
206
+ readonly semanticIdentityHash?: string;
207
+ readonly sourceIdentityHash?: string;
208
+ readonly editContentHash?: string;
209
+ readonly sourcePath?: string;
210
+ readonly symbolName?: string;
211
+ readonly symbolKind?: string;
212
+ readonly status: 'applied' | 'already-applied' | 'conflict' | 'stale' | 'blocked' | string;
213
+ readonly start?: number;
214
+ readonly end?: number;
215
+ readonly replacementBytes?: number;
216
+ readonly replacementText?: string;
217
+ readonly reasonCodes: readonly string[];
218
+ }
219
+
220
+ export interface SemanticEditReplay {
221
+ readonly kind: 'frontier.lang.semanticEditReplay';
222
+ readonly version: 1;
223
+ readonly schema: 'frontier.lang.semanticEditReplay.v1';
224
+ readonly id: string;
225
+ readonly hash: string;
226
+ readonly projectionId?: string;
227
+ readonly scriptId?: string;
228
+ readonly sourcePath?: string;
229
+ readonly language?: FrontierSourceLanguage | string;
230
+ readonly currentHash?: string;
231
+ readonly projectedHash?: string;
232
+ readonly outputHash?: string;
233
+ readonly status: SemanticEditReplayStatus;
234
+ readonly edits: readonly SemanticEditReplayEdit[];
235
+ readonly appliedOperations: readonly string[];
236
+ readonly skippedOperations: readonly string[];
237
+ readonly admission: {
238
+ readonly status: SemanticEditReplayStatus;
239
+ readonly action: 'apply' | 'skip' | 'rerun-semantic-import' | 'human-review' | 'block' | string;
240
+ readonly reviewRequired: boolean;
241
+ readonly autoApplyCandidate: boolean;
242
+ readonly autoMergeClaim: false;
243
+ readonly semanticEquivalenceClaim: false;
244
+ readonly reasonCodes: readonly string[];
245
+ };
246
+ readonly outputSourceText?: string;
247
+ readonly summary: {
248
+ readonly edits: number;
249
+ readonly applied: number;
250
+ readonly alreadyApplied: number;
251
+ readonly conflicts: number;
252
+ readonly stale: number;
253
+ readonly blocked: number;
254
+ readonly reasonCodes: readonly string[];
255
+ };
256
+ readonly metadata?: Record<string, unknown>;
257
+ }
258
+
194
259
  export interface ProjectSemanticEditScriptToSourceOptions {
195
260
  readonly id?: string;
196
261
  readonly script: SemanticEditScript;
@@ -200,6 +265,17 @@ export interface ProjectSemanticEditScriptToSourceOptions {
200
265
  readonly metadata?: Record<string, unknown>;
201
266
  }
202
267
 
268
+ export interface ReplaySemanticEditProjectionOptions {
269
+ readonly id?: string;
270
+ readonly projection: SemanticEditProjection;
271
+ readonly currentSourceText: string;
272
+ readonly currentSourcePath?: string;
273
+ readonly currentSourceHash?: string;
274
+ readonly language?: FrontierSourceLanguage | string;
275
+ readonly parser?: string;
276
+ readonly metadata?: Record<string, unknown>;
277
+ }
278
+
203
279
  export interface CreateSemanticEditScriptOptions {
204
280
  readonly id?: string;
205
281
  readonly language?: FrontierSourceLanguage | string;
@@ -229,3 +305,4 @@ export interface CreateSemanticEditScriptOptions {
229
305
  export declare const SemanticEditScriptAdmissionStatuses: readonly SemanticEditScriptAdmissionStatus[];
230
306
  export declare function createSemanticEditScript(input?: CreateSemanticEditScriptOptions): SemanticEditScript;
231
307
  export declare function projectSemanticEditScriptToSource(input: ProjectSemanticEditScriptToSourceOptions): SemanticEditProjection;
308
+ export declare function replaySemanticEditProjection(input: ReplaySemanticEditProjectionOptions): SemanticEditReplay;
package/dist/index.js CHANGED
@@ -73,6 +73,7 @@ export { createSemanticMergeCandidateAdmissionRecord, decorateSemanticMergeCandi
73
73
  export { querySemanticMergeConflictClasses, SemanticMergeConflictClasses, semanticMergeConflictRiskScore, sortSemanticMergeCandidatesByConflictRisk, summarizeSemanticMergeConflicts } from './internal/index-impl/semanticMergeConflicts.js';
74
74
  export { createSemanticEditScript, SemanticEditScriptAdmissionStatuses } from './internal/index-impl/semanticEditScripts.js';
75
75
  export { projectSemanticEditScriptToSource } from './internal/index-impl/projectSemanticEditScriptToSource.js';
76
+ export { replaySemanticEditProjection } from './internal/index-impl/replaySemanticEditProjection.js';
76
77
  export { queryUniversalConversionPlan } from './internal/index-impl/queryUniversalConversionPlan.js';
77
78
  export { createSemanticAnchor, createSemanticLineageEvent, createSemanticLineageMap, querySemanticLineageEvents, SemanticLineageEventKinds } from './internal/index-impl/semanticLineageRecords.js';
78
79
  export { resolveSemanticLineage, resolveSemanticLineageBatch, SemanticLineageResolutionStatuses } from './internal/index-impl/semanticLineageResolutionRecords.js';
@@ -0,0 +1,191 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { idFragment, normalizeNativeLanguageId, uniqueStrings } from '../../native-import-utils.js';
3
+ import { createSemanticImportSidecar } from './createSemanticImportSidecar.js';
4
+ import { mapDiffSymbols } from './mapDiffSymbols.js';
5
+ import { normalizeNativeDiffImport } from './normalizeNativeDiffImport.js';
6
+
7
+ export function replaySemanticEditProjection(input = {}) {
8
+ const projection = input.projection ?? input.semanticEditProjection;
9
+ if (!projection) throw new Error('replaySemanticEditProjection requires a projection');
10
+ const currentSourceText = input.currentSourceText ?? input.headSourceText;
11
+ const sourcePath = input.currentSourcePath ?? input.headSourcePath ?? projection.sourcePath;
12
+ const language = normalizeNativeLanguageId(input.language ?? projection.language);
13
+ const reasonCodes = baseReasonCodes(projection, currentSourceText);
14
+ const currentHash = typeof currentSourceText === 'string' ? hashSemanticValue(currentSourceText) : undefined;
15
+ if (input.currentSourceHash && currentHash !== input.currentSourceHash) reasonCodes.push('current-source-hash-mismatch');
16
+ const currentSymbols = currentSourceText && isJavaScriptLike(language)
17
+ ? currentSymbolIndex({ currentSourceText, sourcePath, language, parser: input.parser })
18
+ : [];
19
+ const edits = projection.status === 'projected' && typeof currentSourceText === 'string'
20
+ ? (projection.edits ?? []).map((edit) => replayProjectionEdit(edit, { currentSourceText, currentSymbols }))
21
+ : [];
22
+ const status = replayStatus(reasonCodes, edits, projection);
23
+ const outputSourceText = replayOutputSource(status, currentSourceText, edits);
24
+ const core = {
25
+ kind: 'frontier.lang.semanticEditReplay',
26
+ version: 1,
27
+ schema: 'frontier.lang.semanticEditReplay.v1',
28
+ id: input.id ?? `semantic_edit_replay_${idFragment(projection.id ?? sourcePath ?? language ?? 'projection')}`,
29
+ projectionId: projection.id,
30
+ scriptId: projection.scriptId,
31
+ sourcePath,
32
+ language,
33
+ currentHash,
34
+ projectedHash: projection.projectedHash,
35
+ outputHash: outputSourceText === undefined ? undefined : hashSemanticValue(outputSourceText),
36
+ status,
37
+ edits,
38
+ appliedOperations: edits.filter((edit) => edit.status === 'applied').map((edit) => edit.operationId).filter(Boolean),
39
+ skippedOperations: edits.filter((edit) => edit.status !== 'applied').map((edit) => edit.operationId).filter(Boolean),
40
+ admission: replayAdmission(status, reasonCodes, edits),
41
+ outputSourceText,
42
+ summary: replaySummary(edits, reasonCodes),
43
+ metadata: compactRecord({
44
+ autoMergeClaim: false,
45
+ semanticEquivalenceClaim: false,
46
+ anchorMode: currentSymbols.length ? 'javascript-like-symbols' : 'offsets',
47
+ ...input.metadata
48
+ })
49
+ };
50
+ return { ...core, hash: hashSemanticValue(core) };
51
+ }
52
+
53
+ function replayProjectionEdit(edit, context) {
54
+ if (edit.status === 'already-applied') return replayEditRecord(edit, 'already-applied', undefined, ['projection-edit-already-applied']);
55
+ if (typeof edit.replacementText !== 'string') return replayEditRecord(edit, 'blocked', undefined, ['missing-replacement-text']);
56
+ const offset = checkRange(edit, { start: edit.headStart, end: edit.headEnd }, context.currentSourceText, 'head-offset');
57
+ if (offset) return replayEditRecord(edit, offset.status, offset.range, [offset.reason]);
58
+ const symbol = findCurrentSymbol(edit, context.currentSymbols);
59
+ const spanRange = spanOffsets(context.currentSourceText, symbol?.sourceSpan);
60
+ const anchored = checkRange(edit, spanRange, context.currentSourceText, 'current-symbol-anchor');
61
+ if (anchored) return replayEditRecord(edit, anchored.status, anchored.range, [anchored.reason, 'offset-reanchored-by-symbol']);
62
+ return replayEditRecord(edit, symbol ? 'conflict' : 'stale', spanRange, [
63
+ symbol ? 'current-symbol-anchor-content-mismatch' : 'current-symbol-anchor-missing'
64
+ ]);
65
+ }
66
+
67
+ function checkRange(edit, range, sourceText, label) {
68
+ if (!range || range.end < range.start) return undefined;
69
+ const current = sourceText.slice(range.start, range.end);
70
+ const currentHash = hashSemanticValue(current);
71
+ if (edit.deletedTextHash && currentHash === edit.deletedTextHash) return { status: 'applied', range, reason: `${label}-matches-deleted` };
72
+ if (edit.replacementTextHash && currentHash === edit.replacementTextHash) return { status: 'already-applied', range, reason: `${label}-matches-replacement` };
73
+ if (current === edit.replacementText) return { status: 'already-applied', range, reason: `${label}-matches-replacement-text` };
74
+ return undefined;
75
+ }
76
+
77
+ function replayEditRecord(edit, status, range, reasonCodes) {
78
+ return compactRecord({
79
+ operationId: edit.operationId,
80
+ semanticKey: edit.semanticKey,
81
+ semanticIdentityHash: edit.semanticIdentityHash,
82
+ sourceIdentityHash: edit.sourceIdentityHash,
83
+ editContentHash: edit.editContentHash,
84
+ sourcePath: edit.targetSourcePath ?? edit.sourcePath,
85
+ symbolName: edit.targetSymbolName ?? edit.symbolName,
86
+ symbolKind: edit.targetSymbolKind ?? edit.symbolKind,
87
+ status,
88
+ start: range?.start,
89
+ end: range?.end,
90
+ replacementBytes: edit.replacementBytes,
91
+ replacementText: edit.replacementText,
92
+ reasonCodes: reasonList(reasonCodes)
93
+ });
94
+ }
95
+
96
+ function currentSymbolIndex(input) {
97
+ const imported = normalizeNativeDiffImport({
98
+ language: input.language,
99
+ sourcePath: input.sourcePath,
100
+ sourceText: input.currentSourceText,
101
+ parser: input.parser
102
+ }, input, 'current');
103
+ return [...mapDiffSymbols(imported, createSemanticImportSidecar(imported)).values()];
104
+ }
105
+
106
+ function findCurrentSymbol(edit, symbols) {
107
+ const exact = symbols.find((symbol) => [symbol.ownershipKey, symbol.key, symbol.id].some((key) => key && [
108
+ edit.anchorKey,
109
+ edit.targetAnchorKey,
110
+ edit.symbolId
111
+ ].includes(key)));
112
+ if (exact) return exact;
113
+ const name = edit.targetSymbolName ?? edit.symbolName;
114
+ const kind = edit.targetSymbolKind ?? edit.symbolKind;
115
+ return symbols.find((symbol) => symbol.name === name && (!kind || symbol.kind === kind));
116
+ }
117
+
118
+ function replayStatus(reasonCodes, edits, projection) {
119
+ if (reasonCodes.some((reason) => reason !== 'current-source-hash-mismatch')) return 'blocked';
120
+ if (!edits.length && !(projection.edits ?? []).length) return 'evidence-only';
121
+ if (edits.some((edit) => edit.status === 'blocked')) return 'blocked';
122
+ if (edits.some((edit) => edit.status === 'conflict')) return 'conflict';
123
+ if (edits.some((edit) => edit.status === 'stale')) return 'stale';
124
+ if (edits.every((edit) => edit.status === 'already-applied')) return 'already-applied';
125
+ return edits.every((edit) => edit.status === 'applied' || edit.status === 'already-applied') ? 'accepted-clean' : 'needs-port';
126
+ }
127
+
128
+ function replayAdmission(status, reasonCodes, edits) {
129
+ const apply = status === 'accepted-clean';
130
+ return {
131
+ status,
132
+ action: apply ? 'apply' : status === 'already-applied' ? 'skip' : status === 'stale' ? 'rerun-semantic-import' : status === 'blocked' ? 'block' : 'human-review',
133
+ reviewRequired: !apply,
134
+ autoApplyCandidate: apply,
135
+ autoMergeClaim: false,
136
+ semanticEquivalenceClaim: false,
137
+ reasonCodes: reasonList([...reasonCodes, ...edits.flatMap((edit) => edit.reasonCodes ?? [])])
138
+ };
139
+ }
140
+
141
+ function replaySummary(edits, reasonCodes) {
142
+ return {
143
+ edits: edits.length,
144
+ applied: edits.filter((edit) => edit.status === 'applied').length,
145
+ alreadyApplied: edits.filter((edit) => edit.status === 'already-applied').length,
146
+ conflicts: edits.filter((edit) => edit.status === 'conflict').length,
147
+ stale: edits.filter((edit) => edit.status === 'stale').length,
148
+ blocked: edits.filter((edit) => edit.status === 'blocked').length,
149
+ reasonCodes: reasonList([...reasonCodes, ...edits.flatMap((edit) => edit.reasonCodes ?? [])])
150
+ };
151
+ }
152
+
153
+ function replayOutputSource(status, sourceText, edits) {
154
+ if (typeof sourceText !== 'string') return undefined;
155
+ if (status === 'already-applied') return sourceText;
156
+ if (status !== 'accepted-clean') return undefined;
157
+ return edits.filter((edit) => edit.status === 'applied')
158
+ .sort((left, right) => right.start - left.start)
159
+ .reduce((text, edit) => text.slice(0, edit.start) + editReplacement(edit, edits) + text.slice(edit.end), sourceText);
160
+ }
161
+
162
+ function editReplacement(edit, edits) {
163
+ return edits.find((candidate) => candidate.operationId === edit.operationId)?.replacementText ?? '';
164
+ }
165
+
166
+ function baseReasonCodes(projection, currentSourceText) {
167
+ return reasonList([
168
+ projection.status !== 'projected' ? 'projection-not-projected' : undefined,
169
+ projection.admission?.status !== 'auto-merge-candidate' ? 'projection-not-auto-merge-candidate' : undefined,
170
+ typeof currentSourceText !== 'string' ? 'missing-current-source-text' : undefined
171
+ ]);
172
+ }
173
+
174
+ function spanOffsets(sourceText, span) {
175
+ if (typeof sourceText !== 'string' || !span) return undefined;
176
+ if (typeof span.start === 'number' && typeof span.end === 'number' && span.end >= span.start) return { start: span.start, end: span.end };
177
+ if (typeof span.startLine !== 'number') return undefined;
178
+ const starts = [0];
179
+ for (let index = 0; index < sourceText.length; index += 1) if (sourceText[index] === '\n') starts.push(index + 1);
180
+ const startLine = Math.max(1, span.startLine);
181
+ const endLine = Math.max(startLine, typeof span.endLine === 'number' ? span.endLine : startLine);
182
+ const lineStart = starts[startLine - 1];
183
+ const endLineStart = starts[endLine - 1];
184
+ if (lineStart === undefined || endLineStart === undefined) return undefined;
185
+ const lineEnd = starts[endLine] === undefined ? sourceText.length : starts[endLine] - 1;
186
+ return { start: lineStart + Math.max(0, (span.startColumn ?? 1) - 1), end: endLineStart + (span.endColumn === undefined ? lineEnd - endLineStart : Math.max(0, span.endColumn - 1)) };
187
+ }
188
+
189
+ function isJavaScriptLike(language) { return language === 'javascript' || language === 'typescript'; }
190
+ function reasonList(values) { return uniqueStrings((values ?? []).filter(Boolean)); }
191
+ function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.91",
3
+ "version": "0.2.92",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",