@shapeshift-labs/frontier-lang-parser 0.3.28 → 0.3.29
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 +43 -0
- package/dist/application-surface.js +303 -0
- package/dist/index.js +5 -2
- package/dist/metadata.js +10 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -309,6 +309,49 @@ Runtime probes, package-manager solver output, lockfile proof, browser pixels,
|
|
|
309
309
|
worker transfer evidence, and GPU validation must still be supplied by higher
|
|
310
310
|
layers before admission.
|
|
311
311
|
|
|
312
|
+
## Authored application and plugin syntax
|
|
313
|
+
|
|
314
|
+
`.frontier` files can describe application composition boundaries directly.
|
|
315
|
+
These blocks make host/plugin contracts source-level objects: mount points,
|
|
316
|
+
provided surfaces, required capabilities, routes, events, assets, gates, and
|
|
317
|
+
proof gaps can be queried by projection and merge tooling without collapsing
|
|
318
|
+
them into a target-language implementation detail.
|
|
319
|
+
|
|
320
|
+
```frontier
|
|
321
|
+
appHost WorkbenchHost @id("app_surface_workbench") {
|
|
322
|
+
role host
|
|
323
|
+
sourcePath app.frontier
|
|
324
|
+
sourceHash sha256:host
|
|
325
|
+
evidence previewProbe @id("evidence_preview_probe") kind browser-probe status passed path reports/preview.json
|
|
326
|
+
mount dashboard @id("app_mount_dashboard") kind region path /dashboard view view_dashboard target react evidence evidence_preview_probe
|
|
327
|
+
provides shell @id("app_provide_shell") surface view view view_dashboard mount app_mount_dashboard capability host.fetch|host.storage evidence evidence_preview_probe
|
|
328
|
+
requires fetch @id("app_require_fetch") capability host.fetch category network permission network proofGap app-host-capability-adapter-boundary evidence evidence_preview_probe
|
|
329
|
+
route dashboard @id("app_route_dashboard") path /dashboard view view_dashboard action action_refresh mount app_mount_dashboard evidence evidence_preview_probe
|
|
330
|
+
gate preview @id("app_gate_preview") kind browser-probe command "npm run preview:probe" required subject view_dashboard evidence evidence_preview_probe
|
|
331
|
+
gap pluginAbi @id("app_gap_plugin_abi") code plugin-abi-compatibility-boundary summary "Plugin ABI requires host/runtime compatibility proof."
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
plugin WeatherWidget @id("plugin_weather_widget") {
|
|
335
|
+
role plugin
|
|
336
|
+
host app_surface_workbench
|
|
337
|
+
sourcePath plugins/weather.frontier
|
|
338
|
+
sourceHash sha256:plugin
|
|
339
|
+
evidence sandboxProbe @id("evidence_sandbox_probe") kind sandbox-probe status passed path reports/sandbox.json
|
|
340
|
+
provides weatherPanel @id("plugin_provide_weather") surface view view view_weather_panel mount app_mount_dashboard capability host.fetch proofGap plugin-projection-runtime-boundary evidence evidence_sandbox_probe
|
|
341
|
+
requires fetch @id("plugin_require_fetch") capability host.fetch category network permission network adapter host_fetch_adapter proofGap plugin-capability-grant-boundary evidence evidence_sandbox_probe
|
|
342
|
+
gate sandbox @id("plugin_gate_sandbox") kind sandbox command "npm run sandbox:probe" required subject view_weather_panel evidence evidence_sandbox_probe
|
|
343
|
+
gap sandbox @id("plugin_gap_sandbox") code plugin-sandbox-safety-boundary summary "Sandbox safety requires runtime proof."
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The parser stores these blocks in `metadata.applicationSurfaces` and mirrors
|
|
348
|
+
them under `metadata.universalAst.applicationSurfaces`. They are composition
|
|
349
|
+
contracts, not compatibility proof: `runtimeEquivalenceClaim`,
|
|
350
|
+
`abiCompatibilityClaim`, `projectionEquivalenceClaim`,
|
|
351
|
+
`pluginCompatibilityClaim`, and `sandboxSafetyClaim` remain false until a
|
|
352
|
+
higher layer supplies source-bound runtime, ABI, projection, and sandbox
|
|
353
|
+
evidence.
|
|
354
|
+
|
|
312
355
|
## Authored target projection syntax
|
|
313
356
|
|
|
314
357
|
`.frontier` target blocks can carry projection contracts next to their emit settings. These rows describe what a target lowering claims to represent, what it still needs proof for, and which losses or missing evidence must stay visible to merge and translation tooling.
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
|
|
3
|
+
export function parseApplicationSurfaceBlock(block) {
|
|
4
|
+
const name = nameFrom(block.header);
|
|
5
|
+
const surfaceKind = surfaceKindFrom(block.kind);
|
|
6
|
+
const role = readLine('role', block.body) ?? defaultRole(surfaceKind);
|
|
7
|
+
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body);
|
|
8
|
+
const sourceHash = readLine('sourceHash', block.body);
|
|
9
|
+
const evidence = parseEvidenceRows(block.body);
|
|
10
|
+
const records = [];
|
|
11
|
+
const proofGaps = [];
|
|
12
|
+
for (const rawLine of block.body.split('\n')) {
|
|
13
|
+
const line = rawLine.trim();
|
|
14
|
+
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);
|
|
16
|
+
if (!match) continue;
|
|
17
|
+
const [, rowKind, rowName, rest] = match;
|
|
18
|
+
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
19
|
+
proofGaps.push(applicationProofGap(rowName, rest));
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
records.push(applicationRecord(normalizeRowKind(rowKind), rowName, rest, {
|
|
23
|
+
surfaceId: idFrom(block.header, `application_surface_${safeId(name)}`),
|
|
24
|
+
surfaceName: name,
|
|
25
|
+
role,
|
|
26
|
+
sourcePath,
|
|
27
|
+
sourceHash
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
const allGaps = [...records.flatMap((record) => record.proofGaps ?? []), ...proofGaps];
|
|
31
|
+
const tree = {
|
|
32
|
+
kind: 'frontier.lang.applicationSurface',
|
|
33
|
+
version: 1,
|
|
34
|
+
id: idFrom(block.header, `application_surface_${safeId(name)}`),
|
|
35
|
+
name,
|
|
36
|
+
surfaceKind,
|
|
37
|
+
role,
|
|
38
|
+
hostId: readLine('host', block.body) ?? readLine('hostId', block.body),
|
|
39
|
+
sourcePath,
|
|
40
|
+
sourceHash,
|
|
41
|
+
records,
|
|
42
|
+
proofGaps: allGaps,
|
|
43
|
+
evidence,
|
|
44
|
+
parser: { status: 'authored', errors: [] },
|
|
45
|
+
claims: applicationFalseClaims(),
|
|
46
|
+
metadata: { authoredName: name, authoredBlockKind: block.kind }
|
|
47
|
+
};
|
|
48
|
+
tree.summary = summarizeApplicationSurface(tree);
|
|
49
|
+
tree.treeHash = hashSemanticValue({
|
|
50
|
+
kind: 'frontier.lang.applicationSurface.authoredTree.v1',
|
|
51
|
+
role,
|
|
52
|
+
records: records.map(hashableRecord),
|
|
53
|
+
proofGaps: allGaps.map((gap) => gap.code)
|
|
54
|
+
});
|
|
55
|
+
return tree;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function mergeApplicationSurfaceBlocks(blocks) {
|
|
59
|
+
const records = blocks.flatMap((block) => block.records ?? []);
|
|
60
|
+
return {
|
|
61
|
+
id: blocks.length === 1 ? blocks[0].id : 'applicationSurfaces:source',
|
|
62
|
+
surfaces: blocks,
|
|
63
|
+
surfaceIds: ids(blocks),
|
|
64
|
+
recordIds: ids(records),
|
|
65
|
+
mountIds: idsByRecordKind(records, 'mount'),
|
|
66
|
+
providedSurfaceIds: idsByRecordKind(records, 'provided-surface'),
|
|
67
|
+
requiredCapabilityIds: idsByRecordKind(records, 'required-capability'),
|
|
68
|
+
routeIds: idsByRecordKind(records, 'route'),
|
|
69
|
+
eventIds: idsByRecordKind(records, 'event'),
|
|
70
|
+
assetIds: idsByRecordKind(records, 'asset'),
|
|
71
|
+
gateIds: idsByRecordKind(records, 'gate'),
|
|
72
|
+
evidenceIds: blocks.flatMap((block) => ids(block.evidence)),
|
|
73
|
+
proofGapCodes: [...new Set(blocks.flatMap((block) => (block.proofGaps ?? []).map((gap) => gap.code).filter(Boolean)))],
|
|
74
|
+
summary: {
|
|
75
|
+
surfaceCount: blocks.length,
|
|
76
|
+
recordCount: records.length,
|
|
77
|
+
mountCount: blocks.reduce((total, block) => total + (block.summary?.mountCount ?? 0), 0),
|
|
78
|
+
providedSurfaceCount: blocks.reduce((total, block) => total + (block.summary?.providedSurfaceCount ?? 0), 0),
|
|
79
|
+
requiredCapabilityCount: blocks.reduce((total, block) => total + (block.summary?.requiredCapabilityCount ?? 0), 0),
|
|
80
|
+
routeCount: blocks.reduce((total, block) => total + (block.summary?.routeCount ?? 0), 0),
|
|
81
|
+
eventCount: blocks.reduce((total, block) => total + (block.summary?.eventCount ?? 0), 0),
|
|
82
|
+
assetCount: blocks.reduce((total, block) => total + (block.summary?.assetCount ?? 0), 0),
|
|
83
|
+
gateCount: blocks.reduce((total, block) => total + (block.summary?.gateCount ?? 0), 0),
|
|
84
|
+
proofGapCount: blocks.reduce((total, block) => total + (block.proofGaps?.length ?? 0), 0)
|
|
85
|
+
},
|
|
86
|
+
claims: applicationFalseClaims(),
|
|
87
|
+
metadata: { authoredApplicationSurfaceIds: ids(blocks) }
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function applicationRecord(kind, name, text, context) {
|
|
92
|
+
const recordName = readInlineWord('name', text) ?? name;
|
|
93
|
+
const proofGaps = readInlineList(text, 'proofGap', 'proofGaps', 'gap', 'gaps')?.map((code) => applicationProofGap(code, '')) ?? [];
|
|
94
|
+
const base = {
|
|
95
|
+
kind: 'frontier.lang.applicationSurface.record',
|
|
96
|
+
recordKind: kind,
|
|
97
|
+
id: idFrom(text, `application_${kind.replace(/-/g, '_')}_${safeId(recordName)}`),
|
|
98
|
+
name: recordName,
|
|
99
|
+
surfaceId: context.surfaceId,
|
|
100
|
+
surfaceName: context.surfaceName,
|
|
101
|
+
role: context.role,
|
|
102
|
+
identityKey: readInlineWord('identity', text) ?? readInlineWord('identityKey', text) ?? `${kind}:${context.role}:${recordName}`,
|
|
103
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
104
|
+
sourceHash: readInlineWord('sourceHash', text) ?? context.sourceHash,
|
|
105
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text)),
|
|
106
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
107
|
+
proofEvidenceIds: readInlineList(text, 'proofEvidence', 'proofEvidenceIds'),
|
|
108
|
+
missingEvidence: readInlineList(text, 'missingEvidence'),
|
|
109
|
+
proofGaps,
|
|
110
|
+
claims: applicationFalseClaims()
|
|
111
|
+
};
|
|
112
|
+
if (kind === 'mount') return cleanRecord({
|
|
113
|
+
...base,
|
|
114
|
+
mountKind: readInlineWord('kind', text) ?? readInlineWord('mountKind', text) ?? 'region',
|
|
115
|
+
path: readInlineQuoted('path', text) ?? readInlineWord('path', text),
|
|
116
|
+
slot: readInlineWord('slot', text),
|
|
117
|
+
parentId: readInlineWord('parent', text) ?? readInlineWord('parentId', text),
|
|
118
|
+
viewId: readInlineWord('view', text) ?? readInlineWord('viewId', text),
|
|
119
|
+
target: readInlineWord('target', text),
|
|
120
|
+
renderer: readInlineWord('renderer', text)
|
|
121
|
+
});
|
|
122
|
+
if (kind === 'provided-surface') return cleanRecord({
|
|
123
|
+
...base,
|
|
124
|
+
surfaceKind: readInlineWord('surface', text) ?? readInlineWord('surfaceKind', text) ?? readInlineWord('kind', text) ?? 'view',
|
|
125
|
+
viewId: readInlineWord('view', text) ?? readInlineWord('viewId', text),
|
|
126
|
+
actionId: readInlineWord('action', text) ?? readInlineWord('actionId', text),
|
|
127
|
+
effectId: readInlineWord('effect', text) ?? readInlineWord('effectId', text),
|
|
128
|
+
component: readInlineWord('component', text),
|
|
129
|
+
mountId: readInlineWord('mount', text) ?? readInlineWord('mountId', text),
|
|
130
|
+
routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
|
|
131
|
+
capabilityIds: readInlineList(text, 'capability', 'capabilities', 'capabilityIds'),
|
|
132
|
+
target: readInlineWord('target', text),
|
|
133
|
+
language: readInlineWord('language', text),
|
|
134
|
+
contractId: readInlineWord('contract', text) ?? readInlineWord('contractId', text)
|
|
135
|
+
});
|
|
136
|
+
if (kind === 'required-capability') return cleanRecord({
|
|
137
|
+
...base,
|
|
138
|
+
capability: readInlineWord('capability', text) ?? readInlineWord('capabilityId', text) ?? recordName,
|
|
139
|
+
category: readInlineWord('category', text),
|
|
140
|
+
permission: readInlineWord('permission', text),
|
|
141
|
+
adapterId: readInlineWord('adapter', text) ?? readInlineWord('adapterId', text),
|
|
142
|
+
hostId: readInlineWord('host', text) ?? readInlineWord('hostId', text),
|
|
143
|
+
runtime: readInlineWord('runtime', text),
|
|
144
|
+
target: readInlineWord('target', text)
|
|
145
|
+
});
|
|
146
|
+
if (kind === 'route') return cleanRecord({
|
|
147
|
+
...base,
|
|
148
|
+
path: readInlineQuoted('path', text) ?? readInlineWord('path', text) ?? recordName,
|
|
149
|
+
method: readInlineWord('method', text),
|
|
150
|
+
viewId: readInlineWord('view', text) ?? readInlineWord('viewId', text),
|
|
151
|
+
actionId: readInlineWord('action', text) ?? readInlineWord('actionId', text),
|
|
152
|
+
mountId: readInlineWord('mount', text) ?? readInlineWord('mountId', text),
|
|
153
|
+
paramIds: readInlineList(text, 'param', 'params', 'paramIds')
|
|
154
|
+
});
|
|
155
|
+
if (kind === 'event') return cleanRecord({
|
|
156
|
+
...base,
|
|
157
|
+
eventKind: readInlineWord('kind', text) ?? readInlineWord('eventKind', text),
|
|
158
|
+
eventName: readInlineWord('event', text) ?? readInlineWord('eventName', text) ?? recordName,
|
|
159
|
+
actionId: readInlineWord('action', text) ?? readInlineWord('actionId', text),
|
|
160
|
+
input: readInlineWord('input', text),
|
|
161
|
+
sourceId: readInlineWord('source', text) ?? readInlineWord('sourceId', text),
|
|
162
|
+
targetId: readInlineWord('target', text) ?? readInlineWord('targetId', text)
|
|
163
|
+
});
|
|
164
|
+
if (kind === 'asset') return cleanRecord({
|
|
165
|
+
...base,
|
|
166
|
+
assetKind: readInlineWord('kind', text) ?? readInlineWord('assetKind', text),
|
|
167
|
+
path: readInlineQuoted('path', text) ?? readInlineWord('path', text),
|
|
168
|
+
assetHash: readInlineWord('hash', text) ?? readInlineWord('assetHash', text),
|
|
169
|
+
integrity: readInlineWord('integrity', text),
|
|
170
|
+
runtime: readInlineWord('runtime', text),
|
|
171
|
+
mountId: readInlineWord('mount', text) ?? readInlineWord('mountId', text)
|
|
172
|
+
});
|
|
173
|
+
if (kind === 'gate') return cleanRecord({
|
|
174
|
+
...base,
|
|
175
|
+
gateKind: readInlineWord('kind', text) ?? readInlineWord('gateKind', text) ?? 'test',
|
|
176
|
+
command: readInlineQuoted('command', text) ?? readInlineWord('command', text),
|
|
177
|
+
required: readInlineFlag('required', text) ?? undefined,
|
|
178
|
+
status: readInlineWord('status', text),
|
|
179
|
+
subjectIds: readInlineList(text, 'subject', 'subjects', 'subjectIds'),
|
|
180
|
+
runtime: readInlineWord('runtime', text)
|
|
181
|
+
});
|
|
182
|
+
return cleanRecord(base);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function applicationProofGap(name, text) {
|
|
186
|
+
const code = readInlineWord('code', text) ?? name;
|
|
187
|
+
return cleanRecord({
|
|
188
|
+
id: idFrom(text, `application_gap_${safeId(code)}`),
|
|
189
|
+
code,
|
|
190
|
+
status: readInlineWord('status', text) ?? 'not-claimed',
|
|
191
|
+
summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
|
|
192
|
+
failClosed: true,
|
|
193
|
+
...applicationFalseClaims(),
|
|
194
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text))
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function parseEvidenceRows(body) {
|
|
199
|
+
const records = [];
|
|
200
|
+
for (const rawLine of body.split('\n')) {
|
|
201
|
+
const line = rawLine.trim();
|
|
202
|
+
const match = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
203
|
+
if (!match) continue;
|
|
204
|
+
records.push(cleanRecord({
|
|
205
|
+
id: idFrom(match[2], `evidence_${match[1]}`),
|
|
206
|
+
kind: readInlineWord('kind', match[2]) ?? 'note',
|
|
207
|
+
status: readInlineWord('status', match[2]) ?? 'unknown',
|
|
208
|
+
path: readInlineWord('path', match[2]),
|
|
209
|
+
summary: readInlineQuoted('summary', match[2]),
|
|
210
|
+
metadata: { name: match[1] }
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
return records;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function summarizeApplicationSurface(tree) {
|
|
217
|
+
return {
|
|
218
|
+
recordCount: tree.records.length,
|
|
219
|
+
mountCount: count(tree.records, 'mount'),
|
|
220
|
+
providedSurfaceCount: count(tree.records, 'provided-surface'),
|
|
221
|
+
requiredCapabilityCount: count(tree.records, 'required-capability'),
|
|
222
|
+
routeCount: count(tree.records, 'route'),
|
|
223
|
+
eventCount: count(tree.records, 'event'),
|
|
224
|
+
assetCount: count(tree.records, 'asset'),
|
|
225
|
+
gateCount: count(tree.records, 'gate'),
|
|
226
|
+
evidenceCount: tree.evidence.length,
|
|
227
|
+
proofGapCount: tree.proofGaps.length,
|
|
228
|
+
parseErrors: tree.parser.errors.length
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function applicationFalseClaims() {
|
|
233
|
+
return {
|
|
234
|
+
autoMergeClaim: false,
|
|
235
|
+
semanticEquivalenceClaim: false,
|
|
236
|
+
runtimeEquivalenceClaim: false,
|
|
237
|
+
abiCompatibilityClaim: false,
|
|
238
|
+
projectionEquivalenceClaim: false,
|
|
239
|
+
pluginCompatibilityClaim: false,
|
|
240
|
+
sandboxSafetyClaim: false
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function hashableRecord(record) {
|
|
245
|
+
return {
|
|
246
|
+
recordKind: record.recordKind,
|
|
247
|
+
name: record.name,
|
|
248
|
+
identityKey: record.identityKey,
|
|
249
|
+
capability: record.capability,
|
|
250
|
+
path: record.path,
|
|
251
|
+
viewId: record.viewId,
|
|
252
|
+
actionId: record.actionId,
|
|
253
|
+
proofGaps: record.proofGaps?.map((gap) => gap.code)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function normalizeRowKind(kind) {
|
|
258
|
+
if (kind === 'provide' || kind === 'provides') return 'provided-surface';
|
|
259
|
+
if (kind === 'require' || kind === 'requires' || kind === 'required') return 'required-capability';
|
|
260
|
+
return kind;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function surfaceKindFrom(kind) {
|
|
264
|
+
if (kind === 'appHost') return 'host';
|
|
265
|
+
if (kind === 'plugin' || kind === 'pluginSurface' || kind === 'pluginContract') return 'plugin';
|
|
266
|
+
return 'application';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function defaultRole(surfaceKind) {
|
|
270
|
+
if (surfaceKind === 'host') return 'host';
|
|
271
|
+
if (surfaceKind === 'plugin') return 'plugin';
|
|
272
|
+
return 'application';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function count(records, kind) { return records.filter((record) => record.recordKind === kind).length; }
|
|
276
|
+
function ids(records = []) { return records.map((record) => record?.id).filter(Boolean); }
|
|
277
|
+
function idsByRecordKind(records = [], kind) { return ids(records.filter((record) => record?.recordKind === kind)); }
|
|
278
|
+
function isPropertyLine(line) { return /^(role|host|hostId|sourcePath|path|sourceHash|evidence|proofEvidence)\s+/.test(line); }
|
|
279
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
280
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'ApplicationSurface'; }
|
|
281
|
+
function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
|
|
282
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
283
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
284
|
+
function readInlineFlag(label, text) { return new RegExp('(?:^|\\s)' + label + '(?:\\s|$)').test(text) || undefined; }
|
|
285
|
+
function readInlineList(text, ...labels) {
|
|
286
|
+
for (const label of labels) {
|
|
287
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
288
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
289
|
+
}
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
function parseSpan(value) {
|
|
293
|
+
if (!value) return undefined;
|
|
294
|
+
const match = /^(.+?):(\d+):(\d+)-(\d+):(\d+)$/.exec(value);
|
|
295
|
+
if (!match) return { path: value };
|
|
296
|
+
return { path: match[1], startLine: Number(match[2]), startColumn: Number(match[3]), endLine: Number(match[4]), endColumn: Number(match[5]) };
|
|
297
|
+
}
|
|
298
|
+
function cleanRecord(record) {
|
|
299
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0) && (!value || typeof value !== 'object' || Object.keys(value).length > 0)));
|
|
300
|
+
}
|
|
301
|
+
function safeId(value) {
|
|
302
|
+
return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
|
|
303
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { actionNode, capabilityNode, createDocument, effectNode, entityNode, externNode, latticeNode, migrationNode, stateNode, targetNode, typeNode } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
2
|
import { parseConstraintSpaceBlock } from './constraint-space.js';
|
|
3
3
|
import { parseConversionBlock } from './conversion.js';
|
|
4
|
+
import { parseApplicationSurfaceBlock } from './application-surface.js';
|
|
4
5
|
import { parseCanvasSurfaceBlock } from './canvas-surface.js';
|
|
5
6
|
import { parseDecisionGraphBlock } from './decision-graph.js';
|
|
6
7
|
import { parseDialectRegistryBlock } from './dialect-registry.js';
|
|
@@ -29,6 +30,7 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
29
30
|
const nativeSourceBlocks = [];
|
|
30
31
|
const packageManifestBlocks = [];
|
|
31
32
|
const canvasSurfaceBlocks = [];
|
|
33
|
+
const applicationSurfaceBlocks = [];
|
|
32
34
|
const documentId = options.id ?? readId(source) ?? 'mod_frontier';
|
|
33
35
|
const documentName = options.name ?? readName(source) ?? 'FrontierModule';
|
|
34
36
|
for (const block of readBlocks(source)) {
|
|
@@ -59,8 +61,9 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
59
61
|
if (block.kind === 'resourceGraph' || block.kind === 'semanticResourceGraph') resourceGraphBlocks.push(parseResourceGraphBlock(block));
|
|
60
62
|
if (block.kind === 'packageManifest' || block.kind === 'packageGraph' || block.kind === 'packageSurface') packageManifestBlocks.push(parsePackageManifestBlock(block));
|
|
61
63
|
if (block.kind === 'canvasSurface' || block.kind === 'canvasGraph') canvasSurfaceBlocks.push(parseCanvasSurfaceBlock(block));
|
|
64
|
+
if (block.kind === 'applicationSurface' || block.kind === 'appHost' || block.kind === 'plugin' || block.kind === 'pluginSurface' || block.kind === 'pluginContract') applicationSurfaceBlocks.push(parseApplicationSurfaceBlock(block));
|
|
62
65
|
}
|
|
63
|
-
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks });
|
|
66
|
+
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks, applicationSurfaceBlocks });
|
|
64
67
|
return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -70,7 +73,7 @@ function readName(source) { return /module\s+([A-Za-z_$][\w$]*)/.exec(source)?.[
|
|
|
70
73
|
function readId(source) { return /module\s+[A-Za-z_$][\w$]*\s+@id\(\s*["']([^"']+)["']\s*\)/.exec(source)?.[1]; }
|
|
71
74
|
function readBlocks(source) {
|
|
72
75
|
const blocks = [];
|
|
73
|
-
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations|conversion|universalConversionPlan|constraintSpace|possibilitySpace|decisionGraph|admissionGraph|dialectRegistry|universalDialectRegistry|interlingua|universalInterlingua|resourceGraph|semanticResourceGraph|packageManifest|packageGraph|packageSurface|canvasSurface|canvasGraph)\s+([^{}]+)\{/g;
|
|
76
|
+
const header = /\b(entity|state|action|view|migration|capability|effect|type|extern|lattice|nativeSource|target|proof|paradigm|paradigmSemantics|operations|semanticOperations|conversion|universalConversionPlan|constraintSpace|possibilitySpace|decisionGraph|admissionGraph|dialectRegistry|universalDialectRegistry|interlingua|universalInterlingua|resourceGraph|semanticResourceGraph|packageManifest|packageGraph|packageSurface|canvasSurface|canvasGraph|applicationSurface|appHost|plugin|pluginSurface|pluginContract)\s+([^{}]+)\{/g;
|
|
74
77
|
let match;
|
|
75
78
|
while ((match = header.exec(source))) {
|
|
76
79
|
let depth = 1; let index = header.lastIndex;
|
package/dist/metadata.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mergeDialectRegistryBlocks } from './dialect-registry.js';
|
|
2
|
+
import { mergeApplicationSurfaceBlocks } from './application-surface.js';
|
|
2
3
|
|
|
3
4
|
const PROOF_GROUPS = ['contracts', 'refinements', 'invariants', 'termination', 'temporal', 'obligations', 'artifacts', 'assumptions'];
|
|
4
5
|
const PARADIGM_GROUPS = [
|
|
@@ -23,7 +24,7 @@ const PARADIGM_GROUPS = [
|
|
|
23
24
|
'loweringRecords'
|
|
24
25
|
];
|
|
25
26
|
|
|
26
|
-
export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], conversionBlocks = [], constraintSpaceBlocks = [], decisionGraphBlocks = [], dialectRegistryBlocks = [], interlinguaBlocks = [], resourceGraphBlocks = [], nativeSourceBlocks = [], packageManifestBlocks = [], canvasSurfaceBlocks = [] } = {}) {
|
|
27
|
+
export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], operationBlocks = [], conversionBlocks = [], constraintSpaceBlocks = [], decisionGraphBlocks = [], dialectRegistryBlocks = [], interlinguaBlocks = [], resourceGraphBlocks = [], nativeSourceBlocks = [], packageManifestBlocks = [], canvasSurfaceBlocks = [], applicationSurfaceBlocks = [] } = {}) {
|
|
27
28
|
const metadata = {};
|
|
28
29
|
if (proofBlocks.length) metadata.proof = mergeProofBlocks(proofBlocks);
|
|
29
30
|
if (paradigmBlocks.length) metadata.paradigmSemantics = mergeParadigmBlocks(paradigmBlocks);
|
|
@@ -36,8 +37,9 @@ export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], op
|
|
|
36
37
|
if (resourceGraphBlocks.length) metadata.semanticResourceGraphs = mergeResourceGraphBlocks(resourceGraphBlocks);
|
|
37
38
|
if (packageManifestBlocks.length) metadata.packageManifests = mergePackageManifestBlocks(packageManifestBlocks);
|
|
38
39
|
if (canvasSurfaceBlocks.length) metadata.canvasSurfaces = mergeCanvasSurfaceBlocks(canvasSurfaceBlocks);
|
|
39
|
-
if (
|
|
40
|
-
|
|
40
|
+
if (applicationSurfaceBlocks.length) metadata.applicationSurfaces = mergeApplicationSurfaceBlocks(applicationSurfaceBlocks);
|
|
41
|
+
if (nativeSourceBlocks.some((block) => block.sourceMaps.length || block.mergeCandidates.length || block.evidence.length) || packageManifestBlocks.length || canvasSurfaceBlocks.length || applicationSurfaceBlocks.length) {
|
|
42
|
+
metadata.universalAst = mergeUniversalAstBlocks(nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks, applicationSurfaceBlocks);
|
|
41
43
|
}
|
|
42
44
|
return Object.keys(metadata).length ? metadata : undefined;
|
|
43
45
|
}
|
|
@@ -108,7 +110,7 @@ function mergeConstraintSpaceBlocks(blocks) {
|
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
function mergeUniversalAstBlocks(blocks, packageManifestBlocks = [], canvasSurfaceBlocks = []) {
|
|
113
|
+
function mergeUniversalAstBlocks(blocks, packageManifestBlocks = [], canvasSurfaceBlocks = [], applicationSurfaceBlocks = []) {
|
|
112
114
|
return {
|
|
113
115
|
id: blocks.length === 1 ? `universalAst:${blocks[0].node.id}` : 'universalAst:source',
|
|
114
116
|
nativeSourceIds: blocks.map((block) => block.node.id),
|
|
@@ -118,12 +120,15 @@ function mergeUniversalAstBlocks(blocks, packageManifestBlocks = [], canvasSurfa
|
|
|
118
120
|
losses: blocks.flatMap((block) => block.losses ?? []),
|
|
119
121
|
packageManifests: packageManifestBlocks,
|
|
120
122
|
canvasSurfaces: canvasSurfaceBlocks,
|
|
123
|
+
applicationSurfaces: applicationSurfaceBlocks,
|
|
121
124
|
packageManifestIds: ids(packageManifestBlocks),
|
|
122
125
|
canvasSurfaceIds: ids(canvasSurfaceBlocks),
|
|
126
|
+
applicationSurfaceIds: ids(applicationSurfaceBlocks),
|
|
123
127
|
metadata: {
|
|
124
128
|
authoredNativeSourceIds: blocks.map((block) => block.node.id),
|
|
125
129
|
authoredPackageManifestIds: ids(packageManifestBlocks),
|
|
126
|
-
authoredCanvasSurfaceIds: ids(canvasSurfaceBlocks)
|
|
130
|
+
authoredCanvasSurfaceIds: ids(canvasSurfaceBlocks),
|
|
131
|
+
authoredApplicationSurfaceIds: ids(applicationSurfaceBlocks)
|
|
127
132
|
}
|
|
128
133
|
};
|
|
129
134
|
}
|