@shapeshift-labs/frontier-lang-compiler 0.2.39 → 0.2.41
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 +49 -1
- package/bench/smoke.mjs +26 -1
- package/dist/index.d.ts +182 -0
- package/dist/index.js +861 -0
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Compiler facade for Frontier Lang. It composes the parser, checker, semantic kernel, and projection adapters for TypeScript, JavaScript, Rust, Python, and C.
|
|
4
4
|
|
|
5
|
+
## Vision
|
|
6
|
+
|
|
7
|
+
Frontier Lang and Frontier Swarm are two parts of the same system: a semantic programming substrate for agent teams.
|
|
8
|
+
|
|
9
|
+
Frontier Lang is the universal code representation. It imports source from native languages into a replayable semantic graph: AST layers, symbols, ownership regions, source maps, effects, proof obligations, runtime assumptions, tests, traces, and merge history. It preserves exact native source where needed, imports parser/compiler facts where available, and projects semantic programs back out through target-language adapters.
|
|
10
|
+
|
|
11
|
+
Frontier Swarm is the coordination layer for many agents working on that graph. It breaks large engineering goals into owned semantic regions, assigns workers isolated slices of context, collects machine-readable evidence, scores merge readiness, and lets the coordinator integrate patches without reading every worker transcript manually.
|
|
12
|
+
|
|
13
|
+
The shared goal is semantic merging for code. A worker output should say what it changed, what semantic region it owns, what source hashes it depended on, what tests or traces prove the change, what assumptions it conflicts with, and whether it is ready to merge, needs porting, or is discovery-only.
|
|
14
|
+
|
|
5
15
|
```js
|
|
6
16
|
import { compileFrontierSource } from '@shapeshift-labs/frontier-lang-compiler';
|
|
7
17
|
|
|
@@ -254,6 +264,44 @@ console.log(changeSet.mergeCandidate.readiness); // merge-admission classificati
|
|
|
254
264
|
|
|
255
265
|
Use `diffNativeSourceImports` when the worker or runner already produced `importNativeSource` results. Changed regions include a `metadata.changedRegionProjection` envelope with before/after source hashes, source-map links, ownership keys, readiness, and `autoMergeClaim: false` so swarm admission tools can score or port patches without treating semantic metadata as proof. Body-only edits that the lightweight scanner cannot anchor to a symbol are still reported as file-level changed regions instead of being silently treated as safe.
|
|
256
266
|
|
|
267
|
+
Extract a surgical semantic slice when a worker only needs one symbol, region, native AST node, or source path:
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
import {
|
|
271
|
+
createSemanticSlice,
|
|
272
|
+
importNativeSource,
|
|
273
|
+
testSemanticSlice,
|
|
274
|
+
writeSemanticSliceJson
|
|
275
|
+
} from '@shapeshift-labs/frontier-lang-compiler';
|
|
276
|
+
|
|
277
|
+
const sourceText = 'export function parseExpression(input) { return input; }\n';
|
|
278
|
+
const imported = importNativeSource({
|
|
279
|
+
language: 'typescript',
|
|
280
|
+
sourcePath: 'src/parser.ts',
|
|
281
|
+
sourceText
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const slice = createSemanticSlice(imported, {
|
|
285
|
+
entryRefs: ['symbol:parseExpression'],
|
|
286
|
+
includeDependencies: true,
|
|
287
|
+
focusedCommands: ['npm test -- parser-expression'],
|
|
288
|
+
fixtureHints: ['operator precedence corpus']
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
console.log(slice.kind); // "frontier.lang.semanticSlice"
|
|
292
|
+
console.log(slice.mergeAdmission.conflictKeys); // semantic ownership keys
|
|
293
|
+
console.log(slice.sourceFiles[0].sourceHash); // stale-check input for admission
|
|
294
|
+
|
|
295
|
+
const gate = testSemanticSlice(slice, {
|
|
296
|
+
currentSources: { 'src/parser.ts': sourceText }
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
console.log(gate.status); // "passed", "needs-review", or "failed"
|
|
300
|
+
console.log(writeSemanticSliceJson(slice)); // stable JSON for worker inputs
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
A semantic slice is the small unit a swarm can hand to a worker instead of copying a full repository. It carries the selected symbols, ownership regions, native nodes, relations, occurrences, source-map links, source spans, source excerpts, source hashes, focused verification commands, fixture hints, and merge-admission metadata. It does not claim the patch is correct; it makes the context and conflicts machine-readable so admission scoring can combine changed ownership, focused test status, stale/source-hash checks, evidence, and semantic risk in one sortable record.
|
|
304
|
+
|
|
257
305
|
Compile native source imports through the same reader/IR/writer facade that swarms use for sidecar evidence. Same-language targets preserve exact source when hashes match; cross-language targets emit declaration stubs until a real adapter provides stronger evidence:
|
|
258
306
|
|
|
259
307
|
```js
|
|
@@ -496,7 +544,7 @@ The published Frontier package family is generated from one shared package catal
|
|
|
496
544
|
- [`@shapeshift-labs/frontier-lang-csharp`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang-csharp): C# Roslyn source-language importer package for Frontier Lang semantic documents, including package-level metadata, Roslyn adapter helpers, native import results, and semantic sidecar generation for SyntaxTree/SyntaxNode-shaped ASTs.
|
|
497
545
|
- [`@shapeshift-labs/frontier-lang-clang`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang-clang): Clang AST source-language importer package for Frontier Lang semantic documents, including package-level metadata, Clang AST JSON adapter helpers, native import results, and semantic sidecar generation for C/C++ translation units.
|
|
498
546
|
- [`@shapeshift-labs/frontier-lang-cli`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang-cli): Command line interface for parsing, checking, hashing, and emitting Frontier Lang projects.
|
|
499
|
-
- [`@shapeshift-labs/frontier-lang`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang): Umbrella package for Frontier Lang kernel, parser, checker, and projection adapters.
|
|
547
|
+
- [`@shapeshift-labs/frontier-lang`](https://www.npmjs.com/package/@shapeshift-labs/frontier-lang): Umbrella package for Frontier Lang kernel, parser, checker, compiler facade, universal AST helpers, and projection adapters.
|
|
500
548
|
- [`@shapeshift-labs/frontier-kv`](https://www.npmjs.com/package/@shapeshift-labs/frontier-kv): Serializable in-memory key/value state for Frontier apps, including TTL, versioned compare-and-set, batched patch mutations, scans, watchers, snapshots, JSONL event evidence, and replay verification.
|
|
501
549
|
- [`@shapeshift-labs/frontier-kv-locks`](https://www.npmjs.com/package/@shapeshift-labs/frontier-kv-locks): Lease-style lock records on top of Frontier KV, including acquire, renew, release, fencing tokens, expiration, owner evidence, and replayable lock events.
|
|
502
550
|
- [`@shapeshift-labs/frontier-kv-rate-limit`](https://www.npmjs.com/package/@shapeshift-labs/frontier-kv-rate-limit): Patch-native rate limit buckets for Frontier KV, including fixed windows, sliding windows, token buckets, deterministic refill, consume evidence, and reset records.
|
package/bench/smoke.mjs
CHANGED
|
@@ -18,13 +18,15 @@ import {
|
|
|
18
18
|
createRustSynNativeImporterAdapter,
|
|
19
19
|
createSwiftSyntaxNativeImporterAdapter,
|
|
20
20
|
createSemanticImportSidecar,
|
|
21
|
+
createSemanticSlice,
|
|
21
22
|
diffNativeSources,
|
|
22
23
|
importExternalSemanticIndex,
|
|
23
24
|
importNativeSource,
|
|
24
25
|
projectNativeImportToSource,
|
|
25
26
|
queryNativeParserFeatureMatrix,
|
|
26
27
|
runNativeImporterAdapter,
|
|
27
|
-
summarizeNativeImportFeatureEvidence
|
|
28
|
+
summarizeNativeImportFeatureEvidence,
|
|
29
|
+
testSemanticSlice
|
|
28
30
|
} from '../dist/index.js';
|
|
29
31
|
|
|
30
32
|
const source = `
|
|
@@ -163,6 +165,23 @@ const semanticSidecars = nativeImportResults.map((imported) => createSemanticImp
|
|
|
163
165
|
const sidecarDurationMs = performance.now() - sidecarStart;
|
|
164
166
|
const sidecarOwnershipRegions = semanticSidecars.reduce((sum, sidecar) => sum + sidecar.ownershipRegions.length, 0);
|
|
165
167
|
|
|
168
|
+
const sliceStart = performance.now();
|
|
169
|
+
const semanticSlices = nativeImportResults.slice(0, 100).map((imported, index) => {
|
|
170
|
+
const symbol = imported.semanticIndex?.symbols?.[0]?.name ?? imported.semanticIndex?.symbols?.[0]?.id;
|
|
171
|
+
return createSemanticSlice(imported, {
|
|
172
|
+
entryRefs: symbol ? [`symbol:${symbol}`] : [],
|
|
173
|
+
includeDependencies: index % 2 === 0,
|
|
174
|
+
focusedCommands: [`npm test -- semantic-slice-${index}`]
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
const sliceDurationMs = performance.now() - sliceStart;
|
|
178
|
+
const sliceSourceMapLinks = semanticSlices.reduce((sum, slice) => sum + slice.sourceMapLinks.length, 0);
|
|
179
|
+
const sliceConflictKeys = semanticSlices.reduce((sum, slice) => sum + slice.mergeAdmission.conflictKeys.length, 0);
|
|
180
|
+
const sliceGateStart = performance.now();
|
|
181
|
+
const semanticSliceGates = semanticSlices.map((slice) => testSemanticSlice(slice, { requireSourceMapLinks: false }));
|
|
182
|
+
const sliceGateDurationMs = performance.now() - sliceGateStart;
|
|
183
|
+
const sliceGateFailures = semanticSliceGates.filter((gate) => gate.status === 'failed').length;
|
|
184
|
+
|
|
166
185
|
const featureEvidenceStart = performance.now();
|
|
167
186
|
const featureEvidenceSummaries = nativeImportResults.map((imported) => summarizeNativeImportFeatureEvidence(imported.losses, {
|
|
168
187
|
evidence: imported.evidence
|
|
@@ -324,6 +343,12 @@ console.log(JSON.stringify({
|
|
|
324
343
|
semanticSidecars: semanticSidecars.length,
|
|
325
344
|
sidecarOwnershipRegions,
|
|
326
345
|
sidecarDurationMs: Number(sidecarDurationMs.toFixed(2)),
|
|
346
|
+
semanticSlices: semanticSlices.length,
|
|
347
|
+
sliceDurationMs: Number(sliceDurationMs.toFixed(2)),
|
|
348
|
+
sliceGateDurationMs: Number(sliceGateDurationMs.toFixed(2)),
|
|
349
|
+
sliceSourceMapLinks,
|
|
350
|
+
sliceConflictKeys,
|
|
351
|
+
sliceGateFailures,
|
|
327
352
|
featureEvidencePolicyMatches,
|
|
328
353
|
featureEvidenceDurationMs: Number(featureEvidenceDurationMs.toFixed(2)),
|
|
329
354
|
nativeProjections: nativeProjections.length,
|
package/dist/index.d.ts
CHANGED
|
@@ -1424,6 +1424,184 @@ export interface NativeSourceChangeSet {
|
|
|
1424
1424
|
};
|
|
1425
1425
|
}
|
|
1426
1426
|
|
|
1427
|
+
export type SemanticSliceInput =
|
|
1428
|
+
| NativeSourceImportResult
|
|
1429
|
+
| NativeProjectImportResult
|
|
1430
|
+
| ImportNativeSourceOptions
|
|
1431
|
+
| FrontierUniversalAstEnvelope
|
|
1432
|
+
| SemanticImportSidecar
|
|
1433
|
+
| {
|
|
1434
|
+
readonly import?: NativeSourceImportResult | NativeProjectImportResult | ImportNativeSourceOptions;
|
|
1435
|
+
readonly importResult?: NativeSourceImportResult | NativeProjectImportResult | ImportNativeSourceOptions;
|
|
1436
|
+
readonly universalAst?: FrontierUniversalAstEnvelope;
|
|
1437
|
+
readonly sidecar?: SemanticImportSidecar;
|
|
1438
|
+
readonly source?: ImportNativeSourceOptions;
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
export interface CreateSemanticSliceOptions {
|
|
1442
|
+
readonly id?: string;
|
|
1443
|
+
readonly generatedAt?: number;
|
|
1444
|
+
readonly language?: FrontierSourceLanguage | string;
|
|
1445
|
+
readonly sourcePath?: string;
|
|
1446
|
+
readonly sourceHash?: string;
|
|
1447
|
+
readonly sourceText?: string;
|
|
1448
|
+
readonly symbol?: string;
|
|
1449
|
+
readonly region?: string;
|
|
1450
|
+
readonly nativeNodeId?: string;
|
|
1451
|
+
readonly entryRefs?: readonly string[] | string;
|
|
1452
|
+
readonly semanticRefs?: readonly string[] | string;
|
|
1453
|
+
readonly refs?: readonly string[] | string;
|
|
1454
|
+
readonly includeDependencies?: boolean;
|
|
1455
|
+
readonly maxDependencyDepth?: number;
|
|
1456
|
+
readonly includeSourceText?: boolean;
|
|
1457
|
+
readonly maxExcerptBytes?: number;
|
|
1458
|
+
readonly focusedCommands?: readonly string[] | string;
|
|
1459
|
+
readonly fixtureHints?: readonly string[] | string;
|
|
1460
|
+
readonly regionPrefix?: string;
|
|
1461
|
+
readonly evidenceId?: string;
|
|
1462
|
+
readonly mergeCandidateId?: string;
|
|
1463
|
+
readonly sidecar?: SemanticImportSidecar;
|
|
1464
|
+
readonly metadata?: Record<string, unknown>;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
export interface SemanticSliceSourceMapLink {
|
|
1468
|
+
readonly id?: string;
|
|
1469
|
+
readonly sourceMapId?: string;
|
|
1470
|
+
readonly sourcePath?: string;
|
|
1471
|
+
readonly sourceHash?: string;
|
|
1472
|
+
readonly targetPath?: string;
|
|
1473
|
+
readonly targetHash?: string;
|
|
1474
|
+
readonly semanticSymbolId?: string;
|
|
1475
|
+
readonly semanticOccurrenceId?: string;
|
|
1476
|
+
readonly semanticNodeId?: string;
|
|
1477
|
+
readonly nativeSourceId?: string;
|
|
1478
|
+
readonly nativeAstNodeId?: string;
|
|
1479
|
+
readonly precision?: string;
|
|
1480
|
+
readonly sourceSpan?: SourceSpan;
|
|
1481
|
+
readonly generatedSpan?: SourceMapMappingRecord['generatedSpan'];
|
|
1482
|
+
readonly ownershipRegionId?: string;
|
|
1483
|
+
readonly ownershipRegionKey?: string;
|
|
1484
|
+
readonly ownershipRegionKind?: NativeImportRegionTaxonomyKind;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
export interface SemanticSliceSourceFile {
|
|
1488
|
+
readonly path?: string;
|
|
1489
|
+
readonly sourceHash?: string;
|
|
1490
|
+
readonly spans: readonly SourceSpan[];
|
|
1491
|
+
readonly spanCount: number;
|
|
1492
|
+
readonly excerptCount: number;
|
|
1493
|
+
readonly sourceTextAvailable: boolean;
|
|
1494
|
+
readonly excerpts: readonly {
|
|
1495
|
+
readonly span: SourceSpan;
|
|
1496
|
+
readonly text: string;
|
|
1497
|
+
readonly textHash: string;
|
|
1498
|
+
readonly truncated: boolean;
|
|
1499
|
+
}[];
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
export interface SemanticSliceExpectedAssertion {
|
|
1503
|
+
readonly id: string;
|
|
1504
|
+
readonly expected: boolean;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
export interface SemanticSlice {
|
|
1508
|
+
readonly kind: 'frontier.lang.semanticSlice';
|
|
1509
|
+
readonly version: 1;
|
|
1510
|
+
readonly id: string;
|
|
1511
|
+
readonly generatedAt: number;
|
|
1512
|
+
readonly language?: FrontierSourceLanguage | string;
|
|
1513
|
+
readonly sourcePath?: string;
|
|
1514
|
+
readonly importId?: string;
|
|
1515
|
+
readonly universalAstId?: string;
|
|
1516
|
+
readonly semanticIndexId?: string;
|
|
1517
|
+
readonly sidecarId?: string;
|
|
1518
|
+
readonly entryRefs: readonly string[];
|
|
1519
|
+
readonly matchedEntryRefs: readonly string[];
|
|
1520
|
+
readonly unresolvedEntryRefs: readonly string[];
|
|
1521
|
+
readonly symbols: readonly (SemanticIndexRecord['symbols'][number] | SemanticImportSidecarSymbol)[];
|
|
1522
|
+
readonly ownershipRegions: readonly SemanticImportOwnershipRegion[];
|
|
1523
|
+
readonly nativeNodes: readonly NativeAstNode[];
|
|
1524
|
+
readonly relations: readonly SemanticIndexRecord['relations'][number][];
|
|
1525
|
+
readonly occurrences: readonly SemanticIndexRecord['occurrences'][number][];
|
|
1526
|
+
readonly sourceMapLinks: readonly SemanticSliceSourceMapLink[];
|
|
1527
|
+
readonly sourceSpans: readonly SourceSpan[];
|
|
1528
|
+
readonly sourceFiles: readonly SemanticSliceSourceFile[];
|
|
1529
|
+
readonly losses: readonly NativeAstLossRecord[];
|
|
1530
|
+
readonly evidence: readonly EvidenceRecord[];
|
|
1531
|
+
readonly mergeCandidate: SemanticMergeCandidateRecord;
|
|
1532
|
+
readonly verification: {
|
|
1533
|
+
readonly focusedCommands: readonly string[];
|
|
1534
|
+
readonly fixtureHints: readonly string[];
|
|
1535
|
+
readonly expectedAssertions: readonly SemanticSliceExpectedAssertion[];
|
|
1536
|
+
};
|
|
1537
|
+
readonly summary: {
|
|
1538
|
+
readonly symbols: number;
|
|
1539
|
+
readonly ownershipRegions: number;
|
|
1540
|
+
readonly nativeNodes: number;
|
|
1541
|
+
readonly relations: number;
|
|
1542
|
+
readonly occurrences: number;
|
|
1543
|
+
readonly sourceMapLinks: number;
|
|
1544
|
+
readonly sourceFiles: number;
|
|
1545
|
+
readonly losses: number;
|
|
1546
|
+
readonly conflictKeys: number;
|
|
1547
|
+
readonly readiness: SemanticMergeReadiness;
|
|
1548
|
+
readonly unresolvedEntryRefs: number;
|
|
1549
|
+
readonly sourceTextAvailable: boolean;
|
|
1550
|
+
};
|
|
1551
|
+
readonly mergeAdmission: {
|
|
1552
|
+
readonly autoMergeClaim: false;
|
|
1553
|
+
readonly reviewRequired: boolean;
|
|
1554
|
+
readonly readiness: SemanticMergeReadiness;
|
|
1555
|
+
readonly reasons: readonly string[];
|
|
1556
|
+
readonly conflictKeys: readonly string[];
|
|
1557
|
+
readonly ownershipKeys: readonly string[];
|
|
1558
|
+
readonly sourceHashes: readonly { readonly path?: string; readonly sourceHash?: string }[];
|
|
1559
|
+
readonly staleCheck: {
|
|
1560
|
+
readonly mode: 'source-hash' | string;
|
|
1561
|
+
readonly requiresCurrentSource: boolean;
|
|
1562
|
+
readonly sourceFiles: number;
|
|
1563
|
+
};
|
|
1564
|
+
};
|
|
1565
|
+
readonly metadata?: Record<string, unknown>;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
export interface TestSemanticSliceOptions {
|
|
1569
|
+
readonly id?: string;
|
|
1570
|
+
readonly generatedAt?: number;
|
|
1571
|
+
readonly requireSourceMapLinks?: boolean;
|
|
1572
|
+
readonly currentSources?: Readonly<Record<string, string>> | ReadonlyMap<string, string>;
|
|
1573
|
+
readonly metadata?: Record<string, unknown>;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
export interface SemanticSliceTestAssertion {
|
|
1577
|
+
readonly id: string;
|
|
1578
|
+
readonly status: 'passed' | 'warning' | 'failed';
|
|
1579
|
+
readonly summary: string;
|
|
1580
|
+
readonly metadata?: Record<string, unknown>;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
export interface SemanticSliceTestResult {
|
|
1584
|
+
readonly kind: 'frontier.lang.semanticSliceTestResult';
|
|
1585
|
+
readonly version: 1;
|
|
1586
|
+
readonly id: string;
|
|
1587
|
+
readonly generatedAt: number;
|
|
1588
|
+
readonly sliceId?: string;
|
|
1589
|
+
readonly status: 'passed' | 'needs-review' | 'failed';
|
|
1590
|
+
readonly readiness: SemanticMergeReadiness;
|
|
1591
|
+
readonly assertions: readonly SemanticSliceTestAssertion[];
|
|
1592
|
+
readonly summary: {
|
|
1593
|
+
readonly assertions: number;
|
|
1594
|
+
readonly passed: number;
|
|
1595
|
+
readonly warnings: number;
|
|
1596
|
+
readonly failed: number;
|
|
1597
|
+
readonly sourceHashChecks: number;
|
|
1598
|
+
readonly symbols: number;
|
|
1599
|
+
readonly ownershipRegions: number;
|
|
1600
|
+
readonly sourceMapLinks: number;
|
|
1601
|
+
};
|
|
1602
|
+
readonly metadata?: Record<string, unknown>;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1427
1605
|
export type NativeImporterAdapterExactness =
|
|
1428
1606
|
| 'exact-parser-ast'
|
|
1429
1607
|
| 'parser-tree'
|
|
@@ -2371,6 +2549,10 @@ export declare function importExternalSemanticIndex(input: ImportExternalSemanti
|
|
|
2371
2549
|
export declare function importNativeSource(input: ImportNativeSourceOptions): NativeSourceImportResult;
|
|
2372
2550
|
export declare function diffNativeSources(input: DiffNativeSourcesOptions): NativeSourceChangeSet;
|
|
2373
2551
|
export declare function diffNativeSourceImports(input: DiffNativeSourceImportsOptions): NativeSourceChangeSet;
|
|
2552
|
+
export declare function createSemanticSlice(input: SemanticSliceInput, options?: CreateSemanticSliceOptions): SemanticSlice;
|
|
2553
|
+
export declare function testSemanticSlice(slice: SemanticSlice, options?: TestSemanticSliceOptions): SemanticSliceTestResult;
|
|
2554
|
+
export declare function readSemanticSliceJson(source: string): SemanticSlice;
|
|
2555
|
+
export declare function writeSemanticSliceJson(slice: SemanticSlice): string;
|
|
2374
2556
|
export declare function importNativeProject(input: ImportNativeProjectOptions): Promise<NativeProjectImportResult>;
|
|
2375
2557
|
export declare function createUniversalAstFromDocument(document: FrontierLangDocument, input?: {
|
|
2376
2558
|
readonly id?: string;
|
package/dist/index.js
CHANGED
|
@@ -4211,6 +4211,867 @@ export function diffNativeSourceImports(input) {
|
|
|
4211
4211
|
};
|
|
4212
4212
|
}
|
|
4213
4213
|
|
|
4214
|
+
export function createSemanticSlice(input, options = {}) {
|
|
4215
|
+
const context = semanticSliceContext(input, options);
|
|
4216
|
+
const sidecar = context.sidecar ?? (context.importResult ? createSemanticImportSidecar(context.importResult, {
|
|
4217
|
+
generatedAt: options.generatedAt,
|
|
4218
|
+
regionPrefix: options.regionPrefix
|
|
4219
|
+
}) : undefined);
|
|
4220
|
+
const records = semanticSliceRecords(context, sidecar);
|
|
4221
|
+
const entryRefs = uniqueStrings([
|
|
4222
|
+
...readStringArray(options.entryRefs),
|
|
4223
|
+
...readStringArray(options.semanticRefs),
|
|
4224
|
+
...readStringArray(options.refs),
|
|
4225
|
+
...(options.symbol ? [`symbol:${options.symbol}`] : []),
|
|
4226
|
+
...(options.region ? [`region:${options.region}`] : []),
|
|
4227
|
+
...(options.nativeNodeId ? [`native:${options.nativeNodeId}`] : []),
|
|
4228
|
+
...(options.sourcePath ? [`path:${options.sourcePath}`] : [])
|
|
4229
|
+
]);
|
|
4230
|
+
const selection = selectSemanticSliceRecords(records, {
|
|
4231
|
+
entryRefs,
|
|
4232
|
+
includeDependencies: options.includeDependencies !== false,
|
|
4233
|
+
maxDependencyDepth: Number.isFinite(options.maxDependencyDepth) ? Math.max(0, Math.floor(options.maxDependencyDepth)) : 2
|
|
4234
|
+
});
|
|
4235
|
+
const sourceSpans = semanticSliceSourceSpans(selection);
|
|
4236
|
+
const sourceFiles = semanticSliceSourceFiles(sourceSpans, context, options);
|
|
4237
|
+
const sourceMapLinks = semanticSliceSourceMapLinks(selection);
|
|
4238
|
+
const conflictKeys = uniqueStrings([
|
|
4239
|
+
...selection.regions.map((region) => region.conflictKey),
|
|
4240
|
+
...selection.regions.map((region) => region.key ? `region:${region.key}` : region.id ? `region:${region.id}` : undefined),
|
|
4241
|
+
...selection.symbols.map((symbol) => symbol.metadata?.ownershipRegionKey ? `region:${symbol.metadata.ownershipRegionKey}` : undefined),
|
|
4242
|
+
...selection.symbols.map((symbol) => symbol.id ? `symbol:${symbol.id}` : undefined),
|
|
4243
|
+
...sourceFiles.map((file) => file.path ? `source:${file.path}` : undefined)
|
|
4244
|
+
].filter(Boolean));
|
|
4245
|
+
const unresolvedEntryRefs = entryRefs.filter((entryRef) => !selection.matchedEntryRefs.includes(entryRef));
|
|
4246
|
+
const readiness = semanticSliceReadiness(context, selection, unresolvedEntryRefs);
|
|
4247
|
+
const reasons = semanticSliceReasons(context, selection, unresolvedEntryRefs, readiness);
|
|
4248
|
+
const idPart = idFragment(options.id ?? entryRefs.join('_') ?? context.sourcePath ?? context.language ?? context.importResult?.id ?? context.universalAst?.id ?? 'semantic_slice');
|
|
4249
|
+
const evidence = [{
|
|
4250
|
+
id: options.evidenceId ?? `evidence_${idPart}_semantic_slice`,
|
|
4251
|
+
kind: 'semantic-slice',
|
|
4252
|
+
status: readiness === 'blocked' ? 'failed' : 'passed',
|
|
4253
|
+
path: context.sourcePath,
|
|
4254
|
+
summary: `Created semantic slice with ${selection.symbols.length} symbol(s), ${selection.regions.length} ownership region(s), and ${sourceMapLinks.length} source-map link(s).`,
|
|
4255
|
+
metadata: {
|
|
4256
|
+
entryRefs,
|
|
4257
|
+
unresolvedEntryRefs,
|
|
4258
|
+
importId: context.importResult?.id,
|
|
4259
|
+
universalAstId: context.universalAst?.id,
|
|
4260
|
+
semanticIndexId: context.semanticIndex?.id,
|
|
4261
|
+
sidecarId: sidecar?.id
|
|
4262
|
+
}
|
|
4263
|
+
}];
|
|
4264
|
+
const mergeCandidate = createSemanticMergeCandidateRecord({
|
|
4265
|
+
id: options.mergeCandidateId ?? `merge_candidate_${idPart}_semantic_slice`,
|
|
4266
|
+
importResultId: context.importResult?.id,
|
|
4267
|
+
language: context.language,
|
|
4268
|
+
sourcePath: context.sourcePath,
|
|
4269
|
+
touchedSymbols: selection.symbols.map(semanticSliceTouchedSymbol),
|
|
4270
|
+
touchedSemanticNodes: [],
|
|
4271
|
+
nativeSpans: sourceSpans,
|
|
4272
|
+
conflictKeys,
|
|
4273
|
+
readiness,
|
|
4274
|
+
reasons,
|
|
4275
|
+
evidence,
|
|
4276
|
+
metadata: {
|
|
4277
|
+
kind: 'semantic-slice',
|
|
4278
|
+
sidecarId: sidecar?.id,
|
|
4279
|
+
sourceMapLinks: sourceMapLinks.length,
|
|
4280
|
+
nativeNodeIds: selection.nativeNodes.map((node) => node.id),
|
|
4281
|
+
dependencyRelationIds: selection.relations.map((relation) => relation.id).filter(Boolean),
|
|
4282
|
+
autoMergeClaim: false
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
4285
|
+
return {
|
|
4286
|
+
kind: 'frontier.lang.semanticSlice',
|
|
4287
|
+
version: 1,
|
|
4288
|
+
id: options.id ?? `semantic_slice_${idPart}`,
|
|
4289
|
+
generatedAt: options.generatedAt ?? Date.now(),
|
|
4290
|
+
language: context.language,
|
|
4291
|
+
sourcePath: context.sourcePath,
|
|
4292
|
+
importId: context.importResult?.id,
|
|
4293
|
+
universalAstId: context.universalAst?.id,
|
|
4294
|
+
semanticIndexId: context.semanticIndex?.id,
|
|
4295
|
+
sidecarId: sidecar?.id,
|
|
4296
|
+
entryRefs,
|
|
4297
|
+
matchedEntryRefs: selection.matchedEntryRefs,
|
|
4298
|
+
unresolvedEntryRefs,
|
|
4299
|
+
symbols: selection.symbols,
|
|
4300
|
+
ownershipRegions: selection.regions,
|
|
4301
|
+
nativeNodes: selection.nativeNodes,
|
|
4302
|
+
relations: selection.relations,
|
|
4303
|
+
occurrences: selection.occurrences,
|
|
4304
|
+
sourceMapLinks,
|
|
4305
|
+
sourceSpans,
|
|
4306
|
+
sourceFiles,
|
|
4307
|
+
losses: selection.losses,
|
|
4308
|
+
evidence,
|
|
4309
|
+
mergeCandidate,
|
|
4310
|
+
verification: {
|
|
4311
|
+
focusedCommands: readStringArray(options.focusedCommands),
|
|
4312
|
+
fixtureHints: readStringArray(options.fixtureHints),
|
|
4313
|
+
expectedAssertions: semanticSliceExpectedAssertions(selection, unresolvedEntryRefs)
|
|
4314
|
+
},
|
|
4315
|
+
summary: {
|
|
4316
|
+
symbols: selection.symbols.length,
|
|
4317
|
+
ownershipRegions: selection.regions.length,
|
|
4318
|
+
nativeNodes: selection.nativeNodes.length,
|
|
4319
|
+
relations: selection.relations.length,
|
|
4320
|
+
occurrences: selection.occurrences.length,
|
|
4321
|
+
sourceMapLinks: sourceMapLinks.length,
|
|
4322
|
+
sourceFiles: sourceFiles.length,
|
|
4323
|
+
losses: selection.losses.length,
|
|
4324
|
+
conflictKeys: conflictKeys.length,
|
|
4325
|
+
readiness,
|
|
4326
|
+
unresolvedEntryRefs: unresolvedEntryRefs.length,
|
|
4327
|
+
sourceTextAvailable: sourceFiles.some((file) => file.excerptCount > 0)
|
|
4328
|
+
},
|
|
4329
|
+
mergeAdmission: {
|
|
4330
|
+
autoMergeClaim: false,
|
|
4331
|
+
reviewRequired: readiness !== 'ready',
|
|
4332
|
+
readiness,
|
|
4333
|
+
reasons,
|
|
4334
|
+
conflictKeys,
|
|
4335
|
+
ownershipKeys: uniqueStrings(selection.regions.map((region) => region.key).filter(Boolean)),
|
|
4336
|
+
sourceHashes: sourceFiles.map((file) => ({ path: file.path, sourceHash: file.sourceHash })).filter((file) => file.path || file.sourceHash),
|
|
4337
|
+
staleCheck: {
|
|
4338
|
+
mode: 'source-hash',
|
|
4339
|
+
requiresCurrentSource: sourceFiles.some((file) => file.sourceHash),
|
|
4340
|
+
sourceFiles: sourceFiles.length
|
|
4341
|
+
}
|
|
4342
|
+
},
|
|
4343
|
+
metadata: {
|
|
4344
|
+
note: 'Semantic slices are focused source-addressable evidence for agent isolation and merge admission; they are not correctness proofs.',
|
|
4345
|
+
...options.metadata
|
|
4346
|
+
}
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4350
|
+
export function testSemanticSlice(slice, options = {}) {
|
|
4351
|
+
const assertions = [];
|
|
4352
|
+
assertions.push(semanticSliceAssertion('kind', slice?.kind === 'frontier.lang.semanticSlice', 'Input is a Frontier semantic slice.'));
|
|
4353
|
+
assertions.push(semanticSliceAssertion('entryRefsResolved', (slice?.unresolvedEntryRefs?.length ?? 0) === 0, 'All requested semantic entry refs resolved.', {
|
|
4354
|
+
unresolvedEntryRefs: slice?.unresolvedEntryRefs ?? []
|
|
4355
|
+
}));
|
|
4356
|
+
assertions.push(semanticSliceAssertion('nonEmptySelection', (slice?.symbols?.length ?? 0) + (slice?.ownershipRegions?.length ?? 0) + (slice?.nativeNodes?.length ?? 0) > 0, 'Slice selected at least one symbol, ownership region, or native node.'));
|
|
4357
|
+
assertions.push(semanticSliceAssertion('sourceMapLinks', options.requireSourceMapLinks === false || (slice?.sourceMapLinks?.length ?? 0) > 0, 'Slice has source-map links or the requirement was disabled.'));
|
|
4358
|
+
assertions.push(semanticSliceAssertion('conflictKeys', (slice?.mergeAdmission?.conflictKeys?.length ?? 0) > 0, 'Slice exposes merge-admission conflict keys.'));
|
|
4359
|
+
const sourceHashAssertions = semanticSliceSourceHashAssertions(slice, options.currentSources);
|
|
4360
|
+
assertions.push(...sourceHashAssertions);
|
|
4361
|
+
const failed = assertions.filter((assertion) => assertion.status === 'failed');
|
|
4362
|
+
const warnings = assertions.filter((assertion) => assertion.status === 'warning');
|
|
4363
|
+
const readiness = failed.length
|
|
4364
|
+
? 'blocked'
|
|
4365
|
+
: maxSemanticMergeReadiness(slice?.mergeAdmission?.readiness ?? slice?.summary?.readiness ?? 'ready', warnings.length ? 'needs-review' : 'ready');
|
|
4366
|
+
return {
|
|
4367
|
+
kind: 'frontier.lang.semanticSliceTestResult',
|
|
4368
|
+
version: 1,
|
|
4369
|
+
id: options.id ?? `semantic_slice_test_${idFragment(slice?.id ?? 'slice')}`,
|
|
4370
|
+
generatedAt: options.generatedAt ?? Date.now(),
|
|
4371
|
+
sliceId: slice?.id,
|
|
4372
|
+
status: failed.length ? 'failed' : warnings.length ? 'needs-review' : 'passed',
|
|
4373
|
+
readiness,
|
|
4374
|
+
assertions,
|
|
4375
|
+
summary: {
|
|
4376
|
+
assertions: assertions.length,
|
|
4377
|
+
passed: assertions.filter((assertion) => assertion.status === 'passed').length,
|
|
4378
|
+
warnings: warnings.length,
|
|
4379
|
+
failed: failed.length,
|
|
4380
|
+
sourceHashChecks: sourceHashAssertions.length,
|
|
4381
|
+
symbols: slice?.summary?.symbols ?? slice?.symbols?.length ?? 0,
|
|
4382
|
+
ownershipRegions: slice?.summary?.ownershipRegions ?? slice?.ownershipRegions?.length ?? 0,
|
|
4383
|
+
sourceMapLinks: slice?.summary?.sourceMapLinks ?? slice?.sourceMapLinks?.length ?? 0
|
|
4384
|
+
},
|
|
4385
|
+
metadata: {
|
|
4386
|
+
sliceReadiness: slice?.mergeAdmission?.readiness ?? slice?.summary?.readiness,
|
|
4387
|
+
...options.metadata
|
|
4388
|
+
}
|
|
4389
|
+
};
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
export function readSemanticSliceJson(source) {
|
|
4393
|
+
const slice = JSON.parse(source);
|
|
4394
|
+
if (slice?.kind !== 'frontier.lang.semanticSlice') {
|
|
4395
|
+
throw new Error('Invalid Frontier semantic slice JSON: expected kind frontier.lang.semanticSlice');
|
|
4396
|
+
}
|
|
4397
|
+
return slice;
|
|
4398
|
+
}
|
|
4399
|
+
|
|
4400
|
+
export function writeSemanticSliceJson(slice) {
|
|
4401
|
+
if (slice?.kind !== 'frontier.lang.semanticSlice') {
|
|
4402
|
+
throw new Error('Invalid Frontier semantic slice: expected kind frontier.lang.semanticSlice');
|
|
4403
|
+
}
|
|
4404
|
+
return stableUniversalAstJson(slice);
|
|
4405
|
+
}
|
|
4406
|
+
|
|
4407
|
+
function semanticSliceContext(input, options = {}) {
|
|
4408
|
+
const value = input?.importResult ?? input?.import ?? input?.source ?? input;
|
|
4409
|
+
const imported = semanticSliceImportResult(value, options);
|
|
4410
|
+
const universalAst = imported?.universalAst ?? (value?.kind === 'frontier.lang.universalAst' ? value : value?.universalAst);
|
|
4411
|
+
const sidecar = value?.kind === 'frontier.lang.semanticImportSidecar' ? value : input?.sidecar ?? options.sidecar;
|
|
4412
|
+
const nativeSource = imported?.nativeSource ?? universalAst?.nativeSources?.[0];
|
|
4413
|
+
const nativeAst = imported?.nativeAst ?? nativeSource?.ast;
|
|
4414
|
+
const semanticIndex = imported?.semanticIndex ?? universalAst?.semanticIndex;
|
|
4415
|
+
const language = options.language ?? imported?.language ?? universalAst?.metadata?.sourceLanguage ?? nativeSource?.language ?? sidecar?.language;
|
|
4416
|
+
const sourcePath = options.sourcePath ?? imported?.sourcePath ?? universalAst?.metadata?.sourcePath ?? nativeSource?.sourcePath ?? semanticIndex?.documents?.[0]?.path;
|
|
4417
|
+
return {
|
|
4418
|
+
importResult: imported,
|
|
4419
|
+
universalAst,
|
|
4420
|
+
sidecar,
|
|
4421
|
+
nativeSource,
|
|
4422
|
+
nativeAst,
|
|
4423
|
+
semanticIndex,
|
|
4424
|
+
language,
|
|
4425
|
+
sourcePath,
|
|
4426
|
+
sourceTexts: semanticSliceSourceTextMap(imported, universalAst, value, options),
|
|
4427
|
+
sourceHashes: semanticSliceSourceHashMap(imported, universalAst, value, options)
|
|
4428
|
+
};
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
function semanticSliceImportResult(value, options = {}) {
|
|
4432
|
+
if (!value) return undefined;
|
|
4433
|
+
if (value.kind === 'frontier.lang.importResult') return value;
|
|
4434
|
+
if (value.kind === 'frontier.lang.universalAst') {
|
|
4435
|
+
const nativeSource = value.nativeSources?.[0];
|
|
4436
|
+
return {
|
|
4437
|
+
kind: 'frontier.lang.importResult',
|
|
4438
|
+
version: 1,
|
|
4439
|
+
id: value.metadata?.nativeImportId ?? `import_${idFragment(value.id)}`,
|
|
4440
|
+
language: value.metadata?.sourceLanguage ?? nativeSource?.language ?? options.language,
|
|
4441
|
+
sourcePath: value.metadata?.sourcePath ?? nativeSource?.sourcePath ?? options.sourcePath,
|
|
4442
|
+
document: value.document,
|
|
4443
|
+
nativeSource,
|
|
4444
|
+
nativeAst: nativeSource?.ast,
|
|
4445
|
+
semanticIndex: value.semanticIndex,
|
|
4446
|
+
universalAst: value,
|
|
4447
|
+
sourceMaps: value.sourceMaps ?? [],
|
|
4448
|
+
losses: value.losses ?? [],
|
|
4449
|
+
evidence: value.evidence ?? [],
|
|
4450
|
+
mergeCandidates: value.mergeCandidates ?? [],
|
|
4451
|
+
metadata: value.metadata ?? {}
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4454
|
+
if (value.kind === 'frontier.lang.semanticImportSidecar') return undefined;
|
|
4455
|
+
if (value.universalAst || value.semanticIndex || value.nativeSource || value.nativeAst) return value;
|
|
4456
|
+
if (value.sourceText || value.nodes || value.nativeAst) return importNativeSource(value);
|
|
4457
|
+
return undefined;
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
function semanticSliceRecords(context, sidecar) {
|
|
4461
|
+
const imports = context.importResult ? nativeImportEntries(context.importResult) : [];
|
|
4462
|
+
const universalAst = context.universalAst;
|
|
4463
|
+
const semanticIndexes = [
|
|
4464
|
+
context.semanticIndex,
|
|
4465
|
+
universalAst?.semanticIndex,
|
|
4466
|
+
...imports.map((imported) => imported?.semanticIndex ?? imported?.universalAst?.semanticIndex)
|
|
4467
|
+
].filter(Boolean);
|
|
4468
|
+
const sourceMaps = uniqueRecordsById([
|
|
4469
|
+
...(context.importResult?.sourceMaps ?? []),
|
|
4470
|
+
...(universalAst?.sourceMaps ?? []),
|
|
4471
|
+
...imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? [])
|
|
4472
|
+
]);
|
|
4473
|
+
const symbols = mergeSemanticSliceSymbols([
|
|
4474
|
+
...(sidecar?.symbols ?? []),
|
|
4475
|
+
...semanticIndexes.flatMap((index) => index.symbols ?? [])
|
|
4476
|
+
]);
|
|
4477
|
+
const regions = uniqueSemanticSliceRegions([
|
|
4478
|
+
...(sidecar?.ownershipRegions ?? []),
|
|
4479
|
+
...symbols.map((symbol) => semanticSliceRegionFromSymbol(symbol, context)).filter(Boolean),
|
|
4480
|
+
...sourceMaps.flatMap((sourceMap) => (sourceMap.mappings ?? []).map((mapping) => semanticSliceRegionFromMapping(mapping, context)).filter(Boolean))
|
|
4481
|
+
]);
|
|
4482
|
+
const nativeNodes = uniqueSemanticSliceNativeNodes([
|
|
4483
|
+
...imports.flatMap((imported) => nativeAstNodes(imported?.nativeAst ?? imported?.nativeSource?.ast)),
|
|
4484
|
+
...nativeAstNodes(context.nativeAst),
|
|
4485
|
+
...((universalAst?.nativeSources ?? []).flatMap((source) => nativeAstNodes(source?.ast)))
|
|
4486
|
+
]);
|
|
4487
|
+
const mappings = sourceMaps.flatMap((sourceMap) => (sourceMap.mappings ?? []).map((mapping) => ({
|
|
4488
|
+
...mapping,
|
|
4489
|
+
sourceMapId: mapping.sourceMapId ?? sourceMap.id,
|
|
4490
|
+
sourceMapSourcePath: sourceMap.sourcePath,
|
|
4491
|
+
sourceMapSourceHash: sourceMap.sourceHash,
|
|
4492
|
+
sourceMapTargetPath: sourceMap.targetPath,
|
|
4493
|
+
sourceMapTargetHash: sourceMap.targetHash
|
|
4494
|
+
})));
|
|
4495
|
+
const losses = uniqueByLossId([
|
|
4496
|
+
...(context.importResult?.losses ?? []),
|
|
4497
|
+
...(universalAst?.losses ?? []),
|
|
4498
|
+
...imports.flatMap((imported) => imported?.losses ?? imported?.nativeAst?.losses ?? [])
|
|
4499
|
+
]);
|
|
4500
|
+
const evidence = uniqueByEvidenceId([
|
|
4501
|
+
...(context.importResult?.evidence ?? []),
|
|
4502
|
+
...(universalAst?.evidence ?? []),
|
|
4503
|
+
...imports.flatMap((imported) => imported?.evidence ?? [])
|
|
4504
|
+
]);
|
|
4505
|
+
return {
|
|
4506
|
+
symbols,
|
|
4507
|
+
symbolById: new Map(symbols.map((symbol) => [symbol.id, symbol])),
|
|
4508
|
+
regions,
|
|
4509
|
+
nativeNodes,
|
|
4510
|
+
nativeNodeById: new Map(nativeNodes.map((node) => [node.id, node])),
|
|
4511
|
+
mappings,
|
|
4512
|
+
sourceMaps,
|
|
4513
|
+
relations: uniqueRecordsById(semanticIndexes.flatMap((index) => index.relations ?? [])),
|
|
4514
|
+
occurrences: uniqueRecordsById(semanticIndexes.flatMap((index) => index.occurrences ?? [])),
|
|
4515
|
+
losses,
|
|
4516
|
+
evidence,
|
|
4517
|
+
sidecar
|
|
4518
|
+
};
|
|
4519
|
+
}
|
|
4520
|
+
|
|
4521
|
+
function selectSemanticSliceRecords(records, options) {
|
|
4522
|
+
const selectedSymbols = new Set();
|
|
4523
|
+
const selectedRegions = new Set();
|
|
4524
|
+
const selectedNativeNodes = new Set();
|
|
4525
|
+
const selectedMappings = new Set();
|
|
4526
|
+
const selectedRelations = new Set();
|
|
4527
|
+
const selectedOccurrences = new Set();
|
|
4528
|
+
const matchedEntryRefs = [];
|
|
4529
|
+
const entryRefs = options.entryRefs ?? [];
|
|
4530
|
+
if (entryRefs.length === 0) {
|
|
4531
|
+
for (const symbol of records.symbols) selectedSymbols.add(symbol.id);
|
|
4532
|
+
for (const region of records.regions) selectedRegions.add(region.id);
|
|
4533
|
+
for (const node of records.nativeNodes) selectedNativeNodes.add(node.id);
|
|
4534
|
+
for (const mapping of records.mappings) selectedMappings.add(mapping.id);
|
|
4535
|
+
} else {
|
|
4536
|
+
for (const ref of entryRefs) {
|
|
4537
|
+
let matched = false;
|
|
4538
|
+
for (const symbol of records.symbols) {
|
|
4539
|
+
if (!semanticSliceSymbolMatchesRef(symbol, ref)) continue;
|
|
4540
|
+
selectedSymbols.add(symbol.id);
|
|
4541
|
+
matched = true;
|
|
4542
|
+
}
|
|
4543
|
+
for (const region of records.regions) {
|
|
4544
|
+
if (!semanticSliceRegionMatchesRef(region, ref)) continue;
|
|
4545
|
+
selectedRegions.add(region.id);
|
|
4546
|
+
matched = true;
|
|
4547
|
+
}
|
|
4548
|
+
for (const node of records.nativeNodes) {
|
|
4549
|
+
if (!semanticSliceNativeNodeMatchesRef(node, ref)) continue;
|
|
4550
|
+
selectedNativeNodes.add(node.id);
|
|
4551
|
+
matched = true;
|
|
4552
|
+
}
|
|
4553
|
+
for (const mapping of records.mappings) {
|
|
4554
|
+
if (!semanticSliceMappingMatchesRef(mapping, ref)) continue;
|
|
4555
|
+
selectedMappings.add(mapping.id);
|
|
4556
|
+
matched = true;
|
|
4557
|
+
}
|
|
4558
|
+
if (matched) matchedEntryRefs.push(ref);
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4561
|
+
expandSemanticSliceSelection(records, {
|
|
4562
|
+
selectedSymbols,
|
|
4563
|
+
selectedRegions,
|
|
4564
|
+
selectedNativeNodes,
|
|
4565
|
+
selectedMappings,
|
|
4566
|
+
selectedRelations,
|
|
4567
|
+
selectedOccurrences,
|
|
4568
|
+
includeDependencies: options.includeDependencies,
|
|
4569
|
+
maxDependencyDepth: options.maxDependencyDepth
|
|
4570
|
+
});
|
|
4571
|
+
const symbols = records.symbols.filter((symbol) => selectedSymbols.has(symbol.id));
|
|
4572
|
+
const regions = records.regions.filter((region) => selectedRegions.has(region.id));
|
|
4573
|
+
const nativeNodes = records.nativeNodes.filter((node) => selectedNativeNodes.has(node.id));
|
|
4574
|
+
const mappings = records.mappings.filter((mapping) => selectedMappings.has(mapping.id));
|
|
4575
|
+
const relations = records.relations.filter((relation) => selectedRelations.has(relation.id));
|
|
4576
|
+
const occurrences = records.occurrences.filter((occurrence) => selectedOccurrences.has(occurrence.id));
|
|
4577
|
+
const selectedNodeIds = new Set(nativeNodes.map((node) => node.id));
|
|
4578
|
+
const losses = records.losses.filter((loss) => !loss.nodeId || selectedNodeIds.has(loss.nodeId) || semanticSliceSpanTouchesSelection(loss.span, mappings, regions));
|
|
4579
|
+
return {
|
|
4580
|
+
matchedEntryRefs,
|
|
4581
|
+
symbols,
|
|
4582
|
+
regions,
|
|
4583
|
+
nativeNodes,
|
|
4584
|
+
mappings,
|
|
4585
|
+
relations,
|
|
4586
|
+
occurrences,
|
|
4587
|
+
losses
|
|
4588
|
+
};
|
|
4589
|
+
}
|
|
4590
|
+
|
|
4591
|
+
function expandSemanticSliceSelection(records, selection) {
|
|
4592
|
+
let changed = true;
|
|
4593
|
+
let depth = 0;
|
|
4594
|
+
const childToParent = semanticSliceNativeParentMap(records.nativeNodes);
|
|
4595
|
+
while (changed && depth <= selection.maxDependencyDepth) {
|
|
4596
|
+
changed = false;
|
|
4597
|
+
for (const mapping of records.mappings) {
|
|
4598
|
+
if (!semanticSliceMappingTouchesSets(mapping, selection)) continue;
|
|
4599
|
+
changed = addSet(selection.selectedMappings, mapping.id) || changed;
|
|
4600
|
+
if (mapping.semanticSymbolId) changed = addSet(selection.selectedSymbols, mapping.semanticSymbolId) || changed;
|
|
4601
|
+
if (mapping.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, mapping.nativeAstNodeId) || changed;
|
|
4602
|
+
if (mapping.ownershipRegionId) changed = addSet(selection.selectedRegions, mapping.ownershipRegionId) || changed;
|
|
4603
|
+
}
|
|
4604
|
+
for (const symbol of records.symbols) {
|
|
4605
|
+
if (!semanticSliceSymbolTouchesSets(symbol, selection)) continue;
|
|
4606
|
+
changed = addSet(selection.selectedSymbols, symbol.id) || changed;
|
|
4607
|
+
if (symbol.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, symbol.nativeAstNodeId) || changed;
|
|
4608
|
+
const regionId = symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId;
|
|
4609
|
+
if (regionId) changed = addSet(selection.selectedRegions, regionId) || changed;
|
|
4610
|
+
}
|
|
4611
|
+
for (const region of records.regions) {
|
|
4612
|
+
if (!semanticSliceRegionTouchesSets(region, selection)) continue;
|
|
4613
|
+
changed = addSet(selection.selectedRegions, region.id) || changed;
|
|
4614
|
+
if (region.symbolId) changed = addSet(selection.selectedSymbols, region.symbolId) || changed;
|
|
4615
|
+
if (region.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, region.nativeAstNodeId) || changed;
|
|
4616
|
+
}
|
|
4617
|
+
for (const occurrence of records.occurrences) {
|
|
4618
|
+
if (!selection.selectedSymbols.has(occurrence.symbolId) && !selection.selectedNativeNodes.has(occurrence.nativeAstNodeId)) continue;
|
|
4619
|
+
changed = addSet(selection.selectedOccurrences, occurrence.id) || changed;
|
|
4620
|
+
if (occurrence.symbolId) changed = addSet(selection.selectedSymbols, occurrence.symbolId) || changed;
|
|
4621
|
+
if (occurrence.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, occurrence.nativeAstNodeId) || changed;
|
|
4622
|
+
}
|
|
4623
|
+
if (selection.includeDependencies) {
|
|
4624
|
+
for (const relation of records.relations) {
|
|
4625
|
+
const touches = selection.selectedSymbols.has(relation.sourceId) || selection.selectedSymbols.has(relation.targetId);
|
|
4626
|
+
if (!touches) continue;
|
|
4627
|
+
changed = addSet(selection.selectedRelations, relation.id) || changed;
|
|
4628
|
+
if (relation.sourceId && records.symbolById.has(relation.sourceId)) changed = addSet(selection.selectedSymbols, relation.sourceId) || changed;
|
|
4629
|
+
if (relation.targetId && records.symbolById.has(relation.targetId)) changed = addSet(selection.selectedSymbols, relation.targetId) || changed;
|
|
4630
|
+
}
|
|
4631
|
+
for (const node of records.nativeNodes) {
|
|
4632
|
+
if (!selection.selectedNativeNodes.has(node.id)) continue;
|
|
4633
|
+
for (const child of node.children ?? []) changed = addSet(selection.selectedNativeNodes, child) || changed;
|
|
4634
|
+
const parent = childToParent.get(node.id);
|
|
4635
|
+
if (parent) changed = addSet(selection.selectedNativeNodes, parent) || changed;
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
depth += 1;
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4642
|
+
function semanticSliceSourceSpans(selection) {
|
|
4643
|
+
return uniqueSemanticSliceSpans([
|
|
4644
|
+
...selection.symbols.map(semanticSliceSymbolSpan),
|
|
4645
|
+
...selection.regions.map((region) => region.sourceSpan),
|
|
4646
|
+
...selection.nativeNodes.map((node) => node.span ?? node.sourceSpan),
|
|
4647
|
+
...selection.mappings.map((mapping) => mapping.sourceSpan)
|
|
4648
|
+
].filter(Boolean));
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
function semanticSliceSourceFiles(spans, context, options = {}) {
|
|
4652
|
+
const byPath = new Map();
|
|
4653
|
+
const includeExcerpts = options.includeSourceText !== false;
|
|
4654
|
+
const maxExcerptBytes = Number.isFinite(options.maxExcerptBytes) ? Math.max(0, Math.floor(options.maxExcerptBytes)) : 4000;
|
|
4655
|
+
for (const span of spans) {
|
|
4656
|
+
const path = span.path ?? span.sourceId ?? context.sourcePath ?? 'unknown';
|
|
4657
|
+
const record = byPath.get(path) ?? {
|
|
4658
|
+
path,
|
|
4659
|
+
sourceHash: context.sourceHashes.get(path) ?? context.sourceHashes.get('') ?? span.sourceHash,
|
|
4660
|
+
spans: [],
|
|
4661
|
+
excerpts: []
|
|
4662
|
+
};
|
|
4663
|
+
record.spans.push(span);
|
|
4664
|
+
const sourceText = context.sourceTexts.get(path) ?? context.sourceTexts.get('');
|
|
4665
|
+
const excerpt = includeExcerpts ? sourceTextForSpan(sourceText, span) : undefined;
|
|
4666
|
+
if (typeof excerpt === 'string') {
|
|
4667
|
+
const clipped = excerpt.length > maxExcerptBytes ? excerpt.slice(0, maxExcerptBytes) : excerpt;
|
|
4668
|
+
record.excerpts.push({
|
|
4669
|
+
span,
|
|
4670
|
+
text: clipped,
|
|
4671
|
+
textHash: hashSemanticValue(excerpt),
|
|
4672
|
+
truncated: clipped.length !== excerpt.length
|
|
4673
|
+
});
|
|
4674
|
+
}
|
|
4675
|
+
byPath.set(path, record);
|
|
4676
|
+
}
|
|
4677
|
+
return [...byPath.values()].map((file) => ({
|
|
4678
|
+
...file,
|
|
4679
|
+
spanCount: file.spans.length,
|
|
4680
|
+
excerptCount: file.excerpts.length,
|
|
4681
|
+
sourceTextAvailable: file.excerpts.length > 0
|
|
4682
|
+
}));
|
|
4683
|
+
}
|
|
4684
|
+
|
|
4685
|
+
function semanticSliceSourceMapLinks(selection) {
|
|
4686
|
+
return selection.mappings.map((mapping) => ({
|
|
4687
|
+
id: mapping.id,
|
|
4688
|
+
sourceMapId: mapping.sourceMapId,
|
|
4689
|
+
sourcePath: mapping.sourceSpan?.path ?? mapping.sourceMapSourcePath,
|
|
4690
|
+
sourceHash: mapping.sourceMapSourceHash,
|
|
4691
|
+
targetPath: mapping.generatedSpan?.targetPath ?? mapping.sourceMapTargetPath,
|
|
4692
|
+
targetHash: mapping.generatedSpan?.targetHash ?? mapping.sourceMapTargetHash,
|
|
4693
|
+
semanticSymbolId: mapping.semanticSymbolId,
|
|
4694
|
+
semanticOccurrenceId: mapping.semanticOccurrenceId,
|
|
4695
|
+
semanticNodeId: mapping.semanticNodeId,
|
|
4696
|
+
nativeSourceId: mapping.nativeSourceId,
|
|
4697
|
+
nativeAstNodeId: mapping.nativeAstNodeId,
|
|
4698
|
+
precision: mapping.precision,
|
|
4699
|
+
sourceSpan: mapping.sourceSpan,
|
|
4700
|
+
generatedSpan: mapping.generatedSpan,
|
|
4701
|
+
ownershipRegionId: mapping.ownershipRegionId,
|
|
4702
|
+
ownershipRegionKey: mapping.ownershipRegionKey,
|
|
4703
|
+
ownershipRegionKind: mapping.ownershipRegionKind
|
|
4704
|
+
}));
|
|
4705
|
+
}
|
|
4706
|
+
|
|
4707
|
+
function semanticSliceReadiness(context, selection, unresolvedEntryRefs) {
|
|
4708
|
+
if (unresolvedEntryRefs.length) return 'blocked';
|
|
4709
|
+
if (selection.symbols.length + selection.regions.length + selection.nativeNodes.length === 0) return 'blocked';
|
|
4710
|
+
const lossReadiness = summarizeNativeImportLosses(selection.losses, { evidence: context.importResult?.evidence }).semanticMergeReadiness;
|
|
4711
|
+
return [
|
|
4712
|
+
context.importResult?.metadata?.semanticMergeReadiness,
|
|
4713
|
+
context.importResult?.metadata?.nativeImportLossSummary?.semanticMergeReadiness,
|
|
4714
|
+
context.sidecar?.summary?.readiness,
|
|
4715
|
+
...(context.importResult?.mergeCandidates ?? []).map((candidate) => candidate.readiness),
|
|
4716
|
+
lossReadiness
|
|
4717
|
+
].reduce((current, value) => maxSemanticMergeReadiness(current, value ?? 'ready'), 'ready');
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
function semanticSliceReasons(context, selection, unresolvedEntryRefs, readiness) {
|
|
4721
|
+
return uniqueStrings([
|
|
4722
|
+
unresolvedEntryRefs.length ? `Unresolved semantic slice entry refs: ${unresolvedEntryRefs.join(', ')}` : undefined,
|
|
4723
|
+
selection.symbols.length + selection.regions.length + selection.nativeNodes.length === 0 ? 'Semantic slice selected no reviewable records.' : undefined,
|
|
4724
|
+
selection.mappings.length === 0 ? 'Semantic slice has no source-map links; source review may need file-level fallback.' : undefined,
|
|
4725
|
+
selection.losses.length ? `Semantic slice carries ${selection.losses.length} native import loss record(s).` : undefined,
|
|
4726
|
+
readiness !== 'ready' ? `Semantic slice readiness is ${readiness}.` : undefined,
|
|
4727
|
+
context.importResult?.metadata?.nativeImportLossSummary?.semanticMergeReadiness === 'needs-review' ? 'Parent native import requires review.' : undefined
|
|
4728
|
+
].filter(Boolean));
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
function semanticSliceExpectedAssertions(selection, unresolvedEntryRefs) {
|
|
4732
|
+
return [
|
|
4733
|
+
{ id: 'entryRefsResolved', expected: unresolvedEntryRefs.length === 0 },
|
|
4734
|
+
{ id: 'nonEmptySelection', expected: selection.symbols.length + selection.regions.length + selection.nativeNodes.length > 0 },
|
|
4735
|
+
{ id: 'sourceMapLinks', expected: selection.mappings.length > 0 },
|
|
4736
|
+
{ id: 'conflictKeys', expected: true }
|
|
4737
|
+
];
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4740
|
+
function semanticSliceTouchedSymbol(symbol) {
|
|
4741
|
+
return {
|
|
4742
|
+
id: symbol.id,
|
|
4743
|
+
name: symbol.name,
|
|
4744
|
+
kind: symbol.kind,
|
|
4745
|
+
nativeAstNodeId: symbol.nativeAstNodeId,
|
|
4746
|
+
span: semanticSliceSymbolSpan(symbol),
|
|
4747
|
+
conflictKey: symbol.metadata?.ownershipRegionKey ? `region:${symbol.metadata.ownershipRegionKey}` : `symbol:${symbol.id}`,
|
|
4748
|
+
metadata: {
|
|
4749
|
+
ownershipRegionId: symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId,
|
|
4750
|
+
ownershipRegionKind: symbol.ownershipRegionKind ?? symbol.metadata?.ownershipRegionKind,
|
|
4751
|
+
signatureHash: symbol.signatureHash
|
|
4752
|
+
}
|
|
4753
|
+
};
|
|
4754
|
+
}
|
|
4755
|
+
|
|
4756
|
+
function semanticSliceAssertion(id, ok, summary, metadata = {}) {
|
|
4757
|
+
return {
|
|
4758
|
+
id,
|
|
4759
|
+
status: ok ? 'passed' : 'failed',
|
|
4760
|
+
summary,
|
|
4761
|
+
metadata
|
|
4762
|
+
};
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
function semanticSliceSourceHashAssertions(slice, currentSources) {
|
|
4766
|
+
if (!currentSources) return [];
|
|
4767
|
+
const assertions = [];
|
|
4768
|
+
for (const file of slice?.sourceFiles ?? []) {
|
|
4769
|
+
if (!file.sourceHash || !file.path) continue;
|
|
4770
|
+
const currentSource = semanticSliceCurrentSource(currentSources, file.path);
|
|
4771
|
+
if (typeof currentSource !== 'string') {
|
|
4772
|
+
assertions.push({
|
|
4773
|
+
id: `sourceHash:${file.path}`,
|
|
4774
|
+
status: 'warning',
|
|
4775
|
+
summary: `Current source text was not supplied for ${file.path}.`,
|
|
4776
|
+
metadata: { path: file.path, expectedSourceHash: file.sourceHash }
|
|
4777
|
+
});
|
|
4778
|
+
continue;
|
|
4779
|
+
}
|
|
4780
|
+
const actualSourceHash = hashSemanticValue(currentSource);
|
|
4781
|
+
assertions.push({
|
|
4782
|
+
id: `sourceHash:${file.path}`,
|
|
4783
|
+
status: actualSourceHash === file.sourceHash ? 'passed' : 'failed',
|
|
4784
|
+
summary: actualSourceHash === file.sourceHash ? `Source hash matched for ${file.path}.` : `Source hash changed for ${file.path}.`,
|
|
4785
|
+
metadata: { path: file.path, expectedSourceHash: file.sourceHash, actualSourceHash }
|
|
4786
|
+
});
|
|
4787
|
+
}
|
|
4788
|
+
return assertions;
|
|
4789
|
+
}
|
|
4790
|
+
|
|
4791
|
+
function semanticSliceCurrentSource(currentSources, path) {
|
|
4792
|
+
if (currentSources instanceof Map) return currentSources.get(path);
|
|
4793
|
+
return currentSources[path] ?? currentSources[`./${path}`];
|
|
4794
|
+
}
|
|
4795
|
+
|
|
4796
|
+
function semanticSliceSourceTextMap(imported, universalAst, value, options) {
|
|
4797
|
+
const entries = new Map();
|
|
4798
|
+
const add = (path, sourceText) => {
|
|
4799
|
+
if (typeof sourceText !== 'string') return;
|
|
4800
|
+
entries.set(path ?? '', sourceText);
|
|
4801
|
+
};
|
|
4802
|
+
add(options.sourcePath, options.sourceText);
|
|
4803
|
+
add(value?.sourcePath, value?.sourceText);
|
|
4804
|
+
for (const item of [imported, ...(imported ? nativeImportEntries(imported) : [])]) {
|
|
4805
|
+
add(item?.sourcePath, nativeImportSourceText(item));
|
|
4806
|
+
add(item?.nativeSource?.sourcePath, item?.nativeSource?.metadata?.sourcePreservation?.sourceText);
|
|
4807
|
+
}
|
|
4808
|
+
for (const nativeSource of universalAst?.nativeSources ?? []) {
|
|
4809
|
+
add(nativeSource?.sourcePath, nativeSource?.metadata?.sourcePreservation?.sourceText);
|
|
4810
|
+
add(nativeSource?.sourcePath, nativeSource?.ast?.metadata?.sourcePreservation?.sourceText);
|
|
4811
|
+
}
|
|
4812
|
+
return entries;
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
function semanticSliceSourceHashMap(imported, universalAst, value, options) {
|
|
4816
|
+
const entries = new Map();
|
|
4817
|
+
const add = (path, sourceHash) => {
|
|
4818
|
+
if (!sourceHash) return;
|
|
4819
|
+
entries.set(path ?? '', sourceHash);
|
|
4820
|
+
};
|
|
4821
|
+
add(options.sourcePath, options.sourceHash);
|
|
4822
|
+
add(value?.sourcePath, value?.sourceHash);
|
|
4823
|
+
for (const item of [imported, ...(imported ? nativeImportEntries(imported) : [])]) {
|
|
4824
|
+
add(item?.sourcePath, item?.nativeSource?.sourceHash ?? item?.nativeAst?.sourceHash ?? item?.sourceHash);
|
|
4825
|
+
}
|
|
4826
|
+
for (const nativeSource of universalAst?.nativeSources ?? []) {
|
|
4827
|
+
add(nativeSource?.sourcePath, nativeSource?.sourceHash ?? nativeSource?.ast?.sourceHash);
|
|
4828
|
+
}
|
|
4829
|
+
for (const sourceMap of universalAst?.sourceMaps ?? []) add(sourceMap.sourcePath, sourceMap.sourceHash);
|
|
4830
|
+
return entries;
|
|
4831
|
+
}
|
|
4832
|
+
|
|
4833
|
+
function mergeSemanticSliceSymbols(symbols) {
|
|
4834
|
+
const byId = new Map();
|
|
4835
|
+
for (const symbol of symbols ?? []) {
|
|
4836
|
+
if (!symbol?.id) continue;
|
|
4837
|
+
const existing = byId.get(symbol.id);
|
|
4838
|
+
byId.set(symbol.id, {
|
|
4839
|
+
...(existing ?? {}),
|
|
4840
|
+
...symbol,
|
|
4841
|
+
metadata: {
|
|
4842
|
+
...(existing?.metadata ?? {}),
|
|
4843
|
+
...(symbol.metadata ?? {})
|
|
4844
|
+
}
|
|
4845
|
+
});
|
|
4846
|
+
}
|
|
4847
|
+
return [...byId.values()];
|
|
4848
|
+
}
|
|
4849
|
+
|
|
4850
|
+
function uniqueSemanticSliceRegions(regions) {
|
|
4851
|
+
const seen = new Set();
|
|
4852
|
+
const result = [];
|
|
4853
|
+
for (const region of regions ?? []) {
|
|
4854
|
+
if (!region) continue;
|
|
4855
|
+
const id = region.id ?? `region_${idFragment(region.key ?? region.sourcePath ?? region.symbolId ?? result.length)}`;
|
|
4856
|
+
const key = region.key ?? id;
|
|
4857
|
+
const dedupeKey = `${id}:${key}`;
|
|
4858
|
+
if (seen.has(dedupeKey)) continue;
|
|
4859
|
+
seen.add(dedupeKey);
|
|
4860
|
+
result.push({ ...region, id, key });
|
|
4861
|
+
}
|
|
4862
|
+
return result;
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4865
|
+
function uniqueSemanticSliceNativeNodes(nodes) {
|
|
4866
|
+
const seen = new Set();
|
|
4867
|
+
const result = [];
|
|
4868
|
+
for (const node of nodes ?? []) {
|
|
4869
|
+
const id = node?.id ?? node?.nodeId;
|
|
4870
|
+
if (!id || seen.has(id)) continue;
|
|
4871
|
+
seen.add(id);
|
|
4872
|
+
result.push({ ...node, id });
|
|
4873
|
+
}
|
|
4874
|
+
return result;
|
|
4875
|
+
}
|
|
4876
|
+
|
|
4877
|
+
function uniqueSemanticSliceSpans(spans) {
|
|
4878
|
+
const seen = new Set();
|
|
4879
|
+
const result = [];
|
|
4880
|
+
for (const span of spans ?? []) {
|
|
4881
|
+
if (!span) continue;
|
|
4882
|
+
const key = [
|
|
4883
|
+
span.path,
|
|
4884
|
+
span.sourceId,
|
|
4885
|
+
span.start,
|
|
4886
|
+
span.end,
|
|
4887
|
+
span.startLine,
|
|
4888
|
+
span.startColumn,
|
|
4889
|
+
span.endLine,
|
|
4890
|
+
span.endColumn
|
|
4891
|
+
].join(':');
|
|
4892
|
+
if (seen.has(key)) continue;
|
|
4893
|
+
seen.add(key);
|
|
4894
|
+
result.push(span);
|
|
4895
|
+
}
|
|
4896
|
+
return result;
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
function nativeAstNodes(nativeAst) {
|
|
4900
|
+
if (!nativeAst?.nodes) return [];
|
|
4901
|
+
return Object.values(nativeAst.nodes);
|
|
4902
|
+
}
|
|
4903
|
+
|
|
4904
|
+
function semanticSliceRegionFromSymbol(symbol, context) {
|
|
4905
|
+
const regionId = symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId;
|
|
4906
|
+
const regionKey = symbol.ownershipKey ?? symbol.metadata?.ownershipRegionKey;
|
|
4907
|
+
if (!regionId && !regionKey) return undefined;
|
|
4908
|
+
return {
|
|
4909
|
+
id: regionId,
|
|
4910
|
+
key: regionKey,
|
|
4911
|
+
regionKind: symbol.ownershipRegionKind ?? symbol.metadata?.ownershipRegionKind ?? symbol.kind,
|
|
4912
|
+
granularity: 'symbol',
|
|
4913
|
+
language: symbol.language ?? context.language,
|
|
4914
|
+
sourcePath: semanticSliceSymbolSpan(symbol)?.path ?? context.sourcePath,
|
|
4915
|
+
sourceSpan: semanticSliceSymbolSpan(symbol),
|
|
4916
|
+
symbolId: symbol.id,
|
|
4917
|
+
symbolName: symbol.name,
|
|
4918
|
+
symbolKind: symbol.kind,
|
|
4919
|
+
nativeAstNodeId: symbol.nativeAstNodeId,
|
|
4920
|
+
precision: 'symbol',
|
|
4921
|
+
mergePolicy: 'semantic-region-review'
|
|
4922
|
+
};
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4925
|
+
function semanticSliceRegionFromMapping(mapping, context) {
|
|
4926
|
+
if (!mapping.ownershipRegionId && !mapping.ownershipRegionKey) return undefined;
|
|
4927
|
+
return {
|
|
4928
|
+
id: mapping.ownershipRegionId,
|
|
4929
|
+
key: mapping.ownershipRegionKey,
|
|
4930
|
+
regionKind: mapping.ownershipRegionKind,
|
|
4931
|
+
granularity: 'source-map',
|
|
4932
|
+
language: context.language,
|
|
4933
|
+
sourcePath: mapping.sourceSpan?.path ?? context.sourcePath,
|
|
4934
|
+
sourceSpan: mapping.sourceSpan,
|
|
4935
|
+
symbolId: mapping.semanticSymbolId,
|
|
4936
|
+
nativeAstNodeId: mapping.nativeAstNodeId,
|
|
4937
|
+
precision: mapping.precision,
|
|
4938
|
+
mergePolicy: 'source-map-region-review'
|
|
4939
|
+
};
|
|
4940
|
+
}
|
|
4941
|
+
|
|
4942
|
+
function semanticSliceSymbolSpan(symbol) {
|
|
4943
|
+
return symbol.sourceSpan ?? symbol.definitionSpan ?? symbol.span;
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
function semanticSliceSymbolMatchesRef(symbol, ref) {
|
|
4947
|
+
const normalized = normalizeSemanticSliceRef(ref, 'symbol');
|
|
4948
|
+
return [
|
|
4949
|
+
symbol.id,
|
|
4950
|
+
symbol.name,
|
|
4951
|
+
symbol.displayName,
|
|
4952
|
+
symbol.signature,
|
|
4953
|
+
symbol.nativeAstNodeId,
|
|
4954
|
+
symbol.metadata?.ownershipRegionId,
|
|
4955
|
+
symbol.metadata?.ownershipRegionKey
|
|
4956
|
+
].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized));
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
function semanticSliceRegionMatchesRef(region, ref) {
|
|
4960
|
+
const normalized = normalizeSemanticSliceRef(ref, 'region');
|
|
4961
|
+
return [
|
|
4962
|
+
region.id,
|
|
4963
|
+
region.key,
|
|
4964
|
+
region.symbolId,
|
|
4965
|
+
region.symbolName,
|
|
4966
|
+
region.nativeAstNodeId,
|
|
4967
|
+
region.sourcePath
|
|
4968
|
+
].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized));
|
|
4969
|
+
}
|
|
4970
|
+
|
|
4971
|
+
function semanticSliceNativeNodeMatchesRef(node, ref) {
|
|
4972
|
+
const normalized = normalizeSemanticSliceRef(ref, 'native');
|
|
4973
|
+
return [
|
|
4974
|
+
node.id,
|
|
4975
|
+
node.nodeId,
|
|
4976
|
+
node.name,
|
|
4977
|
+
node.symbolId,
|
|
4978
|
+
node.kind,
|
|
4979
|
+
node.languageKind
|
|
4980
|
+
].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized));
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4983
|
+
function semanticSliceMappingMatchesRef(mapping, ref) {
|
|
4984
|
+
const pathRef = normalizeSemanticSliceRef(ref, 'path');
|
|
4985
|
+
const normalized = normalizeSemanticSliceRef(ref, 'mapping');
|
|
4986
|
+
return [
|
|
4987
|
+
mapping.id,
|
|
4988
|
+
mapping.semanticSymbolId,
|
|
4989
|
+
mapping.semanticOccurrenceId,
|
|
4990
|
+
mapping.semanticNodeId,
|
|
4991
|
+
mapping.nativeAstNodeId,
|
|
4992
|
+
mapping.ownershipRegionId,
|
|
4993
|
+
mapping.ownershipRegionKey
|
|
4994
|
+
].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized))
|
|
4995
|
+
|| semanticSliceValueMatches(mapping.sourceSpan?.path ?? mapping.sourceMapSourcePath, pathRef);
|
|
4996
|
+
}
|
|
4997
|
+
|
|
4998
|
+
function normalizeSemanticSliceRef(ref, prefix) {
|
|
4999
|
+
const text = String(ref ?? '');
|
|
5000
|
+
return text.startsWith(`${prefix}:`) ? text.slice(prefix.length + 1) : text;
|
|
5001
|
+
}
|
|
5002
|
+
|
|
5003
|
+
function semanticSliceValueMatches(value, ref) {
|
|
5004
|
+
if (!value || !ref) return false;
|
|
5005
|
+
const left = String(value);
|
|
5006
|
+
const right = String(ref);
|
|
5007
|
+
return left === right || left.endsWith(`:${right}`) || left.includes(right);
|
|
5008
|
+
}
|
|
5009
|
+
|
|
5010
|
+
function semanticSliceMappingTouchesSets(mapping, selection) {
|
|
5011
|
+
return selection.selectedMappings.has(mapping.id)
|
|
5012
|
+
|| selection.selectedSymbols.has(mapping.semanticSymbolId)
|
|
5013
|
+
|| selection.selectedNativeNodes.has(mapping.nativeAstNodeId)
|
|
5014
|
+
|| selection.selectedRegions.has(mapping.ownershipRegionId)
|
|
5015
|
+
|| selection.selectedRegions.has(mapping.ownershipRegionKey);
|
|
5016
|
+
}
|
|
5017
|
+
|
|
5018
|
+
function semanticSliceSymbolTouchesSets(symbol, selection) {
|
|
5019
|
+
return selection.selectedSymbols.has(symbol.id)
|
|
5020
|
+
|| selection.selectedNativeNodes.has(symbol.nativeAstNodeId)
|
|
5021
|
+
|| selection.selectedRegions.has(symbol.ownershipRegionId)
|
|
5022
|
+
|| selection.selectedRegions.has(symbol.metadata?.ownershipRegionId)
|
|
5023
|
+
|| selection.selectedRegions.has(symbol.metadata?.ownershipRegionKey);
|
|
5024
|
+
}
|
|
5025
|
+
|
|
5026
|
+
function semanticSliceRegionTouchesSets(region, selection) {
|
|
5027
|
+
return selection.selectedRegions.has(region.id)
|
|
5028
|
+
|| selection.selectedRegions.has(region.key)
|
|
5029
|
+
|| selection.selectedSymbols.has(region.symbolId)
|
|
5030
|
+
|| selection.selectedNativeNodes.has(region.nativeAstNodeId);
|
|
5031
|
+
}
|
|
5032
|
+
|
|
5033
|
+
function semanticSliceSpanTouchesSelection(span, mappings, regions) {
|
|
5034
|
+
if (!span) return false;
|
|
5035
|
+
return mappings.some((mapping) => mapping.sourceSpan === span || semanticSliceSpansOverlap(mapping.sourceSpan, span))
|
|
5036
|
+
|| regions.some((region) => semanticSliceSpansOverlap(region.sourceSpan, span));
|
|
5037
|
+
}
|
|
5038
|
+
|
|
5039
|
+
function semanticSliceSpansOverlap(left, right) {
|
|
5040
|
+
if (!left || !right) return false;
|
|
5041
|
+
const leftPath = left.path ?? left.sourceId;
|
|
5042
|
+
const rightPath = right.path ?? right.sourceId;
|
|
5043
|
+
if (leftPath && rightPath && leftPath !== rightPath) return false;
|
|
5044
|
+
if (typeof left.start === 'number' && typeof left.end === 'number' && typeof right.start === 'number' && typeof right.end === 'number') {
|
|
5045
|
+
return left.start <= right.end && right.start <= left.end;
|
|
5046
|
+
}
|
|
5047
|
+
if (typeof left.startLine === 'number' && typeof right.startLine === 'number') {
|
|
5048
|
+
const leftEnd = left.endLine ?? left.startLine;
|
|
5049
|
+
const rightEnd = right.endLine ?? right.startLine;
|
|
5050
|
+
return left.startLine <= rightEnd && right.startLine <= leftEnd;
|
|
5051
|
+
}
|
|
5052
|
+
return false;
|
|
5053
|
+
}
|
|
5054
|
+
|
|
5055
|
+
function semanticSliceNativeParentMap(nativeNodes) {
|
|
5056
|
+
const parents = new Map();
|
|
5057
|
+
for (const node of nativeNodes ?? []) {
|
|
5058
|
+
for (const child of node.children ?? []) parents.set(child, node.id);
|
|
5059
|
+
}
|
|
5060
|
+
return parents;
|
|
5061
|
+
}
|
|
5062
|
+
|
|
5063
|
+
function addSet(set, value) {
|
|
5064
|
+
if (!value || set.has(value)) return false;
|
|
5065
|
+
set.add(value);
|
|
5066
|
+
return true;
|
|
5067
|
+
}
|
|
5068
|
+
|
|
5069
|
+
function readStringArray(value) {
|
|
5070
|
+
if (value === undefined || value === null) return [];
|
|
5071
|
+
if (Array.isArray(value)) return value.flatMap((entry) => readStringArray(entry));
|
|
5072
|
+
return String(value).split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
5073
|
+
}
|
|
5074
|
+
|
|
4214
5075
|
function normalizeNativeDiffImport(value, input, side) {
|
|
4215
5076
|
if (!value) return undefined;
|
|
4216
5077
|
if (value.kind === 'frontier.lang.importResult' && value.nativeSource) return value;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapeshift-labs/frontier-lang-compiler",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.41",
|
|
4
4
|
"description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,10 +27,11 @@
|
|
|
27
27
|
"fuzz": "npm run build && node fuzz/smoke.mjs",
|
|
28
28
|
"bench": "npm run build && node bench/smoke.mjs",
|
|
29
29
|
"prepare": "npm run build",
|
|
30
|
-
"prepack": "npm test",
|
|
30
|
+
"prepack": "npm run lint && npm test",
|
|
31
31
|
"pack:dry": "npm pack --dry-run",
|
|
32
32
|
"readme:packages": "node benchmarks/package-readme-sections.mjs",
|
|
33
|
-
"readme:packages:check": "node benchmarks/package-readme-sections.mjs --check"
|
|
33
|
+
"readme:packages:check": "node benchmarks/package-readme-sections.mjs --check",
|
|
34
|
+
"lint": "node scripts/strict-source-policy.mjs"
|
|
34
35
|
},
|
|
35
36
|
"keywords": [
|
|
36
37
|
"frontier",
|
|
@@ -56,14 +57,14 @@
|
|
|
56
57
|
"access": "public"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@shapeshift-labs/frontier-lang-c": "0.2.
|
|
60
|
-
"@shapeshift-labs/frontier-lang-checker": "0.3.
|
|
61
|
-
"@shapeshift-labs/frontier-lang-javascript": "0.2.
|
|
62
|
-
"@shapeshift-labs/frontier-lang-kernel": "0.3.
|
|
63
|
-
"@shapeshift-labs/frontier-lang-parser": "0.3.
|
|
64
|
-
"@shapeshift-labs/frontier-lang-python": "0.2.
|
|
65
|
-
"@shapeshift-labs/frontier-lang-rust": "0.2.
|
|
66
|
-
"@shapeshift-labs/frontier-lang-typescript": "0.3.
|
|
60
|
+
"@shapeshift-labs/frontier-lang-c": "0.2.6",
|
|
61
|
+
"@shapeshift-labs/frontier-lang-checker": "0.3.5",
|
|
62
|
+
"@shapeshift-labs/frontier-lang-javascript": "0.2.6",
|
|
63
|
+
"@shapeshift-labs/frontier-lang-kernel": "0.3.9",
|
|
64
|
+
"@shapeshift-labs/frontier-lang-parser": "0.3.5",
|
|
65
|
+
"@shapeshift-labs/frontier-lang-python": "0.2.6",
|
|
66
|
+
"@shapeshift-labs/frontier-lang-rust": "0.2.6",
|
|
67
|
+
"@shapeshift-labs/frontier-lang-typescript": "0.3.6"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"typescript": "^5.9.3"
|