@shapeshift-labs/frontier-lang-parser 0.3.64 → 0.3.65
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/dist/application-surface.js +23 -23
- package/dist/canvas-surface.js +23 -23
- package/dist/package-manifest.js +21 -22
- package/dist/resource-graph.js +6 -1
- package/dist/row-identity.js +81 -0
- package/dist/runtime-capability-records.js +2 -1
- package/dist/runtime-capability.js +43 -24
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { createRowIdentityTracker } from './row-identity.js';
|
|
2
3
|
|
|
3
4
|
export function parseApplicationSurfaceBlock(block) {
|
|
4
5
|
const name = nameFrom(block.header);
|
|
@@ -6,26 +7,32 @@ export function parseApplicationSurfaceBlock(block) {
|
|
|
6
7
|
const role = readLine('role', block.body) ?? defaultRole(surfaceKind);
|
|
7
8
|
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body);
|
|
8
9
|
const sourceHash = readLine('sourceHash', block.body);
|
|
9
|
-
const
|
|
10
|
+
const rowIdentity = createRowIdentityTracker();
|
|
11
|
+
const evidence = [];
|
|
10
12
|
const records = [];
|
|
11
13
|
const proofGaps = [];
|
|
12
14
|
for (const rawLine of block.body.split('\n')) {
|
|
13
15
|
const line = rawLine.trim();
|
|
14
16
|
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
15
|
-
const match = /^(mount|provide|provides|required|requires|require|route|event|asset|gate|gap|proofGap)\s+([A-Za-z_$@/.*][\w$./@:*+-]*)(.*)$/.exec(line);
|
|
17
|
+
const match = /^(mount|provide|provides|required|requires|require|route|event|asset|gate|evidence|proofEvidence|gap|proofGap)\s+([A-Za-z_$@/.*][\w$./@:*+-]*)(.*)$/.exec(line);
|
|
16
18
|
if (!match) continue;
|
|
17
19
|
const [, rowKind, rowName, rest] = match;
|
|
20
|
+
if (rowKind === 'evidence' || rowKind === 'proofEvidence') {
|
|
21
|
+
rowIdentity.push(evidence, applicationEvidence(rowName, rest), { rowKind, normalizedRowKind: 'evidence', name: rowName });
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
18
24
|
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
19
|
-
|
|
25
|
+
rowIdentity.push(proofGaps, applicationProofGap(rowName, rest), { rowKind, normalizedRowKind: 'proofGap', name: rowName });
|
|
20
26
|
continue;
|
|
21
27
|
}
|
|
22
|
-
|
|
28
|
+
const normalizedRowKind = normalizeRowKind(rowKind);
|
|
29
|
+
rowIdentity.push(records, applicationRecord(normalizedRowKind, rowName, rest, {
|
|
23
30
|
surfaceId: idFrom(block.header, `application_surface_${safeId(name)}`),
|
|
24
31
|
surfaceName: name,
|
|
25
32
|
role,
|
|
26
33
|
sourcePath,
|
|
27
34
|
sourceHash
|
|
28
|
-
}));
|
|
35
|
+
}), { rowKind, normalizedRowKind, name: rowName });
|
|
29
36
|
}
|
|
30
37
|
const allGaps = [...records.flatMap((record) => record.proofGaps ?? []), ...proofGaps];
|
|
31
38
|
const tree = {
|
|
@@ -41,7 +48,7 @@ export function parseApplicationSurfaceBlock(block) {
|
|
|
41
48
|
records,
|
|
42
49
|
proofGaps: allGaps,
|
|
43
50
|
evidence,
|
|
44
|
-
parser: { status: 'authored', errors:
|
|
51
|
+
parser: { status: 'authored', errors: rowIdentity.errors },
|
|
45
52
|
claims: applicationFalseClaims(),
|
|
46
53
|
metadata: { authoredName: name, authoredBlockKind: block.kind }
|
|
47
54
|
};
|
|
@@ -200,22 +207,15 @@ function applicationProofGap(name, text) {
|
|
|
200
207
|
});
|
|
201
208
|
}
|
|
202
209
|
|
|
203
|
-
function
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
status: readInlineWord('status', match[2]) ?? 'unknown',
|
|
213
|
-
path: readInlineWord('path', match[2]),
|
|
214
|
-
summary: readInlineQuoted('summary', match[2]),
|
|
215
|
-
metadata: { name: match[1] }
|
|
216
|
-
}));
|
|
217
|
-
}
|
|
218
|
-
return records;
|
|
210
|
+
function applicationEvidence(name, text) {
|
|
211
|
+
return cleanRecord({
|
|
212
|
+
id: idFrom(text, `evidence_${name}`),
|
|
213
|
+
kind: readInlineWord('kind', text) ?? 'note',
|
|
214
|
+
status: readInlineWord('status', text) ?? 'unknown',
|
|
215
|
+
path: readInlineWord('path', text),
|
|
216
|
+
summary: readInlineQuoted('summary', text),
|
|
217
|
+
metadata: { name }
|
|
218
|
+
});
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
function summarizeApplicationSurface(tree) {
|
|
@@ -280,7 +280,7 @@ function defaultRole(surfaceKind) {
|
|
|
280
280
|
function count(records, kind) { return records.filter((record) => record.recordKind === kind).length; }
|
|
281
281
|
function ids(records = []) { return records.map((record) => record?.id).filter(Boolean); }
|
|
282
282
|
function idsByRecordKind(records = [], kind) { return ids(records.filter((record) => record?.recordKind === kind)); }
|
|
283
|
-
function isPropertyLine(line) { return /^(role|host|hostId|sourcePath|path|sourceHash
|
|
283
|
+
function isPropertyLine(line) { return /^(role|host|hostId|sourcePath|path|sourceHash)\s+/.test(line); }
|
|
284
284
|
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
285
285
|
function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'ApplicationSurface'; }
|
|
286
286
|
function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
|
package/dist/canvas-surface.js
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { createRowIdentityTracker } from './row-identity.js';
|
|
2
3
|
|
|
3
4
|
export function parseCanvasSurfaceBlock(block) {
|
|
4
5
|
const name = nameFrom(block.header);
|
|
5
6
|
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body);
|
|
6
7
|
const sourceHash = readLine('sourceHash', block.body);
|
|
7
|
-
const
|
|
8
|
+
const rowIdentity = createRowIdentityTracker();
|
|
9
|
+
const evidence = [];
|
|
8
10
|
const records = [];
|
|
9
11
|
const commandTraces = [];
|
|
10
12
|
const proofGaps = [];
|
|
11
13
|
for (const rawLine of block.body.split('\n')) {
|
|
12
14
|
const line = rawLine.trim();
|
|
13
15
|
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
14
|
-
const match = /^(element|command|state|stateWrite|trace|gap|proofGap)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
16
|
+
const match = /^(element|command|state|stateWrite|trace|evidence|proofEvidence|gap|proofGap)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
15
17
|
if (!match) continue;
|
|
16
18
|
const [, rowKind, rowName, rest] = match;
|
|
19
|
+
if (rowKind === 'evidence' || rowKind === 'proofEvidence') {
|
|
20
|
+
rowIdentity.push(evidence, canvasEvidence(rowName, rest), { rowKind, normalizedRowKind: 'evidence', name: rowName });
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
17
23
|
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
18
|
-
|
|
24
|
+
rowIdentity.push(proofGaps, canvasProofGap(rowName, rest), { rowKind, normalizedRowKind: 'proofGap', name: rowName });
|
|
19
25
|
continue;
|
|
20
26
|
}
|
|
21
27
|
if (rowKind === 'trace') {
|
|
22
|
-
|
|
28
|
+
rowIdentity.push(commandTraces, canvasTrace(rowName, rest, { sourcePath }), { rowKind, normalizedRowKind: 'trace', name: rowName });
|
|
23
29
|
continue;
|
|
24
30
|
}
|
|
25
|
-
|
|
31
|
+
const normalizedRowKind = rowKind === 'stateWrite' ? 'state-write' : rowKind;
|
|
32
|
+
rowIdentity.push(records, canvasRecord(normalizedRowKind, rowName, rest, { sourcePath, sourceHash }), { rowKind, normalizedRowKind, name: rowName });
|
|
26
33
|
}
|
|
27
34
|
const allGaps = [...records.flatMap((record) => record.proofGaps ?? []), ...proofGaps];
|
|
28
35
|
const tree = {
|
|
@@ -36,7 +43,7 @@ export function parseCanvasSurfaceBlock(block) {
|
|
|
36
43
|
commandTraces,
|
|
37
44
|
proofGaps: allGaps,
|
|
38
45
|
evidence,
|
|
39
|
-
parser: { status: 'authored', errors:
|
|
46
|
+
parser: { status: 'authored', errors: rowIdentity.errors },
|
|
40
47
|
claims: {
|
|
41
48
|
autoMergeClaim: false,
|
|
42
49
|
semanticEquivalenceClaim: false,
|
|
@@ -121,22 +128,15 @@ function canvasProofGap(name, text) {
|
|
|
121
128
|
});
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
status: readInlineWord('status', match[2]) ?? 'unknown',
|
|
134
|
-
path: readInlineWord('path', match[2]),
|
|
135
|
-
summary: readInlineQuoted('summary', match[2]),
|
|
136
|
-
metadata: { name: match[1] }
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
return records;
|
|
131
|
+
function canvasEvidence(name, text) {
|
|
132
|
+
return cleanRecord({
|
|
133
|
+
id: idFrom(text, `evidence_${name}`),
|
|
134
|
+
kind: readInlineWord('kind', text) ?? 'note',
|
|
135
|
+
status: readInlineWord('status', text) ?? 'unknown',
|
|
136
|
+
path: readInlineWord('path', text),
|
|
137
|
+
summary: readInlineQuoted('summary', text),
|
|
138
|
+
metadata: { name }
|
|
139
|
+
});
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
function summarizeCanvasSurface(tree) {
|
|
@@ -173,7 +173,7 @@ function hashableRecord(record) {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
function isPropertyLine(line) {
|
|
176
|
-
return /^(sourcePath|path|sourceHash
|
|
176
|
+
return /^(sourcePath|path|sourceHash)\s+/.test(line);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
package/dist/package-manifest.js
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { createRowIdentityTracker } from './row-identity.js';
|
|
2
3
|
|
|
3
4
|
export function parsePackageManifestBlock(block) {
|
|
4
5
|
const name = nameFrom(block.header);
|
|
5
6
|
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body) ?? 'package.json';
|
|
6
7
|
const sourceHash = readLine('sourceHash', block.body);
|
|
7
|
-
const
|
|
8
|
+
const rowIdentity = createRowIdentityTracker();
|
|
9
|
+
const evidence = [];
|
|
8
10
|
const records = [];
|
|
9
11
|
const proofGaps = [];
|
|
10
12
|
for (const rawLine of block.body.split('\n')) {
|
|
11
13
|
const line = rawLine.trim();
|
|
12
14
|
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
13
|
-
const match = /^(metadata|dependency|script|export|gap|proofGap)\s+([A-Za-z_$.*@/][\w$./@*-]*)(.*)$/.exec(line);
|
|
15
|
+
const match = /^(metadata|dependency|script|export|evidence|proofEvidence|gap|proofGap)\s+([A-Za-z_$.*@/][\w$./@*-]*)(.*)$/.exec(line);
|
|
14
16
|
if (!match) continue;
|
|
15
17
|
const [, rowKind, rowName, rest] = match;
|
|
18
|
+
if (rowKind === 'evidence' || rowKind === 'proofEvidence') {
|
|
19
|
+
rowIdentity.push(evidence, packageEvidence(rowName, rest), { rowKind, normalizedRowKind: 'evidence', name: rowName });
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
16
22
|
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
17
|
-
|
|
23
|
+
rowIdentity.push(proofGaps, packageProofGap(rowName, rest), { rowKind, normalizedRowKind: 'proofGap', name: rowName });
|
|
18
24
|
continue;
|
|
19
25
|
}
|
|
20
26
|
const record = packageRecord(rowKind, rowName, rest, { sourcePath, sourceHash, evidence });
|
|
21
|
-
|
|
27
|
+
rowIdentity.push(records, record, { rowKind, normalizedRowKind: rowKind, name: rowName });
|
|
22
28
|
}
|
|
23
29
|
const recordGaps = records.flatMap((record) => record.proofGaps ?? []);
|
|
24
30
|
const allGaps = [...recordGaps, ...proofGaps];
|
|
@@ -33,7 +39,7 @@ export function parsePackageManifestBlock(block) {
|
|
|
33
39
|
records,
|
|
34
40
|
proofGaps: allGaps,
|
|
35
41
|
evidence,
|
|
36
|
-
parser: { status: 'authored', errors:
|
|
42
|
+
parser: { status: 'authored', errors: rowIdentity.errors },
|
|
37
43
|
claims: {
|
|
38
44
|
autoMergeClaim: false,
|
|
39
45
|
semanticEquivalenceClaim: false,
|
|
@@ -99,22 +105,15 @@ function packageProofGap(name, text) {
|
|
|
99
105
|
});
|
|
100
106
|
}
|
|
101
107
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
status: readInlineWord('status', match[2]) ?? 'unknown',
|
|
112
|
-
path: readInlineWord('path', match[2]),
|
|
113
|
-
summary: readInlineQuoted('summary', match[2]),
|
|
114
|
-
metadata: { name: match[1] }
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
|
-
return records;
|
|
108
|
+
function packageEvidence(name, text) {
|
|
109
|
+
return cleanRecord({
|
|
110
|
+
id: idFrom(text, `evidence_${name}`),
|
|
111
|
+
kind: readInlineWord('kind', text) ?? 'note',
|
|
112
|
+
status: readInlineWord('status', text) ?? 'unknown',
|
|
113
|
+
path: readInlineWord('path', text),
|
|
114
|
+
summary: readInlineQuoted('summary', text),
|
|
115
|
+
metadata: { name }
|
|
116
|
+
});
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
function summarizePackageManifest(tree) {
|
|
@@ -133,7 +132,7 @@ function hashableRecord(record) {
|
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
function isPropertyLine(line) {
|
|
136
|
-
return /^(sourcePath|path|sourceHash|packageManager
|
|
135
|
+
return /^(sourcePath|path|sourceHash|packageManager)\s+/.test(line);
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
package/dist/resource-graph.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createRowIdentityTracker } from './row-identity.js';
|
|
2
|
+
|
|
1
3
|
const GROUPS = {
|
|
2
4
|
resource: 'resources',
|
|
3
5
|
owner: 'owners',
|
|
@@ -16,6 +18,7 @@ const GROUPS = {
|
|
|
16
18
|
|
|
17
19
|
export function parseResourceGraphBlock(block) {
|
|
18
20
|
const name = nameFrom(block.header);
|
|
21
|
+
const rowIdentity = createRowIdentityTracker();
|
|
19
22
|
const graph = {
|
|
20
23
|
kind: 'frontier.lang.semanticResourceGraph',
|
|
21
24
|
version: 1,
|
|
@@ -40,6 +43,7 @@ export function parseResourceGraphBlock(block) {
|
|
|
40
43
|
unsafeBoundaries: [],
|
|
41
44
|
conflicts: [],
|
|
42
45
|
proofObligations: [],
|
|
46
|
+
parser: { status: 'authored', errors: rowIdentity.errors },
|
|
43
47
|
claims: {
|
|
44
48
|
borrowCheckerClaim: false,
|
|
45
49
|
aliasSafetyClaim: false,
|
|
@@ -59,7 +63,7 @@ export function parseResourceGraphBlock(block) {
|
|
|
59
63
|
const normalized = normalizeRowKind(rowKind);
|
|
60
64
|
const record = parseResourceRecord(normalized, rowName, rest, graph);
|
|
61
65
|
const group = GROUPS[normalized];
|
|
62
|
-
if (record && group) graph[group]
|
|
66
|
+
if (record && group) rowIdentity.push(graph[group], record, { rowKind, normalizedRowKind: normalized, name: rowName });
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
graph.outlives = graph.lifetimeRelations;
|
|
@@ -133,6 +137,7 @@ function summarize(graph) {
|
|
|
133
137
|
conflicts: graph.conflicts.length,
|
|
134
138
|
proofObligations: graph.proofObligations.length,
|
|
135
139
|
unsafeBoundariesWithoutProof: graph.unsafeBoundaries.filter((record) => record.proofStatus !== 'passed').length,
|
|
140
|
+
parseErrors: graph.parser?.errors?.length ?? 0,
|
|
136
141
|
reasonCodes: unique(graph.conflicts.map((record) => record.reasonCode))
|
|
137
142
|
};
|
|
138
143
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function createRowIdentityTracker(options = {}) {
|
|
2
|
+
const seenIds = new Map();
|
|
3
|
+
const errors = [];
|
|
4
|
+
const code = options.code ?? 'duplicate-generic-row-id';
|
|
5
|
+
|
|
6
|
+
function accept(record, context = {}) {
|
|
7
|
+
if (!record?.id) return true;
|
|
8
|
+
const first = seenIds.get(record.id);
|
|
9
|
+
if (first) {
|
|
10
|
+
errors.push(duplicateRowIdError(record, context, first, code, errors.length, true));
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
seenIds.set(record.id, rowIdentity(record, context));
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function preserve(record, context = {}) {
|
|
18
|
+
if (!record?.id) return true;
|
|
19
|
+
const first = seenIds.get(record.id);
|
|
20
|
+
if (first) {
|
|
21
|
+
errors.push(duplicateRowIdError(record, context, first, code, errors.length, false));
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
seenIds.set(record.id, rowIdentity(record, context));
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
errors,
|
|
30
|
+
accept,
|
|
31
|
+
preserve,
|
|
32
|
+
push(target, record, context = {}) {
|
|
33
|
+
if (record && accept(record, context)) target.push(record);
|
|
34
|
+
return record;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function duplicateRowIdError(record, context, first, code, index, suppressed) {
|
|
40
|
+
const current = rowIdentity(record, context);
|
|
41
|
+
return cleanRecord({
|
|
42
|
+
id: `parser_error_${safeId(code)}_${safeId(record.id)}_${index}`,
|
|
43
|
+
code,
|
|
44
|
+
reason: code,
|
|
45
|
+
severity: 'error',
|
|
46
|
+
failClosed: true,
|
|
47
|
+
action: suppressed ? 'ignored-duplicate-row' : 'preserved-duplicate-row',
|
|
48
|
+
disposition: context.disposition,
|
|
49
|
+
suppressed,
|
|
50
|
+
rowId: record.id,
|
|
51
|
+
nodeId: record.id,
|
|
52
|
+
rowKind: current.rowKind,
|
|
53
|
+
normalizedRowKind: current.normalizedRowKind,
|
|
54
|
+
name: current.name,
|
|
55
|
+
sourceSpan: current.sourceSpan,
|
|
56
|
+
firstRowKind: first.rowKind,
|
|
57
|
+
firstNormalizedRowKind: first.normalizedRowKind,
|
|
58
|
+
firstName: first.name,
|
|
59
|
+
firstSourceSpan: first.sourceSpan,
|
|
60
|
+
message: suppressed
|
|
61
|
+
? `Duplicate authored row id "${record.id}" was ignored.`
|
|
62
|
+
: `Duplicate authored row id "${record.id}" was preserved.`
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function rowIdentity(record, context = {}) {
|
|
67
|
+
return cleanRecord({
|
|
68
|
+
rowKind: context.rowKind ?? record.rowKind ?? record.recordKind ?? record.kind,
|
|
69
|
+
normalizedRowKind: context.normalizedRowKind ?? record.normalizedRowKind ?? record.recordKind ?? record.kind,
|
|
70
|
+
name: context.name ?? record.name,
|
|
71
|
+
sourceSpan: context.sourceSpan ?? record.sourceSpan
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function cleanRecord(record) {
|
|
76
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function safeId(value) {
|
|
80
|
+
return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
|
|
81
|
+
}
|
|
@@ -9,7 +9,8 @@ export function summarizeRuntimeCapabilities(matrix) {
|
|
|
9
9
|
capabilityCount: matrix.hostProfiles.reduce((total, profile) => total + Object.keys(profile.capabilities ?? {}).length, 0),
|
|
10
10
|
runtimeRequirementCount: matrix.runtimeRequirements.length,
|
|
11
11
|
evidenceCount: matrix.evidence.length,
|
|
12
|
-
proofGapCount: matrix.proofGaps.length
|
|
12
|
+
proofGapCount: matrix.proofGaps.length,
|
|
13
|
+
parseErrors: matrix.parser?.errors?.length ?? 0
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { createRowIdentityTracker } from './row-identity.js';
|
|
2
3
|
import {
|
|
3
4
|
cleanRecord,
|
|
4
5
|
hostIds,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
|
|
22
23
|
export function parseRuntimeCapabilityBlock(block) {
|
|
23
24
|
const name = nameFrom(block.header);
|
|
25
|
+
const rowIdentity = createRowIdentityTracker();
|
|
24
26
|
const matrix = {
|
|
25
27
|
kind: 'frontier.lang.authoredRuntimeCapabilityMatrixInput',
|
|
26
28
|
version: 1,
|
|
@@ -34,6 +36,7 @@ export function parseRuntimeCapabilityBlock(block) {
|
|
|
34
36
|
runtimeRequirements: [],
|
|
35
37
|
evidence: [],
|
|
36
38
|
proofGaps: [],
|
|
39
|
+
parser: { status: 'authored', errors: rowIdentity.errors },
|
|
37
40
|
claims: runtimeFalseClaims(),
|
|
38
41
|
metadata: { authoredName: name, authoredBlockKind: block.kind }
|
|
39
42
|
};
|
|
@@ -46,26 +49,35 @@ export function parseRuntimeCapabilityBlock(block) {
|
|
|
46
49
|
if (!match) continue;
|
|
47
50
|
const [, rowKind, rowName, rest] = match;
|
|
48
51
|
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
49
|
-
matrix.proofGaps
|
|
52
|
+
rowIdentity.push(matrix.proofGaps, runtimeProofGap(rowName, rest), { rowKind, normalizedRowKind: 'proofGap', name: rowName });
|
|
50
53
|
continue;
|
|
51
54
|
}
|
|
52
55
|
if (rowKind === 'evidence' || rowKind === 'proofEvidence') {
|
|
53
|
-
matrix.evidence
|
|
56
|
+
rowIdentity.push(matrix.evidence, runtimeEvidence(rowName, rest), { rowKind, normalizedRowKind: 'evidence', name: rowName });
|
|
54
57
|
continue;
|
|
55
58
|
}
|
|
56
59
|
if (rowKind === 'requirement' || rowKind === 'runtimeRequirement' || rowKind === 'requiredRuntime') {
|
|
57
|
-
matrix.runtimeRequirements
|
|
60
|
+
rowIdentity.push(matrix.runtimeRequirements, runtimeRequirement(rowName, rest), { rowKind, normalizedRowKind: 'runtimeRequirement', name: rowName });
|
|
58
61
|
continue;
|
|
59
62
|
}
|
|
60
63
|
if (rowKind === 'capability' || rowKind === 'hostCapability') {
|
|
61
|
-
|
|
64
|
+
const capabilityRecord = runtimeHostCapability(rowName, rest);
|
|
65
|
+
if (capabilityRecord && rowIdentity.accept(capabilityRecord, { rowKind, normalizedRowKind: 'hostCapability', name: rowName })) {
|
|
66
|
+
attachCapability(hostMap, matrix, capabilityRecord, rest);
|
|
67
|
+
}
|
|
62
68
|
continue;
|
|
63
69
|
}
|
|
64
70
|
if (rowKind === 'hostBinding' || rowKind === 'binding') {
|
|
65
|
-
matrix.hostBindings
|
|
71
|
+
rowIdentity.push(matrix.hostBindings, runtimeHostBinding(rowName, rest), { rowKind, normalizedRowKind: 'hostBinding', name: rowName });
|
|
66
72
|
continue;
|
|
67
73
|
}
|
|
68
74
|
const profile = runtimeHostProfile(rowKind, rowName, rest);
|
|
75
|
+
rowIdentity.preserve(profile, {
|
|
76
|
+
rowKind,
|
|
77
|
+
normalizedRowKind: 'hostProfile',
|
|
78
|
+
name: rowName,
|
|
79
|
+
disposition: 'preserved-runtime-host-profile-upsert'
|
|
80
|
+
});
|
|
69
81
|
upsertHost(hostMap, matrix, profile, rowKind);
|
|
70
82
|
}
|
|
71
83
|
|
|
@@ -115,6 +127,7 @@ export function mergeRuntimeCapabilityBlocks(blocks) {
|
|
|
115
127
|
const runtimeRequirements = uniqueRecords(blocks.flatMap((block) => block.runtimeRequirements ?? []));
|
|
116
128
|
const evidence = uniqueRecords(blocks.flatMap((block) => block.evidence ?? []));
|
|
117
129
|
const proofGaps = uniqueRecords(blocks.flatMap((block) => block.proofGaps ?? []));
|
|
130
|
+
const parser = { status: 'authored', errors: blocks.flatMap((block) => block.parser?.errors ?? []) };
|
|
118
131
|
return {
|
|
119
132
|
kind: 'frontier.lang.authoredRuntimeCapabilityMatrixInput',
|
|
120
133
|
version: 1,
|
|
@@ -129,6 +142,7 @@ export function mergeRuntimeCapabilityBlocks(blocks) {
|
|
|
129
142
|
runtimeRequirements,
|
|
130
143
|
evidence,
|
|
131
144
|
proofGaps,
|
|
145
|
+
parser,
|
|
132
146
|
hostProfileIds: ids(hostProfiles),
|
|
133
147
|
sourceHostIds: hostIds(sourceHosts),
|
|
134
148
|
targetHostIds: hostIds(targetHosts),
|
|
@@ -137,7 +151,7 @@ export function mergeRuntimeCapabilityBlocks(blocks) {
|
|
|
137
151
|
runtimeRequirementIds: ids(runtimeRequirements),
|
|
138
152
|
evidenceIds: ids(evidence),
|
|
139
153
|
proofGapCodes: [...new Set(proofGaps.map((gap) => gap.code).filter(Boolean))],
|
|
140
|
-
summary: summarizeRuntimeCapabilities({ hostProfiles, sourceHosts, targetHosts, hostCapabilities, hostBindings, runtimeRequirements, evidence, proofGaps }),
|
|
154
|
+
summary: summarizeRuntimeCapabilities({ hostProfiles, sourceHosts, targetHosts, hostCapabilities, hostBindings, runtimeRequirements, evidence, proofGaps, parser }),
|
|
141
155
|
claims: runtimeFalseClaims(),
|
|
142
156
|
metadata: { authoredRuntimeCapabilityBlockIds: ids(blocks) }
|
|
143
157
|
};
|
|
@@ -177,13 +191,32 @@ function upsertHost(hostMap, matrix, profile, rowKind) {
|
|
|
177
191
|
if (rowKind === 'targetHost' || merged.role === 'target') matrix.targetHosts.push(merged.id);
|
|
178
192
|
}
|
|
179
193
|
|
|
180
|
-
function attachCapability(hostMap, matrix,
|
|
181
|
-
const hostId =
|
|
182
|
-
if (!hostId) return;
|
|
194
|
+
function attachCapability(hostMap, matrix, capabilityRecord, text) {
|
|
195
|
+
const hostId = capabilityRecord.hostId;
|
|
183
196
|
const profile = hostMap.get(hostId) ?? runtimeHostProfile('host', hostId, '');
|
|
197
|
+
const capability = capabilityRecord.capability;
|
|
198
|
+
const normalized = cleanRecord({
|
|
199
|
+
kind: capability,
|
|
200
|
+
support: capabilityRecord.support,
|
|
201
|
+
binding: capabilityRecord.binding ?? `${hostId}.${capability}`,
|
|
202
|
+
notes: readInlineList(text, 'note', 'notes'),
|
|
203
|
+
evidenceIds: capabilityRecord.evidenceIds,
|
|
204
|
+
sourcePath: capabilityRecord.sourcePath,
|
|
205
|
+
sourceHash: capabilityRecord.sourceHash
|
|
206
|
+
});
|
|
207
|
+
profile.capabilities = { ...(profile.capabilities ?? {}), [capability]: normalized };
|
|
208
|
+
hostMap.set(hostId, profile);
|
|
209
|
+
matrix.hostCapabilities.push(capabilityRecord);
|
|
210
|
+
if (readInlineFlag('source', text) || readInlineWord('role', text) === 'source') matrix.sourceHosts.push(hostId);
|
|
211
|
+
if (readInlineFlag('target', text) || readInlineWord('role', text) === 'target') matrix.targetHosts.push(hostId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function runtimeHostCapability(name, text) {
|
|
215
|
+
const hostId = readInlineWord('host', text) ?? readInlineWord('hostId', text) ?? readInlineWord('sourceHost', text) ?? readInlineWord('targetHost', text);
|
|
216
|
+
if (!hostId) return undefined;
|
|
184
217
|
const capability = readInlineWord('capability', text) ?? readInlineWord('kind', text) ?? name;
|
|
185
218
|
const bindingId = readInlineWord('bindingId', text) ?? readInlineWord('binding', text);
|
|
186
|
-
|
|
219
|
+
return cleanRecord({
|
|
187
220
|
kind: 'frontier.lang.runtimeCapability.hostCapability',
|
|
188
221
|
id: idFrom(text, `runtime_capability_${safeId(hostId)}_${safeId(capability)}`),
|
|
189
222
|
name,
|
|
@@ -204,20 +237,6 @@ function attachCapability(hostMap, matrix, name, text) {
|
|
|
204
237
|
failClosed: true,
|
|
205
238
|
claims: runtimeFalseClaims()
|
|
206
239
|
});
|
|
207
|
-
const normalized = cleanRecord({
|
|
208
|
-
kind: capability,
|
|
209
|
-
support: capabilityRecord.support,
|
|
210
|
-
binding: capabilityRecord.binding ?? `${hostId}.${capability}`,
|
|
211
|
-
notes: readInlineList(text, 'note', 'notes'),
|
|
212
|
-
evidenceIds: capabilityRecord.evidenceIds,
|
|
213
|
-
sourcePath: capabilityRecord.sourcePath,
|
|
214
|
-
sourceHash: capabilityRecord.sourceHash
|
|
215
|
-
});
|
|
216
|
-
profile.capabilities = { ...(profile.capabilities ?? {}), [capability]: normalized };
|
|
217
|
-
hostMap.set(hostId, profile);
|
|
218
|
-
matrix.hostCapabilities.push(capabilityRecord);
|
|
219
|
-
if (readInlineFlag('source', text) || readInlineWord('role', text) === 'source') matrix.sourceHosts.push(hostId);
|
|
220
|
-
if (readInlineFlag('target', text) || readInlineWord('role', text) === 'target') matrix.targetHosts.push(hostId);
|
|
221
240
|
}
|
|
222
241
|
|
|
223
242
|
function runtimeHostBinding(name, text) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapeshift-labs/frontier-lang-parser",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.65",
|
|
4
4
|
"description": "Parser for the first Frontier Lang .frontier syntax slice.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "node scripts/build.mjs",
|
|
25
|
-
"test": "npm run build && node test/smoke.mjs && node test/member-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
25
|
+
"test": "npm run build && node test/smoke.mjs && node test/member-identity-smoke.mjs && node test/generic-row-parse-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
26
26
|
"typecheck": "node ./node_modules/typescript/bin/tsc --noEmit -p test/tsconfig.json",
|
|
27
27
|
"fuzz": "npm run build && node fuzz/smoke.mjs",
|
|
28
28
|
"bench": "npm run build && node bench/smoke.mjs",
|