@shapeshift-labs/frontier-lang-parser 0.3.28 → 0.3.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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
+ `autoMergeClaim`, `semanticEquivalenceClaim`, `abiCompatibilityClaim`,
351
+ `projectionEquivalenceClaim`, `pluginCompatibilityClaim`, and
352
+ `sandboxSafetyClaim` remain false until a higher layer supplies source-bound
353
+ runtime, ABI, projection, sandbox, and admission 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,308 @@
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
+ const evidenceIds = [
61
+ ...blocks.flatMap((block) => ids(block.evidence)),
62
+ ...records.flatMap((record) => record.evidenceIds ?? []),
63
+ ...records.flatMap((record) => record.proofEvidenceIds ?? [])
64
+ ];
65
+ return {
66
+ id: blocks.length === 1 ? blocks[0].id : 'applicationSurfaces:source',
67
+ surfaces: blocks,
68
+ surfaceIds: ids(blocks),
69
+ recordIds: ids(records),
70
+ mountIds: idsByRecordKind(records, 'mount'),
71
+ providedSurfaceIds: idsByRecordKind(records, 'provided-surface'),
72
+ requiredCapabilityIds: idsByRecordKind(records, 'required-capability'),
73
+ routeIds: idsByRecordKind(records, 'route'),
74
+ eventIds: idsByRecordKind(records, 'event'),
75
+ assetIds: idsByRecordKind(records, 'asset'),
76
+ gateIds: idsByRecordKind(records, 'gate'),
77
+ evidenceIds: [...new Set(evidenceIds.filter(Boolean))],
78
+ proofGapCodes: [...new Set(blocks.flatMap((block) => (block.proofGaps ?? []).map((gap) => gap.code).filter(Boolean)))],
79
+ summary: {
80
+ surfaceCount: blocks.length,
81
+ recordCount: records.length,
82
+ mountCount: blocks.reduce((total, block) => total + (block.summary?.mountCount ?? 0), 0),
83
+ providedSurfaceCount: blocks.reduce((total, block) => total + (block.summary?.providedSurfaceCount ?? 0), 0),
84
+ requiredCapabilityCount: blocks.reduce((total, block) => total + (block.summary?.requiredCapabilityCount ?? 0), 0),
85
+ routeCount: blocks.reduce((total, block) => total + (block.summary?.routeCount ?? 0), 0),
86
+ eventCount: blocks.reduce((total, block) => total + (block.summary?.eventCount ?? 0), 0),
87
+ assetCount: blocks.reduce((total, block) => total + (block.summary?.assetCount ?? 0), 0),
88
+ gateCount: blocks.reduce((total, block) => total + (block.summary?.gateCount ?? 0), 0),
89
+ proofGapCount: blocks.reduce((total, block) => total + (block.proofGaps?.length ?? 0), 0)
90
+ },
91
+ claims: applicationFalseClaims(),
92
+ metadata: { authoredApplicationSurfaceIds: ids(blocks) }
93
+ };
94
+ }
95
+
96
+ function applicationRecord(kind, name, text, context) {
97
+ const recordName = readInlineWord('name', text) ?? name;
98
+ const proofGaps = readInlineList(text, 'proofGap', 'proofGaps', 'gap', 'gaps')?.map((code) => applicationProofGap(code, '')) ?? [];
99
+ const base = {
100
+ kind: 'frontier.lang.applicationSurface.record',
101
+ recordKind: kind,
102
+ id: idFrom(text, `application_${kind.replace(/-/g, '_')}_${safeId(recordName)}`),
103
+ name: recordName,
104
+ surfaceId: context.surfaceId,
105
+ surfaceName: context.surfaceName,
106
+ role: context.role,
107
+ identityKey: readInlineWord('identity', text) ?? readInlineWord('identityKey', text) ?? `${kind}:${context.role}:${recordName}`,
108
+ sourcePath: readInlineWord('sourcePath', text) ?? context.sourcePath,
109
+ sourceHash: readInlineWord('sourceHash', text) ?? context.sourceHash,
110
+ sourceSpan: parseSpan(readInlineWord('sourceSpan', text)),
111
+ evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
112
+ proofEvidenceIds: readInlineList(text, 'proofEvidence', 'proofEvidenceIds'),
113
+ missingEvidence: readInlineList(text, 'missingEvidence'),
114
+ proofGaps,
115
+ claims: applicationFalseClaims()
116
+ };
117
+ if (kind === 'mount') return cleanRecord({
118
+ ...base,
119
+ mountKind: readInlineWord('kind', text) ?? readInlineWord('mountKind', text) ?? 'region',
120
+ path: readInlineQuoted('path', text) ?? readInlineWord('path', text),
121
+ slot: readInlineWord('slot', text),
122
+ parentId: readInlineWord('parent', text) ?? readInlineWord('parentId', text),
123
+ viewId: readInlineWord('view', text) ?? readInlineWord('viewId', text),
124
+ target: readInlineWord('target', text),
125
+ renderer: readInlineWord('renderer', text)
126
+ });
127
+ if (kind === 'provided-surface') return cleanRecord({
128
+ ...base,
129
+ surfaceKind: readInlineWord('surface', text) ?? readInlineWord('surfaceKind', text) ?? readInlineWord('kind', text) ?? 'view',
130
+ viewId: readInlineWord('view', text) ?? readInlineWord('viewId', text),
131
+ actionId: readInlineWord('action', text) ?? readInlineWord('actionId', text),
132
+ effectId: readInlineWord('effect', text) ?? readInlineWord('effectId', text),
133
+ component: readInlineWord('component', text),
134
+ mountId: readInlineWord('mount', text) ?? readInlineWord('mountId', text),
135
+ routeId: readInlineWord('route', text) ?? readInlineWord('routeId', text),
136
+ capabilityIds: readInlineList(text, 'capability', 'capabilities', 'capabilityIds'),
137
+ target: readInlineWord('target', text),
138
+ language: readInlineWord('language', text),
139
+ contractId: readInlineWord('contract', text) ?? readInlineWord('contractId', text)
140
+ });
141
+ if (kind === 'required-capability') return cleanRecord({
142
+ ...base,
143
+ capability: readInlineWord('capability', text) ?? readInlineWord('capabilityId', text) ?? recordName,
144
+ category: readInlineWord('category', text),
145
+ permission: readInlineWord('permission', text),
146
+ adapterId: readInlineWord('adapter', text) ?? readInlineWord('adapterId', text),
147
+ hostId: readInlineWord('host', text) ?? readInlineWord('hostId', text),
148
+ runtime: readInlineWord('runtime', text),
149
+ target: readInlineWord('target', text)
150
+ });
151
+ if (kind === 'route') return cleanRecord({
152
+ ...base,
153
+ path: readInlineQuoted('path', text) ?? readInlineWord('path', text) ?? recordName,
154
+ method: readInlineWord('method', text),
155
+ viewId: readInlineWord('view', text) ?? readInlineWord('viewId', text),
156
+ actionId: readInlineWord('action', text) ?? readInlineWord('actionId', text),
157
+ mountId: readInlineWord('mount', text) ?? readInlineWord('mountId', text),
158
+ paramIds: readInlineList(text, 'param', 'params', 'paramIds')
159
+ });
160
+ if (kind === 'event') return cleanRecord({
161
+ ...base,
162
+ eventKind: readInlineWord('kind', text) ?? readInlineWord('eventKind', text),
163
+ eventName: readInlineWord('event', text) ?? readInlineWord('eventName', text) ?? recordName,
164
+ actionId: readInlineWord('action', text) ?? readInlineWord('actionId', text),
165
+ input: readInlineWord('input', text),
166
+ sourceId: readInlineWord('source', text) ?? readInlineWord('sourceId', text),
167
+ targetId: readInlineWord('target', text) ?? readInlineWord('targetId', text)
168
+ });
169
+ if (kind === 'asset') return cleanRecord({
170
+ ...base,
171
+ assetKind: readInlineWord('kind', text) ?? readInlineWord('assetKind', text),
172
+ path: readInlineQuoted('path', text) ?? readInlineWord('path', text),
173
+ assetHash: readInlineWord('hash', text) ?? readInlineWord('assetHash', text),
174
+ integrity: readInlineWord('integrity', text),
175
+ runtime: readInlineWord('runtime', text),
176
+ mountId: readInlineWord('mount', text) ?? readInlineWord('mountId', text)
177
+ });
178
+ if (kind === 'gate') return cleanRecord({
179
+ ...base,
180
+ gateKind: readInlineWord('kind', text) ?? readInlineWord('gateKind', text) ?? 'test',
181
+ command: readInlineQuoted('command', text) ?? readInlineWord('command', text),
182
+ required: readInlineFlag('required', text) ?? undefined,
183
+ status: readInlineWord('status', text),
184
+ subjectIds: readInlineList(text, 'subject', 'subjects', 'subjectIds'),
185
+ runtime: readInlineWord('runtime', text)
186
+ });
187
+ return cleanRecord(base);
188
+ }
189
+
190
+ function applicationProofGap(name, text) {
191
+ const code = readInlineWord('code', text) ?? name;
192
+ return cleanRecord({
193
+ id: idFrom(text, `application_gap_${safeId(code)}`),
194
+ code,
195
+ status: readInlineWord('status', text) ?? 'not-claimed',
196
+ summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
197
+ failClosed: true,
198
+ ...applicationFalseClaims(),
199
+ sourceSpan: parseSpan(readInlineWord('sourceSpan', text))
200
+ });
201
+ }
202
+
203
+ function parseEvidenceRows(body) {
204
+ const records = [];
205
+ for (const rawLine of body.split('\n')) {
206
+ const line = rawLine.trim();
207
+ const match = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
208
+ if (!match) continue;
209
+ records.push(cleanRecord({
210
+ id: idFrom(match[2], `evidence_${match[1]}`),
211
+ kind: readInlineWord('kind', match[2]) ?? 'note',
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;
219
+ }
220
+
221
+ function summarizeApplicationSurface(tree) {
222
+ return {
223
+ recordCount: tree.records.length,
224
+ mountCount: count(tree.records, 'mount'),
225
+ providedSurfaceCount: count(tree.records, 'provided-surface'),
226
+ requiredCapabilityCount: count(tree.records, 'required-capability'),
227
+ routeCount: count(tree.records, 'route'),
228
+ eventCount: count(tree.records, 'event'),
229
+ assetCount: count(tree.records, 'asset'),
230
+ gateCount: count(tree.records, 'gate'),
231
+ evidenceCount: tree.evidence.length,
232
+ proofGapCount: tree.proofGaps.length,
233
+ parseErrors: tree.parser.errors.length
234
+ };
235
+ }
236
+
237
+ function applicationFalseClaims() {
238
+ return {
239
+ autoMergeClaim: false,
240
+ semanticEquivalenceClaim: false,
241
+ runtimeEquivalenceClaim: false,
242
+ abiCompatibilityClaim: false,
243
+ projectionEquivalenceClaim: false,
244
+ pluginCompatibilityClaim: false,
245
+ sandboxSafetyClaim: false
246
+ };
247
+ }
248
+
249
+ function hashableRecord(record) {
250
+ return {
251
+ recordKind: record.recordKind,
252
+ name: record.name,
253
+ identityKey: record.identityKey,
254
+ capability: record.capability,
255
+ path: record.path,
256
+ viewId: record.viewId,
257
+ actionId: record.actionId,
258
+ proofGaps: record.proofGaps?.map((gap) => gap.code)
259
+ };
260
+ }
261
+
262
+ function normalizeRowKind(kind) {
263
+ if (kind === 'provide' || kind === 'provides') return 'provided-surface';
264
+ if (kind === 'require' || kind === 'requires' || kind === 'required') return 'required-capability';
265
+ return kind;
266
+ }
267
+
268
+ function surfaceKindFrom(kind) {
269
+ if (kind === 'appHost') return 'host';
270
+ if (kind === 'plugin' || kind === 'pluginSurface' || kind === 'pluginContract') return 'plugin';
271
+ return 'application';
272
+ }
273
+
274
+ function defaultRole(surfaceKind) {
275
+ if (surfaceKind === 'host') return 'host';
276
+ if (surfaceKind === 'plugin') return 'plugin';
277
+ return 'application';
278
+ }
279
+
280
+ function count(records, kind) { return records.filter((record) => record.recordKind === kind).length; }
281
+ function ids(records = []) { return records.map((record) => record?.id).filter(Boolean); }
282
+ function idsByRecordKind(records = [], kind) { return ids(records.filter((record) => record?.recordKind === kind)); }
283
+ function isPropertyLine(line) { return /^(role|host|hostId|sourcePath|path|sourceHash|evidence|proofEvidence)\s+/.test(line); }
284
+ function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
285
+ function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'ApplicationSurface'; }
286
+ function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
287
+ function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
288
+ function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
289
+ function readInlineFlag(label, text) { return new RegExp('(?:^|\\s)' + label + '(?:\\s|$)').test(text) || undefined; }
290
+ function readInlineList(text, ...labels) {
291
+ for (const label of labels) {
292
+ const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
293
+ if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
294
+ }
295
+ return undefined;
296
+ }
297
+ function parseSpan(value) {
298
+ if (!value) return undefined;
299
+ const match = /^(.+?):(\d+):(\d+)-(\d+):(\d+)$/.exec(value);
300
+ if (!match) return { path: value };
301
+ return { path: match[1], startLine: Number(match[2]), startColumn: Number(match[3]), endLine: Number(match[4]), endColumn: Number(match[5]) };
302
+ }
303
+ function cleanRecord(record) {
304
+ 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)));
305
+ }
306
+ function safeId(value) {
307
+ return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
308
+ }
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 (nativeSourceBlocks.some((block) => block.sourceMaps.length || block.mergeCandidates.length || block.evidence.length) || packageManifestBlocks.length || canvasSurfaceBlocks.length) {
40
- metadata.universalAst = mergeUniversalAstBlocks(nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-parser",
3
- "version": "0.3.28",
3
+ "version": "0.3.30",
4
4
  "description": "Parser for the first Frontier Lang .frontier syntax slice.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",