@shapeshift-labs/frontier-lang-parser 0.3.39 → 0.3.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/action-body.js +62 -0
- package/dist/conversion-metadata.js +43 -0
- package/dist/conversion.js +25 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +11 -5
- package/dist/metadata.js +1 -18
- package/dist/source-syntax-children.js +193 -14
- package/dist/source-syntax-report.js +11 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ The published Frontier package family is generated from one shared package catal
|
|
|
81
81
|
- [`@shapeshift-labs/frontier-trace`](https://www.npmjs.com/package/@shapeshift-labs/frontier-trace): Serializable traces, spans, events, causal links, W3C trace context helpers, timeline/resource/path queries, critical-path analysis, registry graph output, JSONL/proof helpers, Chrome trace export, and redaction for app-wide feature observability.
|
|
82
82
|
- [`@shapeshift-labs/frontier-manifest`](https://www.npmjs.com/package/@shapeshift-labs/frontier-manifest): Build/static feature manifests for owners, routes, actions, states, migrations, tests, source files, assets, resources, tasks, dependency metadata, registry graph output, feature maps, JSONL export, and impact queries.
|
|
83
83
|
- [`@shapeshift-labs/frontier-view`](https://www.npmjs.com/package/@shapeshift-labs/frontier-view): Renderer-neutral view manifests, type defaults, validation frames, action bindings, visual channels, virtual/LOD hints, and data-to-representation mapping for Frontier apps.
|
|
84
|
+
- [`@shapeshift-labs/frontier-collapse`](https://www.npmjs.com/package/@shapeshift-labs/frontier-collapse): Constraint-collapse and projection admission records for one Frontier program becoming many valid shapes, including identity, behavior, runtime, proof, explicit projection losses, admitted projections, rejected shapes, and target contracts for React, SwiftUI, CLI, worker routes, plugin mounts, native adapters, and local-first views.
|
|
84
85
|
- [`@shapeshift-labs/frontier-icons`](https://www.npmjs.com/package/@shapeshift-labs/frontier-icons): Renderer-neutral icon records, icon sets, lookup aliases, SVG frames, string rendering, and registry evidence for Frontier apps.
|
|
85
86
|
- [`@shapeshift-labs/frontier-design`](https://www.npmjs.com/package/@shapeshift-labs/frontier-design): Renderer-neutral design-system tokens, semantic roles, recipes, target style frames, CSS variable output, and registry graph evidence for Frontier apps.
|
|
86
87
|
- [`@shapeshift-labs/frontier-canvas`](https://www.npmjs.com/package/@shapeshift-labs/frontier-canvas): Renderer-neutral infinite canvas surfaces for Frontier apps, including camera and viewport math, pan/zoom plans, grid materialization, snapping, hit testing, selection handles, extensible tool dispatch, frame records, registry graph output, and impact/proof helpers.
|
|
@@ -88,6 +89,8 @@ The published Frontier package family is generated from one shared package catal
|
|
|
88
89
|
- [`@shapeshift-labs/frontier-dnd`](https://www.npmjs.com/package/@shapeshift-labs/frontier-dnd): Renderer-neutral drag-and-drop sessions, sensor descriptors, collision ranking, drop planning, reorder patches, state partitioning, and registry evidence for Frontier apps.
|
|
89
90
|
- [`@shapeshift-labs/frontier-dom`](https://www.npmjs.com/package/@shapeshift-labs/frontier-dom): Patch-native DOM and host renderer bindings, manifest hydration, JSX runtime/compiler helpers, SSR, devtools, and logging bridges.
|
|
90
91
|
- [`@shapeshift-labs/frontier-playwright`](https://www.npmjs.com/package/@shapeshift-labs/frontier-playwright): Playwright/headless automation probes for Frontier state, DOM, devtools, marks, and timeline queries.
|
|
92
|
+
- [`@shapeshift-labs/frontier-annotations`](https://www.npmjs.com/package/@shapeshift-labs/frontier-annotations): Browser harness DOM annotation overlay, selected-node targeting records, click geometry, optional image media refs, CSS/source-hint capture, AST/source graph context ranking, and Codex/frontier-swarm task planning for AI-operable UI fixes.
|
|
93
|
+
- [`@shapeshift-labs/frontier-annotations-harness`](https://www.npmjs.com/package/@shapeshift-labs/frontier-annotations-harness): Node browser annotation harness server and CLI for injecting Frontier annotation threads, capturing selected-element screenshots and canvas click crops, collecting source context, writing Loom/frontier-swarm Codex task artifacts, and optionally starting swarm runs.
|
|
91
94
|
- [`@shapeshift-labs/frontier-test`](https://www.npmjs.com/package/@shapeshift-labs/frontier-test): Serializable test/spec evidence manifests for Frontier apps, including fixtures, commands, expected patches/effects/routes/policies, coverage declarations, run plans, run records, report adapters, replay proofs, fuzzers, benchmarks, registry graph output, and impact queries.
|
|
92
95
|
- [`@shapeshift-labs/frontier-fixtures`](https://www.npmjs.com/package/@shapeshift-labs/frontier-fixtures): Deterministic fixture and scenario generation for Frontier apps, including schema-valid sample state, related entity collections, actor personas, route states, replay-verified patch streams, event records, JSONL bundles, and evidence summaries.
|
|
93
96
|
- [`@shapeshift-labs/frontier-component-preview`](https://www.npmjs.com/package/@shapeshift-labs/frontier-component-preview): Frontier-native component preview books, generated preview manifests, stateful variants, Vite virtual modules, standalone browser preview shells, inspector bridges, and preview harness evidence for Frontier apps.
|
|
@@ -185,6 +188,7 @@ Package source repositories:
|
|
|
185
188
|
- [`siliconjungle/-shapeshift-labs-frontier-trace`](https://github.com/siliconjungle/-shapeshift-labs-frontier-trace)
|
|
186
189
|
- [`siliconjungle/-shapeshift-labs-frontier-manifest`](https://github.com/siliconjungle/-shapeshift-labs-frontier-manifest)
|
|
187
190
|
- [`siliconjungle/-shapeshift-labs-frontier-view`](https://github.com/siliconjungle/-shapeshift-labs-frontier-view)
|
|
191
|
+
- [`siliconjungle/-shapeshift-labs-frontier-collapse`](https://github.com/siliconjungle/-shapeshift-labs-frontier-collapse)
|
|
188
192
|
- [`siliconjungle/-shapeshift-labs-frontier-icons`](https://github.com/siliconjungle/-shapeshift-labs-frontier-icons)
|
|
189
193
|
- [`siliconjungle/-shapeshift-labs-frontier-design`](https://github.com/siliconjungle/-shapeshift-labs-frontier-design)
|
|
190
194
|
- [`siliconjungle/-shapeshift-labs-frontier-canvas`](https://github.com/siliconjungle/-shapeshift-labs-frontier-canvas)
|
|
@@ -192,6 +196,8 @@ Package source repositories:
|
|
|
192
196
|
- [`siliconjungle/-shapeshift-labs-frontier-dnd`](https://github.com/siliconjungle/-shapeshift-labs-frontier-dnd)
|
|
193
197
|
- [`siliconjungle/-shapeshift-labs-frontier-dom`](https://github.com/siliconjungle/-shapeshift-labs-frontier-dom)
|
|
194
198
|
- [`siliconjungle/-shapeshift-labs-frontier-playwright`](https://github.com/siliconjungle/-shapeshift-labs-frontier-playwright)
|
|
199
|
+
- [`siliconjungle/-shapeshift-labs-frontier-annotations`](https://github.com/siliconjungle/-shapeshift-labs-frontier-annotations)
|
|
200
|
+
- [`siliconjungle/-shapeshift-labs-frontier-annotations-harness`](https://github.com/siliconjungle/-shapeshift-labs-frontier-annotations-harness)
|
|
195
201
|
- [`siliconjungle/-shapeshift-labs-frontier-test`](https://github.com/siliconjungle/-shapeshift-labs-frontier-test)
|
|
196
202
|
- [`siliconjungle/-shapeshift-labs-frontier-fixtures`](https://github.com/siliconjungle/-shapeshift-labs-frontier-fixtures)
|
|
197
203
|
- [`siliconjungle/-shapeshift-labs-frontier-component-preview`](https://github.com/siliconjungle/-shapeshift-labs-frontier-component-preview)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { readFrontierNestedBlocks } from './source-syntax-report.js';
|
|
2
|
+
|
|
3
|
+
export function readActionBodyRecords(body) {
|
|
4
|
+
const records = [];
|
|
5
|
+
for (const bodyBlock of readFrontierNestedBlocks('body', body)) {
|
|
6
|
+
for (const rawLine of bodyBlock.body.split('\n')) {
|
|
7
|
+
const line = rawLine.trim();
|
|
8
|
+
if (!line || line.startsWith('#')) continue;
|
|
9
|
+
const record = parseActionBodyLine(line, records.length);
|
|
10
|
+
if (record) records.push(record);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return records;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function stripNestedBlocks(kind, source) {
|
|
17
|
+
let text = source;
|
|
18
|
+
for (const block of readFrontierNestedBlocks(kind, source).reverse()) text = text.slice(0, block.start) + text.slice(block.end);
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseActionBodyLine(line, index) {
|
|
23
|
+
const statement = /^([A-Za-z_$][\w$-]*)(?:\s+([A-Za-z_$@./:*+-][\w$./@:*+-]*))?(.*)$/.exec(line);
|
|
24
|
+
if (!statement) return undefined;
|
|
25
|
+
const [, rowKind, rawName, rawRest] = statement;
|
|
26
|
+
const name = rawName && !rawName.startsWith('@') ? rawName : `${rowKind}_${index}`;
|
|
27
|
+
const rest = rawName?.startsWith('@') ? ` ${rawName}${rawRest ?? ''}` : (rawRest ?? '');
|
|
28
|
+
if (rowKind === 'set' || rowKind === 'insert' || rowKind === 'merge') {
|
|
29
|
+
return compactRecord({ kind: 'patch', op: rowKind, id: idFrom(rest, `action_body_${rowKind}_${name}`), name, path: readInlineWord('path', rest), value: readInlineActionValue('value', rest) });
|
|
30
|
+
}
|
|
31
|
+
if (rowKind === 'remove') {
|
|
32
|
+
return compactRecord({ kind: 'patch', op: 'remove', id: idFrom(rest, `action_body_remove_${name}`), name, path: readInlineWord('path', rest) });
|
|
33
|
+
}
|
|
34
|
+
if (rowKind === 'callEffect') {
|
|
35
|
+
return compactRecord({ kind: 'callEffect', id: idFrom(rest, `action_body_callEffect_${name}`), name, capability: readInlineWord('capability', rest) ?? readInlineWord('effect', rest) ?? name, input: readInlineActionValue('input', rest) });
|
|
36
|
+
}
|
|
37
|
+
if (rowKind === 'return') {
|
|
38
|
+
const valueText = rawName?.startsWith('@') ? rest.trim() : `${rawName ?? ''}${rest ?? ''}`.trim();
|
|
39
|
+
return compactRecord({ kind: 'return', id: idFrom(rest, `action_body_return_${index}`), value: valueText ? readActionValue(valueText) : undefined });
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readInlineActionValue(label, text) {
|
|
45
|
+
const value = new RegExp('(?:^|\\s)' + label + '\\s+(.+?)(?=\\s+[A-Za-z_$][\\w$-]*\\s+|$)').exec(text)?.[1]?.trim();
|
|
46
|
+
return value ? readActionValue(value) : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readActionValue(value) {
|
|
50
|
+
const text = value.trim();
|
|
51
|
+
const quoted = /^["']([^"']*)["']$/.exec(text);
|
|
52
|
+
if (quoted) return { value: quoted[1] };
|
|
53
|
+
if (text === 'true') return { value: true };
|
|
54
|
+
if (text === 'false') return { value: false };
|
|
55
|
+
if (text === 'null') return { value: null };
|
|
56
|
+
if (/^-?\d+(?:\.\d+)?$/.test(text)) return { value: Number(text) };
|
|
57
|
+
return { expression: text };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function idFrom(header, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(header)?.[1] ?? fallback; }
|
|
61
|
+
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
62
|
+
function compactRecord(record) { return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0))); }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function mergeConversionBlocks(blocks) {
|
|
2
|
+
const plan = {
|
|
3
|
+
id: blocks.length === 1 ? blocks[0].id : 'universalConversionPlan:source',
|
|
4
|
+
targets: [...new Set(blocks.flatMap((block) => block.targets ?? []))],
|
|
5
|
+
metadata: { authoredConversionBlockIds: blocks.map((block) => block.id) }
|
|
6
|
+
};
|
|
7
|
+
for (const block of blocks) {
|
|
8
|
+
if (block.sourceLanguage && !plan.sourceLanguage) plan.sourceLanguage = block.sourceLanguage;
|
|
9
|
+
for (const [key, value] of Object.entries(block)) {
|
|
10
|
+
if (Array.isArray(value) && key !== 'targets') plan[key] = [...(plan[key] ?? []), ...value];
|
|
11
|
+
else if ((key === 'sourceRuntimes' || key === 'targetRuntimes') && value && typeof value === 'object') {
|
|
12
|
+
plan[key] = { ...(plan[key] ?? {}), ...value };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
plan.evidenceIds = ids(plan.evidence);
|
|
17
|
+
plan.summary = {
|
|
18
|
+
evidenceCount: plan.evidence?.length ?? 0,
|
|
19
|
+
runtimeRequirementCount: plan.runtimeRequirements?.length ?? 0,
|
|
20
|
+
dialectCount: plan.dialects?.length ?? 0,
|
|
21
|
+
externCount: plan.externs?.length ?? 0,
|
|
22
|
+
constraintCount: conversionConstraintCount(plan)
|
|
23
|
+
};
|
|
24
|
+
plan.claims = {
|
|
25
|
+
autoMergeClaim: false,
|
|
26
|
+
semanticEquivalenceClaim: false,
|
|
27
|
+
conversionEquivalenceClaim: false,
|
|
28
|
+
runtimeEquivalenceClaim: false
|
|
29
|
+
};
|
|
30
|
+
return plan;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function ids(records = []) {
|
|
34
|
+
return records.map((record) => record?.id).filter(Boolean);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function conversionConstraintCount(plan) {
|
|
38
|
+
return Object.entries(plan).reduce((total, [key, value]) => {
|
|
39
|
+
if (!Array.isArray(value)) return total;
|
|
40
|
+
if (key.endsWith('Constraints') || key === 'resourceTransfers') return total + value.length;
|
|
41
|
+
return total;
|
|
42
|
+
}, 0);
|
|
43
|
+
}
|
package/dist/conversion.js
CHANGED
|
@@ -13,6 +13,7 @@ export function parseConversionBlock(block) {
|
|
|
13
13
|
const runtimeRequirement = /^(?:runtimeRequirement|requiredRuntime|requiresRuntime)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
14
14
|
const dialect = /^dialect\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
15
15
|
const extern = /^extern\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
16
|
+
const evidence = /^(?:evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
16
17
|
const constraint = /^constraint\s+([A-Za-z_$][\w$-]*)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line);
|
|
17
18
|
if (target) plan.targets.push(target);
|
|
18
19
|
else if (sourceLanguage) plan.sourceLanguage = sourceLanguage;
|
|
@@ -21,11 +22,35 @@ export function parseConversionBlock(block) {
|
|
|
21
22
|
else if (runtimeRequirement) addRuntimeRequirement(plan, runtimeRequirement[1], runtimeRequirement[2]);
|
|
22
23
|
else if (dialect) addDialectRecord(plan, dialect[1], dialect[2], false);
|
|
23
24
|
else if (extern) addDialectRecord(plan, extern[1], extern[2], true);
|
|
25
|
+
else if (evidence) addEvidenceRecord(plan, evidence[1], evidence[2], authoredLine);
|
|
24
26
|
else if (constraint) addConstraint(plan, constraint[1], constraint[2], constraint[3], authoredLine);
|
|
25
27
|
}
|
|
26
28
|
return cleanRecord({ ...plan, targets: unique(plan.targets) });
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
function addEvidenceRecord(plan, name, text, authoredLine = {}) {
|
|
32
|
+
plan.evidence = [...(plan.evidence ?? []), cleanRecord({
|
|
33
|
+
id: idFrom(text, `conversion_evidence_${name}`),
|
|
34
|
+
name,
|
|
35
|
+
kind: readInlineWord('kind', text) ?? 'conversion-route-evidence',
|
|
36
|
+
status: readInlineWord('status', text) ?? 'unknown',
|
|
37
|
+
sourceLanguage: readInlineWord('sourceLanguage', text) ?? readInlineWord('language', text) ?? plan.sourceLanguage,
|
|
38
|
+
target: readInlineWord('target', text) ?? readInlineWord('targetLanguage', text) ?? plan.targets[0],
|
|
39
|
+
routeId: readInlineWord('routeId', text) ?? readInlineWord('route', text),
|
|
40
|
+
path: readInlineWord('path', text) ?? readInlineWord('report', text),
|
|
41
|
+
command: readInlineQuoted('command', text) ?? readInlineWord('command', text),
|
|
42
|
+
probeId: readInlineWord('probeId', text) ?? readInlineWord('probe', text),
|
|
43
|
+
sourceHash: readInlineWord('sourceHash', text),
|
|
44
|
+
targetHash: readInlineWord('targetHash', text),
|
|
45
|
+
telemetryHash: readInlineWord('telemetryHash', text),
|
|
46
|
+
proofEvidenceIds: readInlineList(text, 'proofEvidence', 'proofEvidenceId', 'proofEvidenceIds', 'proof'),
|
|
47
|
+
summary: readInlineQuoted('summary', text),
|
|
48
|
+
sourceSpan: authoredLine.sourceSpan,
|
|
49
|
+
authoredSourceSpan: authoredLine.sourceSpan,
|
|
50
|
+
metadata: { authoredConversionBlockId: plan.id, autoMergeClaim: false, semanticEquivalenceClaim: false }
|
|
51
|
+
})];
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
function addRuntimeRequirement(plan, name, text) {
|
|
30
55
|
plan.runtimeRequirements = [...(plan.runtimeRequirements ?? []), cleanRecord({
|
|
31
56
|
id: idFrom(text, `runtime_requirement_${name}`),
|
package/dist/index.d.ts
CHANGED
|
@@ -22,11 +22,14 @@ export interface FrontierSourceSyntaxDiagnostic {
|
|
|
22
22
|
readonly location: FrontierSourcePosition;
|
|
23
23
|
}
|
|
24
24
|
export interface FrontierSourceChildSyntaxRecord {
|
|
25
|
-
readonly kind: 'conversionConstraint';
|
|
25
|
+
readonly kind: 'conversionConstraint' | 'conversionEvidence' | 'conversionRuntimeRequirement' | 'conversionDialect' | 'conversionExtern' | 'conversionUnknownRow' | string;
|
|
26
|
+
readonly rowKind?: string;
|
|
27
|
+
readonly normalizedRowKind?: string;
|
|
26
28
|
readonly name: string;
|
|
27
29
|
readonly id?: string;
|
|
28
30
|
readonly family?: string;
|
|
29
31
|
readonly role?: string;
|
|
32
|
+
readonly reason?: string;
|
|
30
33
|
readonly header: string;
|
|
31
34
|
readonly startOffset: number;
|
|
32
35
|
readonly endOffset: number;
|
|
@@ -60,6 +63,10 @@ export interface FrontierUnknownSourceBlockSyntaxRecord extends FrontierSourceBl
|
|
|
60
63
|
readonly recognized: false;
|
|
61
64
|
readonly reason: 'unsupported-top-level-block';
|
|
62
65
|
}
|
|
66
|
+
export interface FrontierUnknownSourceChildSyntaxRecord extends FrontierSourceChildSyntaxRecord {
|
|
67
|
+
readonly recognized: false;
|
|
68
|
+
readonly reason: string;
|
|
69
|
+
}
|
|
63
70
|
export interface FrontierSourceSyntaxReport {
|
|
64
71
|
readonly kind: 'frontier.lang.sourceSyntaxReport';
|
|
65
72
|
readonly version: 1;
|
|
@@ -68,10 +75,12 @@ export interface FrontierSourceSyntaxReport {
|
|
|
68
75
|
readonly blocks: readonly FrontierSourceBlockSyntaxRecord[];
|
|
69
76
|
readonly recognizedBlocks: readonly FrontierSourceBlockSyntaxRecord[];
|
|
70
77
|
readonly unknownBlocks: readonly FrontierUnknownSourceBlockSyntaxRecord[];
|
|
78
|
+
readonly unknownChildren: readonly FrontierUnknownSourceChildSyntaxRecord[];
|
|
71
79
|
readonly summary: {
|
|
72
80
|
readonly blockCount: number;
|
|
73
81
|
readonly recognizedBlockCount: number;
|
|
74
82
|
readonly unknownBlockCount: number;
|
|
83
|
+
readonly unknownChildCount: number;
|
|
75
84
|
readonly malformedBlockCount: number;
|
|
76
85
|
readonly childCount: number;
|
|
77
86
|
readonly recognizedChildCount: number;
|
|
@@ -79,6 +88,7 @@ export interface FrontierSourceSyntaxReport {
|
|
|
79
88
|
readonly recognizedKinds: readonly string[];
|
|
80
89
|
readonly recognizedChildKinds: readonly string[];
|
|
81
90
|
readonly unknownKinds: readonly string[];
|
|
91
|
+
readonly unknownChildKinds: readonly string[];
|
|
82
92
|
readonly failClosed: boolean;
|
|
83
93
|
readonly unsupportedSyntax: boolean;
|
|
84
94
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { actionNode, capabilityNode, createDocument, effectNode, entityNode, externNode, latticeNode, migrationNode, stateNode, targetNode, typeNode } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { readActionBodyRecords, stripNestedBlocks } from './action-body.js';
|
|
2
3
|
import { parseConstraintSpaceBlock } from './constraint-space.js';
|
|
3
4
|
import { parseConversionBlock } from './conversion.js';
|
|
4
5
|
import { parseApplicationSurfaceBlock } from './application-surface.js';
|
|
@@ -117,16 +118,21 @@ function parseState(block) {
|
|
|
117
118
|
}
|
|
118
119
|
function parseAction(block) {
|
|
119
120
|
const name = nameFrom(block.header);
|
|
121
|
+
const topLevelBody = stripNestedBlocks('body', block.body);
|
|
122
|
+
const body = readActionBodyRecords(block.body);
|
|
120
123
|
return actionNode({
|
|
121
124
|
id: idFrom(block.header, `action_${name}`),
|
|
122
125
|
name,
|
|
123
|
-
input: parseOptionalTypeExpression(/input\s*:?\s*([^\n]+)/.exec(
|
|
124
|
-
returns: parseOptionalTypeExpression(/returns\s+([^\n]+)/.exec(
|
|
125
|
-
reads: readList('reads',
|
|
126
|
-
writes: readList('writes',
|
|
127
|
-
uses: readList('uses',
|
|
126
|
+
input: parseOptionalTypeExpression(/input\s*:?\s*([^\n]+)/.exec(topLevelBody)?.[1]),
|
|
127
|
+
returns: parseOptionalTypeExpression(/returns\s+([^\n]+)/.exec(topLevelBody)?.[1]),
|
|
128
|
+
reads: readList('reads', topLevelBody),
|
|
129
|
+
writes: readList('writes', topLevelBody),
|
|
130
|
+
uses: readList('uses', topLevelBody),
|
|
131
|
+
throws: readList('throws', topLevelBody),
|
|
132
|
+
body: body.length ? body : undefined
|
|
128
133
|
});
|
|
129
134
|
}
|
|
135
|
+
|
|
130
136
|
function parseMigration(block) {
|
|
131
137
|
const name = nameFrom(block.header);
|
|
132
138
|
return migrationNode({ id: idFrom(block.header, `migration_${name}`), name, fromVersion: readWord('fromVersion', block.body) ?? readWord('from', block.body) ?? 'unknown', toVersion: readWord('toVersion', block.body) ?? readWord('to', block.body) ?? 'unknown', changes: readChangeRecords(block.body), invariants: readList('invariants', block.body) });
|
package/dist/metadata.js
CHANGED
|
@@ -2,6 +2,7 @@ import { mergeDialectRegistryBlocks } from './dialect-registry.js';
|
|
|
2
2
|
import { mergeApplicationSurfaceBlocks } from './application-surface.js';
|
|
3
3
|
import { mergeRuntimeCapabilityBlocks } from './runtime-capability.js';
|
|
4
4
|
import { mergeTargetProjectionTargets } from './target-projection-aggregate.js';
|
|
5
|
+
import { mergeConversionBlocks } from './conversion-metadata.js';
|
|
5
6
|
|
|
6
7
|
const PROOF_GROUPS = ['contracts', 'refinements', 'invariants', 'termination', 'temporal', 'obligations', 'artifacts', 'assumptions'];
|
|
7
8
|
const PARADIGM_GROUPS = [
|
|
@@ -61,24 +62,6 @@ function mergeOperationBlocks(blocks) {
|
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
function mergeConversionBlocks(blocks) {
|
|
65
|
-
const plan = {
|
|
66
|
-
id: blocks.length === 1 ? blocks[0].id : 'universalConversionPlan:source',
|
|
67
|
-
targets: [...new Set(blocks.flatMap((block) => block.targets ?? []))],
|
|
68
|
-
metadata: { authoredConversionBlockIds: blocks.map((block) => block.id) }
|
|
69
|
-
};
|
|
70
|
-
for (const block of blocks) {
|
|
71
|
-
if (block.sourceLanguage && !plan.sourceLanguage) plan.sourceLanguage = block.sourceLanguage;
|
|
72
|
-
for (const [key, value] of Object.entries(block)) {
|
|
73
|
-
if (Array.isArray(value) && key !== 'targets') plan[key] = [...(plan[key] ?? []), ...value];
|
|
74
|
-
else if ((key === 'sourceRuntimes' || key === 'targetRuntimes') && value && typeof value === 'object') {
|
|
75
|
-
plan[key] = { ...(plan[key] ?? {}), ...value };
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return plan;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
65
|
function mergeConstraintSpaceBlocks(blocks) {
|
|
83
66
|
return {
|
|
84
67
|
id: blocks.length === 1 ? blocks[0].id : 'constraintSpaces:source',
|
|
@@ -4,6 +4,9 @@ const ROW_NAME_PATTERN = '([A-Za-z_$@./:*+-][\\w$./@:*+-]*)';
|
|
|
4
4
|
|
|
5
5
|
export function readSourceSyntaxChildren(source, block, options = {}) {
|
|
6
6
|
if (block.malformed) return [];
|
|
7
|
+
if (block.kind === 'action') {
|
|
8
|
+
return readActionSyntaxChildren(source, block, options);
|
|
9
|
+
}
|
|
7
10
|
if (block.kind === 'conversion' || block.kind === 'universalConversionPlan') {
|
|
8
11
|
return readConversionSyntaxChildren(source, block, options);
|
|
9
12
|
}
|
|
@@ -12,35 +15,134 @@ export function readSourceSyntaxChildren(source, block, options = {}) {
|
|
|
12
15
|
return [];
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
const ACTION_BODY_ROWS = new Set(['set', 'insert', 'remove', 'merge', 'callEffect', 'return']);
|
|
19
|
+
|
|
20
|
+
function readActionSyntaxChildren(source, block, options) {
|
|
21
|
+
const children = [];
|
|
22
|
+
const body = source.slice(block.bodyStartOffset, block.bodyEndOffset);
|
|
23
|
+
const bodyBlocks = readNestedBodyBlocks('body', body);
|
|
24
|
+
let rowIndex = 0;
|
|
25
|
+
for (const bodyBlock of bodyBlocks) {
|
|
26
|
+
const bodyStartOffset = block.bodyStartOffset + bodyBlock.bodyStart;
|
|
27
|
+
const bodyEndOffset = block.bodyStartOffset + bodyBlock.bodyEnd;
|
|
28
|
+
for (const line of readTextLines(source, bodyStartOffset, bodyEndOffset)) {
|
|
29
|
+
if (!line.text || line.text.startsWith('#')) continue;
|
|
30
|
+
const row = /^([A-Za-z_$][\w$-]*)(?:\s+([A-Za-z_$@./:*+-][\w$./@:*+-]*))?(.*)$/.exec(line.text);
|
|
31
|
+
const rowKind = row?.[1] ?? 'unknown';
|
|
32
|
+
const name = row?.[2] && !row[2].startsWith('@') ? row[2] : `${rowKind}_${rowIndex}`;
|
|
33
|
+
const rest = row?.[2]?.startsWith('@') ? ` ${row[2]}${row[3] ?? ''}` : (row?.[3] ?? '');
|
|
34
|
+
const recognized = ACTION_BODY_ROWS.has(rowKind);
|
|
35
|
+
children.push(cleanRecord({
|
|
36
|
+
kind: recognized ? 'actionBodyRow' : 'actionUnknownRow',
|
|
37
|
+
rowKind,
|
|
38
|
+
normalizedRowKind: recognized ? rowKind : 'unknown',
|
|
39
|
+
name,
|
|
40
|
+
id: idFrom(rest, `action_body_${safeId(rowKind)}_${safeId(name)}_${rowIndex}`),
|
|
41
|
+
header: line.text,
|
|
42
|
+
startOffset: line.startOffset,
|
|
43
|
+
endOffset: line.endOffset,
|
|
44
|
+
location: sourcePosition(source, line.startOffset),
|
|
45
|
+
parentKind: block.kind,
|
|
46
|
+
parentId: block.id,
|
|
47
|
+
parentName: block.name,
|
|
48
|
+
moduleId: block.moduleId,
|
|
49
|
+
moduleName: block.moduleName,
|
|
50
|
+
sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options),
|
|
51
|
+
recognized,
|
|
52
|
+
reason: recognized ? undefined : 'unsupported-action-body-row'
|
|
53
|
+
}));
|
|
54
|
+
rowIndex++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return children;
|
|
58
|
+
}
|
|
59
|
+
|
|
15
60
|
function readConversionSyntaxChildren(source, block, options) {
|
|
16
61
|
const children = [];
|
|
17
62
|
for (const line of readBodyLines(source, block)) {
|
|
18
63
|
if (!line.text || line.text.startsWith('#')) continue;
|
|
64
|
+
const planField = /^(?:sourceLanguage|source|target|sourceRuntime|targetRuntime)\s+/.exec(line.text);
|
|
65
|
+
if (planField) continue;
|
|
66
|
+
const runtimeRequirement = /^(runtimeRequirement|requiredRuntime|requiresRuntime)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line.text);
|
|
67
|
+
if (runtimeRequirement) {
|
|
68
|
+
const [, rowKind, name, rest] = runtimeRequirement;
|
|
69
|
+
children.push(conversionChild(source, block, options, line, {
|
|
70
|
+
kind: 'conversionRuntimeRequirement',
|
|
71
|
+
rowKind,
|
|
72
|
+
normalizedRowKind: 'runtimeRequirement',
|
|
73
|
+
name,
|
|
74
|
+
id: idFrom(rest, `runtime_requirement_${name}`)
|
|
75
|
+
}));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const dialect = /^(dialect|extern)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line.text);
|
|
79
|
+
if (dialect) {
|
|
80
|
+
const [, rowKind, name, rest] = dialect;
|
|
81
|
+
children.push(conversionChild(source, block, options, line, {
|
|
82
|
+
kind: rowKind === 'extern' ? 'conversionExtern' : 'conversionDialect',
|
|
83
|
+
rowKind,
|
|
84
|
+
normalizedRowKind: rowKind,
|
|
85
|
+
name,
|
|
86
|
+
id: idFrom(rest, `${rowKind}_${name}`)
|
|
87
|
+
}));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const evidence = /^(evidence|proofEvidence)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line.text);
|
|
91
|
+
if (evidence) {
|
|
92
|
+
const [, rowKind, name, rest] = evidence;
|
|
93
|
+
children.push(conversionChild(source, block, options, line, {
|
|
94
|
+
kind: 'conversionEvidence',
|
|
95
|
+
rowKind,
|
|
96
|
+
normalizedRowKind: 'evidence',
|
|
97
|
+
name,
|
|
98
|
+
id: idFrom(rest, `conversion_evidence_${name}`)
|
|
99
|
+
}));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
19
102
|
const constraint = /^constraint\s+([A-Za-z_$][\w$-]*)\s+([A-Za-z_$][\w$-]*)(.*)$/.exec(line.text);
|
|
20
|
-
if (!constraint)
|
|
103
|
+
if (!constraint) {
|
|
104
|
+
const row = /^([A-Za-z_$][\w$-]*)\b/.exec(line.text);
|
|
105
|
+
children.push(conversionChild(source, block, options, line, {
|
|
106
|
+
kind: 'conversionUnknownRow',
|
|
107
|
+
rowKind: row?.[1],
|
|
108
|
+
normalizedRowKind: 'unknown',
|
|
109
|
+
name: row?.[1] ?? 'unknown',
|
|
110
|
+
id: `conversion_unknown_${safeId(row?.[1] ?? 'row')}_${line.startOffset}`,
|
|
111
|
+
reason: 'unsupported-conversion-row',
|
|
112
|
+
recognized: false
|
|
113
|
+
}));
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
21
116
|
const [, family, name, rest] = constraint;
|
|
22
|
-
children.push(
|
|
117
|
+
children.push(conversionChild(source, block, options, line, {
|
|
23
118
|
kind: 'conversionConstraint',
|
|
24
119
|
name,
|
|
25
120
|
id: idFrom(rest, `conversion_constraint_${family}_${name}`),
|
|
26
121
|
family,
|
|
27
122
|
role: readInlineWord('role', rest) ?? 'source',
|
|
28
|
-
header: line.text,
|
|
29
|
-
startOffset: line.startOffset,
|
|
30
|
-
endOffset: line.endOffset,
|
|
31
|
-
location: sourcePosition(source, line.startOffset),
|
|
32
|
-
parentKind: block.kind,
|
|
33
|
-
parentId: block.id,
|
|
34
|
-
parentName: block.name,
|
|
35
|
-
moduleId: block.moduleId,
|
|
36
|
-
moduleName: block.moduleName,
|
|
37
|
-
sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options),
|
|
38
123
|
recognized: true
|
|
39
124
|
}));
|
|
40
125
|
}
|
|
41
126
|
return children;
|
|
42
127
|
}
|
|
43
128
|
|
|
129
|
+
function conversionChild(source, block, options, line, child) {
|
|
130
|
+
return cleanRecord({
|
|
131
|
+
header: line.text,
|
|
132
|
+
startOffset: line.startOffset,
|
|
133
|
+
endOffset: line.endOffset,
|
|
134
|
+
location: sourcePosition(source, line.startOffset),
|
|
135
|
+
parentKind: block.kind,
|
|
136
|
+
parentId: block.id,
|
|
137
|
+
parentName: block.name,
|
|
138
|
+
moduleId: block.moduleId,
|
|
139
|
+
moduleName: block.moduleName,
|
|
140
|
+
sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options),
|
|
141
|
+
recognized: true,
|
|
142
|
+
...child
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
44
146
|
function readGenericRowSyntaxChildren(source, block, options, config) {
|
|
45
147
|
const children = [];
|
|
46
148
|
const rowPattern = new RegExp('^([A-Za-z_$][\\w$-]*)\\s+' + ROW_NAME_PATTERN + '(.*)$');
|
|
@@ -74,10 +176,14 @@ function readGenericRowSyntaxChildren(source, block, options, config) {
|
|
|
74
176
|
}
|
|
75
177
|
|
|
76
178
|
function readBodyLines(source, block) {
|
|
77
|
-
|
|
179
|
+
return readTextLines(source, block.bodyStartOffset, block.bodyEndOffset);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function readTextLines(source, startOffset, endOffset) {
|
|
183
|
+
const body = source.slice(startOffset, endOffset);
|
|
78
184
|
const lines = body.split('\n');
|
|
79
185
|
const records = [];
|
|
80
|
-
let lineStart =
|
|
186
|
+
let lineStart = startOffset;
|
|
81
187
|
for (const rawLine of lines) {
|
|
82
188
|
const rawEnd = lineStart + rawLine.length;
|
|
83
189
|
const leading = /^\s*/.exec(rawLine)?.[0].length ?? 0;
|
|
@@ -90,6 +196,79 @@ function readBodyLines(source, block) {
|
|
|
90
196
|
return records;
|
|
91
197
|
}
|
|
92
198
|
|
|
199
|
+
function readNestedBodyBlocks(kind, source) {
|
|
200
|
+
const blocks = [];
|
|
201
|
+
const header = new RegExp('\\b' + kind.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '(?:\\s+([^{}\\n]+?))?\\s*\\{', 'g');
|
|
202
|
+
let match;
|
|
203
|
+
while ((match = header.exec(source))) {
|
|
204
|
+
const open = header.lastIndex - 1;
|
|
205
|
+
const close = findMatchingBrace(source, open);
|
|
206
|
+
if (close < 0) continue;
|
|
207
|
+
blocks.push({
|
|
208
|
+
header: (match[1] ?? '').trim(),
|
|
209
|
+
start: match.index,
|
|
210
|
+
bodyStart: open + 1,
|
|
211
|
+
bodyEnd: close,
|
|
212
|
+
end: close + 1
|
|
213
|
+
});
|
|
214
|
+
header.lastIndex = close + 1;
|
|
215
|
+
}
|
|
216
|
+
return blocks;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function findMatchingBrace(source, open) {
|
|
220
|
+
let depth = 0;
|
|
221
|
+
let state = 'code';
|
|
222
|
+
let quote = '';
|
|
223
|
+
for (let index = open; index < source.length; index++) {
|
|
224
|
+
const char = source[index];
|
|
225
|
+
const next = source[index + 1];
|
|
226
|
+
if (state === 'line-comment') {
|
|
227
|
+
if (char === '\n') state = 'code';
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (state === 'block-comment') {
|
|
231
|
+
if (char === '*' && next === '/') {
|
|
232
|
+
index++;
|
|
233
|
+
state = 'code';
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (state === 'string') {
|
|
238
|
+
if (char === '\\') {
|
|
239
|
+
index++;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (char === quote) {
|
|
243
|
+
state = 'code';
|
|
244
|
+
quote = '';
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (char === '/' && next === '/') {
|
|
249
|
+
index++;
|
|
250
|
+
state = 'line-comment';
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (char === '/' && next === '*') {
|
|
254
|
+
index++;
|
|
255
|
+
state = 'block-comment';
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (char === '"' || char === "'") {
|
|
259
|
+
state = 'string';
|
|
260
|
+
quote = char;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (char === '{') depth++;
|
|
264
|
+
if (char === '}') {
|
|
265
|
+
depth--;
|
|
266
|
+
if (depth === 0) return index;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return -1;
|
|
270
|
+
}
|
|
271
|
+
|
|
93
272
|
function sourcePosition(source, offset) {
|
|
94
273
|
const lines = source.slice(0, offset).split('\n');
|
|
95
274
|
return { line: lines.length, column: lines[lines.length - 1].length + 1, offset };
|
|
@@ -23,6 +23,10 @@ export function inspectFrontierSourceSyntax(source, options = {}) {
|
|
|
23
23
|
reason: 'unsupported-top-level-block'
|
|
24
24
|
}));
|
|
25
25
|
const childRecords = blocks.flatMap((block) => block.children ?? []);
|
|
26
|
+
const unknownChildren = childRecords.filter((child) => !child.recognized).map((child) => ({
|
|
27
|
+
...child,
|
|
28
|
+
reason: child.reason ?? 'unsupported-child-syntax'
|
|
29
|
+
}));
|
|
26
30
|
const malformedBlockOpenOffsets = new Set(blocks.filter((block) => block.malformed).map((block) => block.bodyStartOffset - 1));
|
|
27
31
|
const diagnostics = [
|
|
28
32
|
...blocks.flatMap((block) => block.diagnostics ?? []),
|
|
@@ -38,7 +42,7 @@ export function inspectFrontierSourceSyntax(source, options = {}) {
|
|
|
38
42
|
}))
|
|
39
43
|
];
|
|
40
44
|
const malformedBlocks = blocks.filter((block) => block.malformed);
|
|
41
|
-
const failClosed = unknownBlocks.length > 0 || diagnostics.length > 0;
|
|
45
|
+
const failClosed = unknownBlocks.length > 0 || unknownChildren.length > 0 || diagnostics.length > 0;
|
|
42
46
|
return {
|
|
43
47
|
kind: 'frontier.lang.sourceSyntaxReport',
|
|
44
48
|
version: 1,
|
|
@@ -47,10 +51,12 @@ export function inspectFrontierSourceSyntax(source, options = {}) {
|
|
|
47
51
|
blocks,
|
|
48
52
|
recognizedBlocks,
|
|
49
53
|
unknownBlocks,
|
|
54
|
+
unknownChildren,
|
|
50
55
|
summary: {
|
|
51
56
|
blockCount: blocks.length,
|
|
52
57
|
recognizedBlockCount: recognizedBlocks.length,
|
|
53
58
|
unknownBlockCount: unknownBlocks.length,
|
|
59
|
+
unknownChildCount: unknownChildren.length,
|
|
54
60
|
malformedBlockCount: malformedBlocks.length,
|
|
55
61
|
childCount: childRecords.length,
|
|
56
62
|
recognizedChildCount: childRecords.filter((child) => child.recognized).length,
|
|
@@ -58,8 +64,9 @@ export function inspectFrontierSourceSyntax(source, options = {}) {
|
|
|
58
64
|
recognizedKinds: unique(recognizedBlocks.map((block) => block.kind)),
|
|
59
65
|
recognizedChildKinds: unique(childRecords.filter((child) => child.recognized).map((child) => child.kind)),
|
|
60
66
|
unknownKinds: unique(unknownBlocks.map((block) => block.kind)),
|
|
67
|
+
unknownChildKinds: unique(unknownChildren.map((child) => child.rowKind ?? child.kind)),
|
|
61
68
|
failClosed,
|
|
62
|
-
unsupportedSyntax: unknownBlocks.length > 0
|
|
69
|
+
unsupportedSyntax: unknownBlocks.length > 0 || unknownChildren.length > 0
|
|
63
70
|
},
|
|
64
71
|
diagnostics,
|
|
65
72
|
metadata: {
|
|
@@ -84,7 +91,7 @@ export function readFrontierSourceBlocks(source, options = {}) {
|
|
|
84
91
|
export function readFrontierNestedBlocks(kind, source) {
|
|
85
92
|
const structure = scanFrontierStructure(source);
|
|
86
93
|
const blocks = [];
|
|
87
|
-
const header = new RegExp('\\b' + escapeRegExp(kind) + '
|
|
94
|
+
const header = new RegExp('\\b' + escapeRegExp(kind) + '(?:\\s+([^{}\\n]+?))?\\s*\\{', 'g');
|
|
88
95
|
let match;
|
|
89
96
|
while ((match = header.exec(source))) {
|
|
90
97
|
const start = match.index;
|
|
@@ -94,7 +101,7 @@ export function readFrontierNestedBlocks(kind, source) {
|
|
|
94
101
|
if (close < 0) continue;
|
|
95
102
|
blocks.push({
|
|
96
103
|
kind,
|
|
97
|
-
header: match[1].trim(),
|
|
104
|
+
header: (match[1] ?? '').trim(),
|
|
98
105
|
body: source.slice(header.lastIndex, close),
|
|
99
106
|
start,
|
|
100
107
|
end: close + 1
|
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.41",
|
|
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/target-projection-aggregate-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",
|
|
25
|
+
"test": "npm run build && node test/smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
26
26
|
"typecheck": "node ./node_modules/typescript/bin/tsc --noEmit -p test/tsconfig.json",
|
|
27
27
|
"fuzz": "npm run build && node fuzz/smoke.mjs",
|
|
28
28
|
"bench": "npm run build && node bench/smoke.mjs",
|