@shapeshift-labs/frontier-lang-parser 0.3.27 → 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 +92 -0
- package/dist/application-surface.js +303 -0
- package/dist/canvas-surface.js +206 -0
- package/dist/index.js +11 -2
- package/dist/metadata.js +76 -5
- package/dist/package-manifest.js +162 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -260,6 +260,98 @@ and events stay attached to the child render node instead of leaking onto the
|
|
|
260
260
|
parent. `kind component` records `component` instead of a literal HTML tag, so
|
|
261
261
|
target adapters can decide how to project it.
|
|
262
262
|
|
|
263
|
+
## Authored package and canvas syntax
|
|
264
|
+
|
|
265
|
+
`.frontier` files can describe package-management and canvas semantic surfaces
|
|
266
|
+
directly, without requiring the source of truth to be a generated JSON tree.
|
|
267
|
+
These blocks make dependency, script, export, draw-command, command-trace, and
|
|
268
|
+
proof-gap records queryable as source-level evidence. They intentionally do not
|
|
269
|
+
claim package install equivalence, canvas runtime equivalence, or visual
|
|
270
|
+
equivalence.
|
|
271
|
+
|
|
272
|
+
```frontier
|
|
273
|
+
packageManifest AppPackage @id("pkg_manifest_app") {
|
|
274
|
+
sourcePath package.json
|
|
275
|
+
sourceHash sha256:package
|
|
276
|
+
packageManager npm@11.0.0
|
|
277
|
+
evidence packageProbe @id("evidence_package_probe") kind test status passed path reports/package.json
|
|
278
|
+
metadata name @id("pkg_meta_name") value "@example/app" evidence evidence_package_probe
|
|
279
|
+
dependency react @id("pkg_dep_react") section dependencies range ^19.0.0 evidence evidence_package_probe
|
|
280
|
+
dependency typescript @id("pkg_dep_typescript") section peerDependencies range ^5.9.0 proofGap package-peer-compatibility-boundary evidence evidence_package_probe
|
|
281
|
+
script test @id("pkg_script_test") command "vitest --run" proofGap package-script-runtime-boundary evidence evidence_package_probe
|
|
282
|
+
export root @id("pkg_export_root") section exports name . target ./dist/index.js proofGap package-conditional-resolution-boundary evidence evidence_package_probe
|
|
283
|
+
gap workspace @id("pkg_gap_workspace") code package-workspace-graph-boundary summary "Workspace expansion requires repository graph evidence."
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
canvasSurface PreviewCanvas @id("canvas_surface_preview") {
|
|
287
|
+
sourcePath src/draw.js
|
|
288
|
+
sourceHash sha256:draw
|
|
289
|
+
evidence canvasProbe @id("evidence_canvas_probe") kind browser-probe status passed path reports/canvas.json
|
|
290
|
+
element preview @id("canvas_element_preview") name canvas category html-canvas order 1 identity canvas:preview attributes data-frontier-key=preview|width=100 evidence evidence_canvas_probe
|
|
291
|
+
command context @id("canvas_command_context") name getContext category context context 2d order 2 proofGap canvas-context-runtime-boundary evidence evidence_canvas_probe
|
|
292
|
+
state fillStyle @id("canvas_state_fill_style") name fillStyle category state order 3 proofGap canvas-stateful-render-order-boundary evidence evidence_canvas_probe
|
|
293
|
+
command fill @id("canvas_command_fill") name fillRect category draw context 2d order 4 proofGap canvas-stateful-render-order-boundary evidence evidence_canvas_probe
|
|
294
|
+
command offscreen @id("canvas_command_offscreen") name transferControlToOffscreen category offscreen order 5 proofGap canvas-offscreen-worker-boundary evidence evidence_canvas_probe
|
|
295
|
+
trace drawFrame @id("canvas_trace_draw_frame") commands getContext|fillStyle|fillRect|transferControlToOffscreen evidence evidence_canvas_probe
|
|
296
|
+
gap image @id("canvas_gap_image") code canvas-image-resource-boundary summary "Image drawing needs bitmap/resource evidence."
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The parser stores package blocks in `metadata.packageManifests` and canvas
|
|
301
|
+
blocks in `metadata.canvasSurfaces`, and mirrors both under
|
|
302
|
+
`metadata.universalAst` so compiler and merge tooling can see them next to
|
|
303
|
+
native-source source maps and merge candidates. Package records keep
|
|
304
|
+
`packageInstallEquivalenceClaim`, `installEquivalenceClaim`, and
|
|
305
|
+
`runtimeEquivalenceClaim` false. Canvas records keep
|
|
306
|
+
`browserRuntimeEquivalenceClaim`, `canvasRuntimeEquivalenceClaim`, and
|
|
307
|
+
`canvasVisualEquivalenceClaim` false.
|
|
308
|
+
Runtime probes, package-manager solver output, lockfile proof, browser pixels,
|
|
309
|
+
worker transfer evidence, and GPU validation must still be supplied by higher
|
|
310
|
+
layers before admission.
|
|
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
|
+
|
|
263
355
|
## Authored target projection syntax
|
|
264
356
|
|
|
265
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
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
|
|
3
|
+
export function parseCanvasSurfaceBlock(block) {
|
|
4
|
+
const name = nameFrom(block.header);
|
|
5
|
+
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body);
|
|
6
|
+
const sourceHash = readLine('sourceHash', block.body);
|
|
7
|
+
const evidence = parseEvidenceRows(block.body);
|
|
8
|
+
const records = [];
|
|
9
|
+
const commandTraces = [];
|
|
10
|
+
const proofGaps = [];
|
|
11
|
+
for (const rawLine of block.body.split('\n')) {
|
|
12
|
+
const line = rawLine.trim();
|
|
13
|
+
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
14
|
+
const match = /^(element|command|state|stateWrite|trace|gap|proofGap)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
15
|
+
if (!match) continue;
|
|
16
|
+
const [, rowKind, rowName, rest] = match;
|
|
17
|
+
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
18
|
+
proofGaps.push(canvasProofGap(rowName, rest));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (rowKind === 'trace') {
|
|
22
|
+
commandTraces.push(canvasTrace(rowName, rest, { sourcePath }));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
records.push(canvasRecord(rowKind === 'stateWrite' ? 'state-write' : rowKind, rowName, rest, { sourcePath, sourceHash }));
|
|
26
|
+
}
|
|
27
|
+
const allGaps = [...records.flatMap((record) => record.proofGaps ?? []), ...proofGaps];
|
|
28
|
+
const tree = {
|
|
29
|
+
kind: 'frontier.lang.canvasSemanticTree',
|
|
30
|
+
version: 1,
|
|
31
|
+
id: idFrom(block.header, `canvas_surface_${name}`),
|
|
32
|
+
name,
|
|
33
|
+
sourcePath,
|
|
34
|
+
sourceHash,
|
|
35
|
+
records,
|
|
36
|
+
commandTraces,
|
|
37
|
+
proofGaps: allGaps,
|
|
38
|
+
evidence,
|
|
39
|
+
parser: { status: 'authored', errors: [] },
|
|
40
|
+
claims: {
|
|
41
|
+
autoMergeClaim: false,
|
|
42
|
+
semanticEquivalenceClaim: false,
|
|
43
|
+
browserRuntimeEquivalenceClaim: false,
|
|
44
|
+
canvasRuntimeEquivalenceClaim: false,
|
|
45
|
+
canvasVisualEquivalenceClaim: false
|
|
46
|
+
},
|
|
47
|
+
metadata: { authoredName: name }
|
|
48
|
+
};
|
|
49
|
+
tree.summary = summarizeCanvasSurface(tree);
|
|
50
|
+
tree.treeHash = hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTree.v1', records: records.map(hashableRecord), proofGaps: allGaps.map((gap) => gap.code), commandTraces: commandTraces.map((trace) => trace.traceHash) });
|
|
51
|
+
return tree;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function canvasRecord(kind, name, text, context) {
|
|
55
|
+
const category = readInlineWord('category', text) ?? defaultCategory(kind);
|
|
56
|
+
const recordName = readInlineWord('name', text) ?? name;
|
|
57
|
+
const order = readInlineNumber('order', text) ?? readInlineNumber('renderOrder', text) ?? 0;
|
|
58
|
+
const proofGaps = readInlineList(text, 'proofGap', 'proofGaps', 'gap', 'gaps')?.map((code) => canvasProofGap(code, '')) ?? [];
|
|
59
|
+
const textValue = readInlineQuoted('text', text) ?? readInlineWord('text', text) ?? `${kind}:${category}:${recordName}:${order}`;
|
|
60
|
+
return cleanRecord({
|
|
61
|
+
kind,
|
|
62
|
+
id: idFrom(text, `canvas_${kind.replace(/-/g, '_')}_${safeId(recordName)}`),
|
|
63
|
+
name: recordName,
|
|
64
|
+
category,
|
|
65
|
+
contextKind: readInlineWord('context', text) ?? readInlineWord('contextKind', text),
|
|
66
|
+
renderOrder: order,
|
|
67
|
+
identityKey: readInlineWord('identity', text) ?? readInlineWord('identityKey', text) ?? `canvas:${kind}:${category}:${recordName}:${order || safeId(recordName)}`,
|
|
68
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
69
|
+
sourceHash: readInlineWord('sourceHash', text) ?? context.sourceHash,
|
|
70
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text)),
|
|
71
|
+
textHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredRecordText.v1', text: textValue }),
|
|
72
|
+
attributes: parsePairs(readInlineWord('attributes', text) ?? readInlineWord('attrs', text) ?? readInlineWord('attr', text)),
|
|
73
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
74
|
+
proofGaps
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function canvasTrace(name, text, context) {
|
|
79
|
+
const commands = readInlineList(text, 'command', 'commands') ?? [];
|
|
80
|
+
const records = commands.map((command, index) => ({
|
|
81
|
+
kind: 'trace-command',
|
|
82
|
+
name: command,
|
|
83
|
+
category: readInlineWord('category', text) ?? 'custom',
|
|
84
|
+
ordinal: index + 1,
|
|
85
|
+
commandHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTraceCommand.v1', command, index }),
|
|
86
|
+
argsHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTraceArgs.v1', command, index }),
|
|
87
|
+
proofGaps: []
|
|
88
|
+
}));
|
|
89
|
+
return cleanRecord({
|
|
90
|
+
kind: 'frontier.lang.canvasCommandTrace',
|
|
91
|
+
version: 1,
|
|
92
|
+
id: idFrom(text, `canvas_trace_${name}`),
|
|
93
|
+
name,
|
|
94
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
95
|
+
traceHash: hashSemanticValue({ kind: 'frontier.lang.canvas.authoredTrace.v1', commands }),
|
|
96
|
+
records,
|
|
97
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
98
|
+
runtimeEquivalenceClaim: false,
|
|
99
|
+
visualEquivalenceClaim: false,
|
|
100
|
+
summary: {
|
|
101
|
+
commands: records.length,
|
|
102
|
+
drawCommands: records.filter((record) => ['draw', 'image', 'text', 'path'].includes(record.category)).length,
|
|
103
|
+
proofGaps: 0
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function canvasProofGap(name, text) {
|
|
109
|
+
const code = readInlineWord('code', text) ?? name;
|
|
110
|
+
return cleanRecord({
|
|
111
|
+
id: idFrom(text, `canvas_gap_${safeId(code)}`),
|
|
112
|
+
code,
|
|
113
|
+
status: readInlineWord('status', text) ?? 'not-claimed',
|
|
114
|
+
summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
|
|
115
|
+
failClosed: true,
|
|
116
|
+
semanticEquivalenceClaim: false,
|
|
117
|
+
browserRuntimeEquivalenceClaim: false,
|
|
118
|
+
canvasRuntimeEquivalenceClaim: false,
|
|
119
|
+
canvasVisualEquivalenceClaim: false,
|
|
120
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text))
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseEvidenceRows(body) {
|
|
125
|
+
const records = [];
|
|
126
|
+
for (const rawLine of body.split('\n')) {
|
|
127
|
+
const line = rawLine.trim();
|
|
128
|
+
const match = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
129
|
+
if (!match) continue;
|
|
130
|
+
records.push(cleanRecord({
|
|
131
|
+
id: idFrom(match[2], `evidence_${match[1]}`),
|
|
132
|
+
kind: readInlineWord('kind', match[2]) ?? 'note',
|
|
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;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function summarizeCanvasSurface(tree) {
|
|
143
|
+
return {
|
|
144
|
+
elements: tree.records.filter((record) => record.kind === 'element').length,
|
|
145
|
+
commands: tree.records.filter((record) => record.kind === 'command').length,
|
|
146
|
+
stateWrites: tree.records.filter((record) => record.kind === 'state-write').length,
|
|
147
|
+
drawCommands: tree.records.filter((record) => ['draw', 'image', 'text', 'path'].includes(record.category)).length,
|
|
148
|
+
offscreenCommands: tree.records.filter((record) => record.category === 'offscreen').length,
|
|
149
|
+
gpuCommands: tree.records.filter((record) => record.contextKind === 'webgl' || record.contextKind === 'webgl2' || record.contextKind === 'webgpu').length,
|
|
150
|
+
commandTraces: tree.commandTraces.length,
|
|
151
|
+
proofGaps: tree.proofGaps.length,
|
|
152
|
+
parseErrors: tree.parser.errors.length
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function defaultCategory(kind) {
|
|
157
|
+
if (kind === 'element') return 'html-canvas';
|
|
158
|
+
if (kind === 'state-write') return 'state';
|
|
159
|
+
return 'draw';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parsePairs(value) {
|
|
163
|
+
if (!value) return undefined;
|
|
164
|
+
const pairs = Object.fromEntries(value.split(/[|,]/).map((item) => {
|
|
165
|
+
const [key, rawValue] = item.split('=');
|
|
166
|
+
return [key, rawValue ?? true];
|
|
167
|
+
}).filter(([key]) => key));
|
|
168
|
+
return Object.keys(pairs).length ? pairs : undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function hashableRecord(record) {
|
|
172
|
+
return { kind: record.kind, name: record.name, category: record.category, contextKind: record.contextKind, renderOrder: record.renderOrder, proofGaps: record.proofGaps?.map((gap) => gap.code) };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function isPropertyLine(line) {
|
|
176
|
+
return /^(sourcePath|path|sourceHash|evidence|proofEvidence)\s+/.test(line);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
180
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'CanvasSurface'; }
|
|
181
|
+
function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
|
|
182
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
183
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
184
|
+
function readInlineNumber(label, text) {
|
|
185
|
+
const value = readInlineWord(label, text);
|
|
186
|
+
return value === undefined ? undefined : Number(value);
|
|
187
|
+
}
|
|
188
|
+
function readInlineList(text, ...labels) {
|
|
189
|
+
for (const label of labels) {
|
|
190
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
191
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
function parseSpan(value) {
|
|
196
|
+
if (!value) return undefined;
|
|
197
|
+
const match = /^(.+?):(\d+):(\d+)-(\d+):(\d+)$/.exec(value);
|
|
198
|
+
if (!match) return { path: value };
|
|
199
|
+
return { path: match[1], startLine: Number(match[2]), startColumn: Number(match[3]), endLine: Number(match[4]), endColumn: Number(match[5]) };
|
|
200
|
+
}
|
|
201
|
+
function cleanRecord(record) {
|
|
202
|
+
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)));
|
|
203
|
+
}
|
|
204
|
+
function safeId(value) {
|
|
205
|
+
return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
|
|
206
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
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';
|
|
5
|
+
import { parseCanvasSurfaceBlock } from './canvas-surface.js';
|
|
4
6
|
import { parseDecisionGraphBlock } from './decision-graph.js';
|
|
5
7
|
import { parseDialectRegistryBlock } from './dialect-registry.js';
|
|
6
8
|
import { parseInterlinguaBlock } from './interlingua.js';
|
|
7
9
|
import { createParsedMetadata } from './metadata.js';
|
|
8
10
|
import { parseSemanticOperationsBlock } from './operations.js';
|
|
11
|
+
import { parsePackageManifestBlock } from './package-manifest.js';
|
|
9
12
|
import { parseParadigmBlock } from './paradigm.js';
|
|
10
13
|
import { parseProofBlock } from './proof.js';
|
|
11
14
|
import { parseResourceGraphBlock } from './resource-graph.js';
|
|
@@ -25,6 +28,9 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
25
28
|
const interlinguaBlocks = [];
|
|
26
29
|
const resourceGraphBlocks = [];
|
|
27
30
|
const nativeSourceBlocks = [];
|
|
31
|
+
const packageManifestBlocks = [];
|
|
32
|
+
const canvasSurfaceBlocks = [];
|
|
33
|
+
const applicationSurfaceBlocks = [];
|
|
28
34
|
const documentId = options.id ?? readId(source) ?? 'mod_frontier';
|
|
29
35
|
const documentName = options.name ?? readName(source) ?? 'FrontierModule';
|
|
30
36
|
for (const block of readBlocks(source)) {
|
|
@@ -53,8 +59,11 @@ export function parseFrontierSource(source, options = {}) {
|
|
|
53
59
|
if (block.kind === 'dialectRegistry' || block.kind === 'universalDialectRegistry') dialectRegistryBlocks.push(parseDialectRegistryBlock(block));
|
|
54
60
|
if (block.kind === 'interlingua' || block.kind === 'universalInterlingua') interlinguaBlocks.push(parseInterlinguaBlock(block));
|
|
55
61
|
if (block.kind === 'resourceGraph' || block.kind === 'semanticResourceGraph') resourceGraphBlocks.push(parseResourceGraphBlock(block));
|
|
62
|
+
if (block.kind === 'packageManifest' || block.kind === 'packageGraph' || block.kind === 'packageSurface') packageManifestBlocks.push(parsePackageManifestBlock(block));
|
|
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));
|
|
56
65
|
}
|
|
57
|
-
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks });
|
|
66
|
+
const metadata = createParsedMetadata({ proofBlocks, paradigmBlocks, operationBlocks, conversionBlocks, constraintSpaceBlocks, decisionGraphBlocks, dialectRegistryBlocks, interlinguaBlocks, resourceGraphBlocks, nativeSourceBlocks, packageManifestBlocks, canvasSurfaceBlocks, applicationSurfaceBlocks });
|
|
58
67
|
return createDocument({ id: documentId, name: documentName, nodes, ...(metadata ? { metadata } : {}) });
|
|
59
68
|
}
|
|
60
69
|
|
|
@@ -64,7 +73,7 @@ function readName(source) { return /module\s+([A-Za-z_$][\w$]*)/.exec(source)?.[
|
|
|
64
73
|
function readId(source) { return /module\s+[A-Za-z_$][\w$]*\s+@id\(\s*["']([^"']+)["']\s*\)/.exec(source)?.[1]; }
|
|
65
74
|
function readBlocks(source) {
|
|
66
75
|
const blocks = [];
|
|
67
|
-
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)\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;
|
|
68
77
|
let match;
|
|
69
78
|
while ((match = header.exec(source))) {
|
|
70
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 = [] } = {}) {
|
|
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);
|
|
@@ -34,8 +35,11 @@ export function createParsedMetadata({ proofBlocks = [], paradigmBlocks = [], op
|
|
|
34
35
|
if (dialectRegistryBlocks.length) metadata.dialects = mergeDialectRegistryBlocks(dialectRegistryBlocks);
|
|
35
36
|
if (interlinguaBlocks.length) metadata.universalInterlingua = mergeInterlinguaBlocks(interlinguaBlocks);
|
|
36
37
|
if (resourceGraphBlocks.length) metadata.semanticResourceGraphs = mergeResourceGraphBlocks(resourceGraphBlocks);
|
|
37
|
-
if (
|
|
38
|
-
|
|
38
|
+
if (packageManifestBlocks.length) metadata.packageManifests = mergePackageManifestBlocks(packageManifestBlocks);
|
|
39
|
+
if (canvasSurfaceBlocks.length) metadata.canvasSurfaces = mergeCanvasSurfaceBlocks(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);
|
|
39
43
|
}
|
|
40
44
|
return Object.keys(metadata).length ? metadata : undefined;
|
|
41
45
|
}
|
|
@@ -106,7 +110,7 @@ function mergeConstraintSpaceBlocks(blocks) {
|
|
|
106
110
|
};
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
function
|
|
113
|
+
function mergeUniversalAstBlocks(blocks, packageManifestBlocks = [], canvasSurfaceBlocks = [], applicationSurfaceBlocks = []) {
|
|
110
114
|
return {
|
|
111
115
|
id: blocks.length === 1 ? `universalAst:${blocks[0].node.id}` : 'universalAst:source',
|
|
112
116
|
nativeSourceIds: blocks.map((block) => block.node.id),
|
|
@@ -114,7 +118,74 @@ function mergeNativeSourceBlocks(blocks) {
|
|
|
114
118
|
mergeCandidates: blocks.flatMap((block) => block.mergeCandidates ?? []),
|
|
115
119
|
evidence: blocks.flatMap((block) => block.evidence ?? []),
|
|
116
120
|
losses: blocks.flatMap((block) => block.losses ?? []),
|
|
117
|
-
|
|
121
|
+
packageManifests: packageManifestBlocks,
|
|
122
|
+
canvasSurfaces: canvasSurfaceBlocks,
|
|
123
|
+
applicationSurfaces: applicationSurfaceBlocks,
|
|
124
|
+
packageManifestIds: ids(packageManifestBlocks),
|
|
125
|
+
canvasSurfaceIds: ids(canvasSurfaceBlocks),
|
|
126
|
+
applicationSurfaceIds: ids(applicationSurfaceBlocks),
|
|
127
|
+
metadata: {
|
|
128
|
+
authoredNativeSourceIds: blocks.map((block) => block.node.id),
|
|
129
|
+
authoredPackageManifestIds: ids(packageManifestBlocks),
|
|
130
|
+
authoredCanvasSurfaceIds: ids(canvasSurfaceBlocks),
|
|
131
|
+
authoredApplicationSurfaceIds: ids(applicationSurfaceBlocks)
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function mergePackageManifestBlocks(blocks) {
|
|
137
|
+
return {
|
|
138
|
+
id: blocks.length === 1 ? blocks[0].id : 'packageManifests:source',
|
|
139
|
+
manifests: blocks,
|
|
140
|
+
manifestIds: ids(blocks),
|
|
141
|
+
recordIds: blocks.flatMap((block) => ids(block.records)),
|
|
142
|
+
evidenceIds: blocks.flatMap((block) => ids(block.evidence)),
|
|
143
|
+
proofGapCodes: [...new Set(blocks.flatMap((block) => (block.proofGaps ?? []).map((gap) => gap.code).filter(Boolean)))],
|
|
144
|
+
summary: {
|
|
145
|
+
manifestCount: blocks.length,
|
|
146
|
+
recordCount: blocks.reduce((sum, block) => sum + (block.records?.length ?? 0), 0),
|
|
147
|
+
dependencyCount: blocks.reduce((sum, block) => sum + (block.summary?.dependencies ?? 0), 0),
|
|
148
|
+
scriptCount: blocks.reduce((sum, block) => sum + (block.summary?.scripts ?? 0), 0),
|
|
149
|
+
exportCount: blocks.reduce((sum, block) => sum + (block.summary?.exports ?? 0), 0),
|
|
150
|
+
proofGapCount: blocks.reduce((sum, block) => sum + (block.proofGaps?.length ?? 0), 0)
|
|
151
|
+
},
|
|
152
|
+
claims: {
|
|
153
|
+
autoMergeClaim: false,
|
|
154
|
+
semanticEquivalenceClaim: false,
|
|
155
|
+
packageInstallEquivalenceClaim: false,
|
|
156
|
+
installEquivalenceClaim: false,
|
|
157
|
+
runtimeEquivalenceClaim: false
|
|
158
|
+
},
|
|
159
|
+
metadata: { authoredPackageManifestIds: ids(blocks) }
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function mergeCanvasSurfaceBlocks(blocks) {
|
|
164
|
+
return {
|
|
165
|
+
id: blocks.length === 1 ? blocks[0].id : 'canvasSurfaces:source',
|
|
166
|
+
surfaces: blocks,
|
|
167
|
+
surfaceIds: ids(blocks),
|
|
168
|
+
recordIds: blocks.flatMap((block) => ids(block.records)),
|
|
169
|
+
commandTraceIds: blocks.flatMap((block) => ids(block.commandTraces)),
|
|
170
|
+
evidenceIds: blocks.flatMap((block) => ids(block.evidence)),
|
|
171
|
+
proofGapCodes: [...new Set(blocks.flatMap((block) => (block.proofGaps ?? []).map((gap) => gap.code).filter(Boolean)))],
|
|
172
|
+
summary: {
|
|
173
|
+
surfaceCount: blocks.length,
|
|
174
|
+
recordCount: blocks.reduce((sum, block) => sum + (block.records?.length ?? 0), 0),
|
|
175
|
+
commandTraceCount: blocks.reduce((sum, block) => sum + (block.commandTraces?.length ?? 0), 0),
|
|
176
|
+
drawCommandCount: blocks.reduce((sum, block) => sum + (block.summary?.drawCommands ?? 0), 0),
|
|
177
|
+
offscreenCommandCount: blocks.reduce((sum, block) => sum + (block.summary?.offscreenCommands ?? 0), 0),
|
|
178
|
+
gpuCommandCount: blocks.reduce((sum, block) => sum + (block.summary?.gpuCommands ?? 0), 0),
|
|
179
|
+
proofGapCount: blocks.reduce((sum, block) => sum + (block.proofGaps?.length ?? 0), 0)
|
|
180
|
+
},
|
|
181
|
+
claims: {
|
|
182
|
+
autoMergeClaim: false,
|
|
183
|
+
semanticEquivalenceClaim: false,
|
|
184
|
+
browserRuntimeEquivalenceClaim: false,
|
|
185
|
+
canvasRuntimeEquivalenceClaim: false,
|
|
186
|
+
canvasVisualEquivalenceClaim: false
|
|
187
|
+
},
|
|
188
|
+
metadata: { authoredCanvasSurfaceIds: ids(blocks) }
|
|
118
189
|
};
|
|
119
190
|
}
|
|
120
191
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
|
|
3
|
+
export function parsePackageManifestBlock(block) {
|
|
4
|
+
const name = nameFrom(block.header);
|
|
5
|
+
const sourcePath = readLine('sourcePath', block.body) ?? readLine('path', block.body) ?? 'package.json';
|
|
6
|
+
const sourceHash = readLine('sourceHash', block.body);
|
|
7
|
+
const evidence = parseEvidenceRows(block.body);
|
|
8
|
+
const records = [];
|
|
9
|
+
const proofGaps = [];
|
|
10
|
+
for (const rawLine of block.body.split('\n')) {
|
|
11
|
+
const line = rawLine.trim();
|
|
12
|
+
if (!line || line.startsWith('#') || isPropertyLine(line)) continue;
|
|
13
|
+
const match = /^(metadata|dependency|script|export|gap|proofGap)\s+([A-Za-z_$.*@/][\w$./@*-]*)(.*)$/.exec(line);
|
|
14
|
+
if (!match) continue;
|
|
15
|
+
const [, rowKind, rowName, rest] = match;
|
|
16
|
+
if (rowKind === 'gap' || rowKind === 'proofGap') {
|
|
17
|
+
proofGaps.push(packageProofGap(rowName, rest));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const record = packageRecord(rowKind, rowName, rest, { sourcePath, sourceHash, evidence });
|
|
21
|
+
if (record) records.push(record);
|
|
22
|
+
}
|
|
23
|
+
const recordGaps = records.flatMap((record) => record.proofGaps ?? []);
|
|
24
|
+
const allGaps = [...recordGaps, ...proofGaps];
|
|
25
|
+
const tree = {
|
|
26
|
+
kind: 'frontier.lang.packageManifestSemanticTree',
|
|
27
|
+
version: 1,
|
|
28
|
+
id: idFrom(block.header, `package_manifest_${name}`),
|
|
29
|
+
name,
|
|
30
|
+
sourcePath,
|
|
31
|
+
sourceHash,
|
|
32
|
+
packageManager: readLine('packageManager', block.body),
|
|
33
|
+
records,
|
|
34
|
+
proofGaps: allGaps,
|
|
35
|
+
evidence,
|
|
36
|
+
parser: { status: 'authored', errors: [] },
|
|
37
|
+
claims: {
|
|
38
|
+
autoMergeClaim: false,
|
|
39
|
+
semanticEquivalenceClaim: false,
|
|
40
|
+
packageInstallEquivalenceClaim: false,
|
|
41
|
+
installEquivalenceClaim: false,
|
|
42
|
+
runtimeEquivalenceClaim: false
|
|
43
|
+
},
|
|
44
|
+
metadata: { authoredName: name }
|
|
45
|
+
};
|
|
46
|
+
tree.summary = summarizePackageManifest(tree);
|
|
47
|
+
tree.treeHash = hashSemanticValue({ kind: 'frontier.lang.package.authoredTree.v1', records: records.map(hashableRecord), proofGaps: allGaps.map((gap) => gap.code) });
|
|
48
|
+
return tree;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function packageRecord(kind, name, text, context) {
|
|
52
|
+
const section = readInlineWord('section', text) ?? defaultSection(kind);
|
|
53
|
+
const recordName = readInlineWord('name', text) ?? name;
|
|
54
|
+
const value = packageValue(kind, text);
|
|
55
|
+
const proofGaps = readInlineList(text, 'proofGap', 'proofGaps', 'gap', 'gaps')?.map((code) => packageProofGap(code, '')) ?? [];
|
|
56
|
+
return cleanRecord({
|
|
57
|
+
kind,
|
|
58
|
+
id: idFrom(text, `package_${kind}_${safeId(section)}_${safeId(recordName)}`),
|
|
59
|
+
section,
|
|
60
|
+
name: recordName,
|
|
61
|
+
value,
|
|
62
|
+
valueHash: hashSemanticValue({ kind: 'frontier.lang.package.authoredRecordValue.v1', value }),
|
|
63
|
+
identityKey: readInlineWord('identity', text) ?? readInlineWord('identityKey', text) ?? `${kind}:${section}:${recordName}`,
|
|
64
|
+
sourcePath: readInlineWord('sourcePath', text) ?? readInlineWord('path', text) ?? context.sourcePath,
|
|
65
|
+
sourceHash: readInlineWord('sourceHash', text) ?? context.sourceHash,
|
|
66
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text)),
|
|
67
|
+
evidenceIds: readInlineList(text, 'evidence', 'evidenceIds'),
|
|
68
|
+
proofGaps
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function packageValue(kind, text) {
|
|
73
|
+
if (kind === 'dependency') return readInlineQuoted('value', text) ?? readInlineWord('range', text) ?? readInlineWord('value', text) ?? '*';
|
|
74
|
+
if (kind === 'script') return readInlineQuoted('command', text) ?? readInlineQuoted('value', text) ?? readInlineWord('command', text) ?? readInlineWord('value', text);
|
|
75
|
+
if (kind === 'export') return readInlineQuoted('target', text) ?? readInlineQuoted('value', text) ?? readInlineWord('target', text) ?? readInlineWord('value', text);
|
|
76
|
+
return readInlineQuoted('value', text) ?? readInlineWord('value', text);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function defaultSection(kind) {
|
|
80
|
+
if (kind === 'dependency') return 'dependencies';
|
|
81
|
+
if (kind === 'script') return 'scripts';
|
|
82
|
+
if (kind === 'export') return 'exports';
|
|
83
|
+
return 'metadata';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function packageProofGap(name, text) {
|
|
87
|
+
const code = readInlineWord('code', text) ?? name;
|
|
88
|
+
return cleanRecord({
|
|
89
|
+
id: idFrom(text, `package_gap_${safeId(code)}`),
|
|
90
|
+
code,
|
|
91
|
+
status: readInlineWord('status', text) ?? 'not-claimed',
|
|
92
|
+
summary: readInlineQuoted('summary', text) ?? readInlineQuoted('message', text),
|
|
93
|
+
failClosed: true,
|
|
94
|
+
semanticEquivalenceClaim: false,
|
|
95
|
+
packageInstallEquivalenceClaim: false,
|
|
96
|
+
installEquivalenceClaim: false,
|
|
97
|
+
runtimeEquivalenceClaim: false,
|
|
98
|
+
sourceSpan: parseSpan(readInlineWord('sourceSpan', text))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseEvidenceRows(body) {
|
|
103
|
+
const records = [];
|
|
104
|
+
for (const rawLine of body.split('\n')) {
|
|
105
|
+
const line = rawLine.trim();
|
|
106
|
+
const match = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
107
|
+
if (!match) continue;
|
|
108
|
+
records.push(cleanRecord({
|
|
109
|
+
id: idFrom(match[2], `evidence_${match[1]}`),
|
|
110
|
+
kind: readInlineWord('kind', match[2]) ?? 'note',
|
|
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;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function summarizePackageManifest(tree) {
|
|
121
|
+
return {
|
|
122
|
+
metadata: tree.records.filter((record) => record.kind === 'metadata').length,
|
|
123
|
+
dependencies: tree.records.filter((record) => record.kind === 'dependency').length,
|
|
124
|
+
scripts: tree.records.filter((record) => record.kind === 'script').length,
|
|
125
|
+
exports: tree.records.filter((record) => record.kind === 'export').length,
|
|
126
|
+
proofGaps: tree.proofGaps.length,
|
|
127
|
+
parseErrors: tree.parser.errors.length
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function hashableRecord(record) {
|
|
132
|
+
return { kind: record.kind, section: record.section, name: record.name, value: record.value, proofGaps: record.proofGaps?.map((gap) => gap.code) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isPropertyLine(line) {
|
|
136
|
+
return /^(sourcePath|path|sourceHash|packageManager|evidence|proofEvidence)\s+/.test(line);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function idFrom(text, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(text)?.[1] ?? fallback; }
|
|
140
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$-]*)/.exec(header)?.[1] ?? 'PackageManifest'; }
|
|
141
|
+
function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
|
|
142
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
143
|
+
function readInlineQuoted(label, text) { return new RegExp("(?:^|\\s)" + label + "\\s+[\"']([^\"']+)[\"']").exec(text)?.[1]?.trim(); }
|
|
144
|
+
function readInlineList(text, ...labels) {
|
|
145
|
+
for (const label of labels) {
|
|
146
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+([^\\s]+)').exec(text)?.[1]?.trim();
|
|
147
|
+
if (value) return value.split(/[|,]/).map((item) => item.trim()).filter(Boolean);
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
function parseSpan(value) {
|
|
152
|
+
if (!value) return undefined;
|
|
153
|
+
const match = /^(.+?):(\d+):(\d+)-(\d+):(\d+)$/.exec(value);
|
|
154
|
+
if (!match) return { path: value };
|
|
155
|
+
return { path: match[1], startLine: Number(match[2]), startColumn: Number(match[3]), endLine: Number(match[4]), endColumn: Number(match[5]) };
|
|
156
|
+
}
|
|
157
|
+
function cleanRecord(record) {
|
|
158
|
+
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)));
|
|
159
|
+
}
|
|
160
|
+
function safeId(value) {
|
|
161
|
+
return String(value ?? 'unknown').replace(/[^A-Za-z0-9_$-]+/g, '_');
|
|
162
|
+
}
|
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.29",
|
|
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/conversion-constraint-fields-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/conversion-constraint-fields-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",
|