wasm-mcp 0.1.0
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/CHANGELOG.md +46 -0
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/build/wasm-proposals-main.json +1 -0
- package/build/wasm-sections-js-api-main.json +1 -0
- package/build/wasm-sections-web-api-main.json +1 -0
- package/build/wasm-spec-core-main.json +1 -0
- package/dist/mcp/_args.d.ts +22 -0
- package/dist/mcp/_args.js +25 -0
- package/dist/mcp/instructions.d.ts +1 -0
- package/dist/mcp/instructions.js +67 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +63 -0
- package/dist/mcp/tool_meta.d.ts +32 -0
- package/dist/mcp/tool_meta.js +100 -0
- package/dist/mcp/tools/instruction_get.d.ts +32 -0
- package/dist/mcp/tools/instruction_get.js +39 -0
- package/dist/mcp/tools/instruction_list.d.ts +67 -0
- package/dist/mcp/tools/instruction_list.js +52 -0
- package/dist/mcp/tools/instruction_search.d.ts +28 -0
- package/dist/mcp/tools/instruction_search.js +33 -0
- package/dist/mcp/tools/proposal_list.d.ts +51 -0
- package/dist/mcp/tools/proposal_list.js +44 -0
- package/dist/mcp/tools/section_get.d.ts +29 -0
- package/dist/mcp/tools/section_get.js +32 -0
- package/dist/mcp/tools/section_list.d.ts +49 -0
- package/dist/mcp/tools/section_list.js +56 -0
- package/dist/mcp/tools/spec_search.d.ts +35 -0
- package/dist/mcp/tools/spec_search.js +34 -0
- package/dist/mcp/tools/spec_version.d.ts +28 -0
- package/dist/mcp/tools/spec_version.js +30 -0
- package/dist/mcp/tools/type_get.d.ts +22 -0
- package/dist/mcp/tools/type_get.js +31 -0
- package/dist/parser/bikeshed.d.ts +8 -0
- package/dist/parser/bikeshed.js +106 -0
- package/dist/parser/instructions.d.ts +171 -0
- package/dist/parser/instructions.js +241 -0
- package/dist/parser/proposals.d.ts +30 -0
- package/dist/parser/proposals.js +188 -0
- package/dist/parser/sections.d.ts +27 -0
- package/dist/parser/sections.js +213 -0
- package/dist/parser/types.d.ts +37 -0
- package/dist/parser/types.js +116 -0
- package/dist/parser/upstream.d.ts +7 -0
- package/dist/parser/upstream.js +230 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.js +12 -0
- package/dist/spec/catalog.d.ts +10 -0
- package/dist/spec/catalog.js +20 -0
- package/dist/spec/instructions_query.d.ts +46 -0
- package/dist/spec/instructions_query.js +120 -0
- package/dist/spec/pin.d.ts +13 -0
- package/dist/spec/pin.js +39 -0
- package/dist/spec/proposals_query.d.ts +15 -0
- package/dist/spec/proposals_query.js +23 -0
- package/dist/spec/sections_query.d.ts +43 -0
- package/dist/spec/sections_query.js +89 -0
- package/dist/spec/spec_data.d.ts +46 -0
- package/dist/spec/spec_data.js +92 -0
- package/dist/spec/tool_inventory.d.ts +5 -0
- package/dist/spec/tool_inventory.js +17 -0
- package/dist/versions.d.ts +12 -0
- package/dist/versions.js +22 -0
- package/package.json +76 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// MCP tool: proposal_list — list WebAssembly proposals and their
|
|
2
|
+
// phases from the pinned WebAssembly/proposals repository. Filterable
|
|
3
|
+
// by status / phase / champion / affected spec / free-text.
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { loadProposals } from "../../spec/spec_data.js";
|
|
6
|
+
import { listProposals } from "../../spec/proposals_query.js";
|
|
7
|
+
import { PROPOSAL_STATUSES } from "../../parser/proposals.js";
|
|
8
|
+
export const proposalListSchema = {
|
|
9
|
+
status: z
|
|
10
|
+
.enum(PROPOSAL_STATUSES)
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Filter by lifecycle status: `phase-0`…`phase-5`, `finished` (merged into the spec), or `inactive`."),
|
|
13
|
+
phase: z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.min(0)
|
|
17
|
+
.max(5)
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Filter by numeric phase 0–5 (active + finished proposals carry a phase)."),
|
|
20
|
+
champion: z.string().min(1).optional().describe("Champion substring, case-insensitive."),
|
|
21
|
+
affects: z
|
|
22
|
+
.string()
|
|
23
|
+
.min(1)
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Filter to finished proposals affecting a given spec: `core`, `js-api`, or `web-api`."),
|
|
26
|
+
contains: z.string().min(1).optional().describe("Name or champion substring, case-insensitive."),
|
|
27
|
+
};
|
|
28
|
+
export const proposalListExamples = [
|
|
29
|
+
{
|
|
30
|
+
q: "What proposals are finished and in Wasm 3.0?",
|
|
31
|
+
input: { status: "finished", affects: "core" },
|
|
32
|
+
note: "Finished proposals carry affected_specs + spec_version; filter by the spec they touched.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
q: "What's in phase 3?",
|
|
36
|
+
input: { phase: 3 },
|
|
37
|
+
note: "Phase filter scopes to one stage of the proposal process.",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
export function proposalList(args) {
|
|
41
|
+
const { pin, proposals } = loadProposals();
|
|
42
|
+
const filtered = listProposals(proposals, args);
|
|
43
|
+
return { pin: { sha: pin.sha }, count: filtered.length, proposals: filtered };
|
|
44
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { SpecClause } from "../../parser/sections.js";
|
|
3
|
+
import type { SpecName } from "../../spec/catalog.js";
|
|
4
|
+
import type { VersionValue } from "../../versions.js";
|
|
5
|
+
export declare const sectionGetSchema: {
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
spec: z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
core: "core";
|
|
9
|
+
"js-api": "js-api";
|
|
10
|
+
"web-api": "web-api";
|
|
11
|
+
}>>;
|
|
12
|
+
version: z.ZodDefault<z.ZodEnum<{
|
|
13
|
+
main: "main";
|
|
14
|
+
latest: "latest";
|
|
15
|
+
}>>;
|
|
16
|
+
};
|
|
17
|
+
export type SectionGetArgs = {
|
|
18
|
+
id: string;
|
|
19
|
+
spec?: SpecName;
|
|
20
|
+
version?: VersionValue;
|
|
21
|
+
};
|
|
22
|
+
export declare const sectionGetExamples: {
|
|
23
|
+
q: string;
|
|
24
|
+
input: {
|
|
25
|
+
id: string;
|
|
26
|
+
};
|
|
27
|
+
note: string;
|
|
28
|
+
}[];
|
|
29
|
+
export declare function sectionGet(args: SectionGetArgs): SpecClause | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// MCP tool: section_get — fetch one spec clause by id or anchor
|
|
2
|
+
// (e.g. `syntax-numtype`, `valid-unreachable`, `binary-instr`).
|
|
3
|
+
// Returns the clause's title, cleaned prose, cross-references, the
|
|
4
|
+
// SpecTec formal-rule names it references, and the rendered URL.
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { versionArg, specArg } from "../_args.js";
|
|
7
|
+
import { loadSections } from "../../spec/spec_data.js";
|
|
8
|
+
import { getClause } from "../../spec/sections_query.js";
|
|
9
|
+
export const sectionGetSchema = {
|
|
10
|
+
id: z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1)
|
|
13
|
+
.describe("Clause id or anchor. For `core`: `syntax-numtype`, `valid-unreachable`, `binary-instr`, … For `js-api` / `web-api`: `modules`, `memories`, `streaming-modules`, … These match the stable fragment ids in the rendered spec."),
|
|
14
|
+
spec: specArg,
|
|
15
|
+
version: versionArg,
|
|
16
|
+
};
|
|
17
|
+
export const sectionGetExamples = [
|
|
18
|
+
{
|
|
19
|
+
q: "Get the number types section",
|
|
20
|
+
input: { id: "syntax-numtype" },
|
|
21
|
+
note: "Returns the Number Types clause: prose, the i32/i64/f32/f64 formal refs, and the spec URL.",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
q: "Read the validation rule anchor for unreachable",
|
|
25
|
+
input: { id: "valid-unreachable" },
|
|
26
|
+
note: "Validation/execution clauses are SpecTec-spliced; prose may be terse but `formal_refs` names the rule and `url` links the rendered notation.",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
export function sectionGet(args) {
|
|
30
|
+
const sections = loadSections(args.spec ?? "core", args.version);
|
|
31
|
+
return getClause(sections, args.id);
|
|
32
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type SectionSummary } from "../../spec/sections_query.js";
|
|
3
|
+
import type { SpecName } from "../../spec/catalog.js";
|
|
4
|
+
import type { VersionValue } from "../../versions.js";
|
|
5
|
+
export declare const sectionListSchema: {
|
|
6
|
+
spec: z.ZodDefault<z.ZodEnum<{
|
|
7
|
+
core: "core";
|
|
8
|
+
"js-api": "js-api";
|
|
9
|
+
"web-api": "web-api";
|
|
10
|
+
}>>;
|
|
11
|
+
path: z.ZodOptional<z.ZodString>;
|
|
12
|
+
anchor_prefix: z.ZodOptional<z.ZodString>;
|
|
13
|
+
titled_only: z.ZodDefault<z.ZodBoolean>;
|
|
14
|
+
max_level: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
version: z.ZodDefault<z.ZodEnum<{
|
|
16
|
+
main: "main";
|
|
17
|
+
latest: "latest";
|
|
18
|
+
}>>;
|
|
19
|
+
};
|
|
20
|
+
export type SectionListArgs = {
|
|
21
|
+
spec?: SpecName;
|
|
22
|
+
path?: string;
|
|
23
|
+
anchor_prefix?: string;
|
|
24
|
+
titled_only?: boolean;
|
|
25
|
+
max_level?: number;
|
|
26
|
+
version?: VersionValue;
|
|
27
|
+
};
|
|
28
|
+
export declare const sectionListExamples: ({
|
|
29
|
+
q: string;
|
|
30
|
+
input: {
|
|
31
|
+
path: string;
|
|
32
|
+
titled_only: boolean;
|
|
33
|
+
anchor_prefix?: undefined;
|
|
34
|
+
};
|
|
35
|
+
note: string;
|
|
36
|
+
} | {
|
|
37
|
+
q: string;
|
|
38
|
+
input: {
|
|
39
|
+
anchor_prefix: string;
|
|
40
|
+
path?: undefined;
|
|
41
|
+
titled_only?: undefined;
|
|
42
|
+
};
|
|
43
|
+
note: string;
|
|
44
|
+
})[];
|
|
45
|
+
export interface SectionListResult {
|
|
46
|
+
count: number;
|
|
47
|
+
sections: SectionSummary[];
|
|
48
|
+
}
|
|
49
|
+
export declare function sectionList(args: SectionListArgs): SectionListResult;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// MCP tool: section_list — enumerate spec clauses for navigation,
|
|
2
|
+
// optionally filtered by source path (structure / validation /
|
|
3
|
+
// execution / binary / text live under distinct paths), anchor
|
|
4
|
+
// prefix, heading presence, or depth. Returns lightweight rows;
|
|
5
|
+
// follow up with section_get.
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { versionArg, specArg } from "../_args.js";
|
|
8
|
+
import { loadSections } from "../../spec/spec_data.js";
|
|
9
|
+
import { listSections } from "../../spec/sections_query.js";
|
|
10
|
+
export const sectionListSchema = {
|
|
11
|
+
spec: specArg,
|
|
12
|
+
path: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Filter to a source path / prefix. Top-level areas: `intro`, `syntax` (structure), `valid` (validation), `exec` (execution), `binary`, `text`, `appendix`. Sub-paths like `syntax/types` also work."),
|
|
17
|
+
anchor_prefix: z
|
|
18
|
+
.string()
|
|
19
|
+
.min(1)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Filter to clauses whose id/anchor starts with this prefix, e.g. `syntax-`, `valid-`, `exec-`."),
|
|
22
|
+
titled_only: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.default(false)
|
|
25
|
+
.describe("Drop anchor-only content blocks (keep only clauses with a heading)."),
|
|
26
|
+
max_level: z
|
|
27
|
+
.number()
|
|
28
|
+
.int()
|
|
29
|
+
.min(1)
|
|
30
|
+
.max(6)
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Cap heading depth (1 = page titles only). Anchor-only blocks are unaffected."),
|
|
33
|
+
version: versionArg,
|
|
34
|
+
};
|
|
35
|
+
export const sectionListExamples = [
|
|
36
|
+
{
|
|
37
|
+
q: "Outline the binary format sections",
|
|
38
|
+
input: { path: "binary", titled_only: true },
|
|
39
|
+
note: "Path filter scopes to one area; titled_only yields a clean outline.",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
q: "List every validation clause",
|
|
43
|
+
input: { anchor_prefix: "valid-" },
|
|
44
|
+
note: "Anchor-prefix filter gathers all clauses in one rule family.",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
export function sectionList(args) {
|
|
48
|
+
const sections = loadSections(args.spec ?? "core", args.version);
|
|
49
|
+
const out = listSections(sections, {
|
|
50
|
+
path: args.path,
|
|
51
|
+
anchor_prefix: args.anchor_prefix,
|
|
52
|
+
titled_only: args.titled_only,
|
|
53
|
+
max_level: args.max_level,
|
|
54
|
+
});
|
|
55
|
+
return { count: out.length, sections: out };
|
|
56
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type SpecSearchHit } from "../../spec/sections_query.js";
|
|
3
|
+
import type { SpecName } from "../../spec/catalog.js";
|
|
4
|
+
import type { VersionValue } from "../../versions.js";
|
|
5
|
+
export declare const specSearchSchema: {
|
|
6
|
+
query: z.ZodString;
|
|
7
|
+
spec: z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
core: "core";
|
|
9
|
+
"js-api": "js-api";
|
|
10
|
+
"web-api": "web-api";
|
|
11
|
+
}>>;
|
|
12
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
13
|
+
version: z.ZodDefault<z.ZodEnum<{
|
|
14
|
+
main: "main";
|
|
15
|
+
latest: "latest";
|
|
16
|
+
}>>;
|
|
17
|
+
};
|
|
18
|
+
export type SpecSearchArgs = {
|
|
19
|
+
query: string;
|
|
20
|
+
spec?: SpecName;
|
|
21
|
+
limit?: number;
|
|
22
|
+
version?: VersionValue;
|
|
23
|
+
};
|
|
24
|
+
export declare const specSearchExamples: {
|
|
25
|
+
q: string;
|
|
26
|
+
input: {
|
|
27
|
+
query: string;
|
|
28
|
+
};
|
|
29
|
+
note: string;
|
|
30
|
+
}[];
|
|
31
|
+
export interface SpecSearchResult {
|
|
32
|
+
count: number;
|
|
33
|
+
hits: SpecSearchHit[];
|
|
34
|
+
}
|
|
35
|
+
export declare function specSearch(args: SpecSearchArgs): SpecSearchResult;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// MCP tool: spec_search — full-text-ish search across the spec
|
|
2
|
+
// section index (anchors, titles, and prose). The entry point when
|
|
3
|
+
// you don't know the exact anchor. Returns ranked lightweight hits
|
|
4
|
+
// with a matched_on field and a prose snippet for body matches.
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { versionArg, specArg } from "../_args.js";
|
|
7
|
+
import { loadSections } from "../../spec/spec_data.js";
|
|
8
|
+
import { searchSpec } from "../../spec/sections_query.js";
|
|
9
|
+
export const specSearchSchema = {
|
|
10
|
+
query: z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1)
|
|
13
|
+
.describe("Search text. Matched against clause anchors/ids, titles, and prose. E.g. `block type`, `trap`, `funcref`, `streaming compilation`."),
|
|
14
|
+
spec: specArg,
|
|
15
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Max ranked hits returned."),
|
|
16
|
+
version: versionArg,
|
|
17
|
+
};
|
|
18
|
+
export const specSearchExamples = [
|
|
19
|
+
{
|
|
20
|
+
q: "Where does the spec define traps?",
|
|
21
|
+
input: { query: "trap" },
|
|
22
|
+
note: "Ranks title matches above prose matches; prose hits include a snippet around the match.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
q: "Find the block type section",
|
|
26
|
+
input: { query: "block type" },
|
|
27
|
+
note: "Title substring search surfaces syntax-blocktype near the top.",
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
export function specSearch(args) {
|
|
31
|
+
const sections = loadSections(args.spec ?? "core", args.version);
|
|
32
|
+
const hits = searchSpec(sections, args.query, args.limit ?? 20);
|
|
33
|
+
return { count: hits.length, hits };
|
|
34
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const specVersionSchema: {};
|
|
3
|
+
export interface SpecVersionPin {
|
|
4
|
+
/** Pin key, e.g. `spec/main` or `proposals/main`. */
|
|
5
|
+
key: string;
|
|
6
|
+
/** Full upstream commit SHA pinned for this snapshot. */
|
|
7
|
+
sha: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SpecVersionResult {
|
|
10
|
+
/** Package name as published to npm. */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Package version read from package.json at runtime. */
|
|
13
|
+
version: string;
|
|
14
|
+
/** Pins baked into the package (core spec + proposals). */
|
|
15
|
+
pins: SpecVersionPin[];
|
|
16
|
+
}
|
|
17
|
+
export declare const SpecVersionResultSchema: z.ZodObject<{
|
|
18
|
+
name: z.ZodString;
|
|
19
|
+
version: z.ZodString;
|
|
20
|
+
pins: z.ZodArray<z.ZodObject<{
|
|
21
|
+
key: z.ZodString;
|
|
22
|
+
sha: z.ZodString;
|
|
23
|
+
}, z.core.$strip>>;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
export declare function specVersion(packageInfo: {
|
|
26
|
+
name: string;
|
|
27
|
+
version: string;
|
|
28
|
+
}): SpecVersionResult;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// `spec_version` — return the pinned upstream commits + package
|
|
2
|
+
// version. Reads the pin from the baked artifacts (which ship in the
|
|
3
|
+
// package), NOT from vendor/PINNED.txt (which is a build-time-only
|
|
4
|
+
// input and is absent from the published package). The simplest
|
|
5
|
+
// read-only tool; serves as a freshness probe and a smoke test that
|
|
6
|
+
// the data pipeline ran.
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { loadSnapshot, loadProposals } from "../../spec/spec_data.js";
|
|
9
|
+
export const specVersionSchema = {};
|
|
10
|
+
const PinSchema = z.object({ key: z.string(), sha: z.string() });
|
|
11
|
+
export const SpecVersionResultSchema = z.object({
|
|
12
|
+
name: z.string(),
|
|
13
|
+
version: z.string(),
|
|
14
|
+
pins: z.array(PinSchema),
|
|
15
|
+
});
|
|
16
|
+
export function specVersion(packageInfo) {
|
|
17
|
+
const pins = [];
|
|
18
|
+
const spec = loadSnapshot();
|
|
19
|
+
pins.push({ key: spec.pin.key, sha: spec.pin.sha });
|
|
20
|
+
// Proposals are a separate, optional artifact (separate upstream
|
|
21
|
+
// repo + pin). Tolerate its absence so the core tool still works.
|
|
22
|
+
try {
|
|
23
|
+
const proposals = loadProposals();
|
|
24
|
+
pins.push({ key: proposals.pin.key, sha: proposals.pin.sha });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// proposals index not built — omit it from the pin list.
|
|
28
|
+
}
|
|
29
|
+
return { name: packageInfo.name, version: packageInfo.version, pins };
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type TypeEntry } from "../../parser/types.js";
|
|
3
|
+
import type { VersionValue } from "../../versions.js";
|
|
4
|
+
export declare const typeGetSchema: {
|
|
5
|
+
name: z.ZodString;
|
|
6
|
+
version: z.ZodDefault<z.ZodEnum<{
|
|
7
|
+
main: "main";
|
|
8
|
+
latest: "latest";
|
|
9
|
+
}>>;
|
|
10
|
+
};
|
|
11
|
+
export type TypeGetArgs = {
|
|
12
|
+
name: string;
|
|
13
|
+
version?: VersionValue;
|
|
14
|
+
};
|
|
15
|
+
export declare const typeGetExamples: {
|
|
16
|
+
q: string;
|
|
17
|
+
input: {
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
note: string;
|
|
21
|
+
}[];
|
|
22
|
+
export declare function typeGet(args: TypeGetArgs): TypeEntry | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// MCP tool: type_get — look up a WebAssembly type or type form by
|
|
2
|
+
// name (`i32`, `funcref`, `v128`, `functype`, `limits`, …). Returns
|
|
3
|
+
// its classification, sibling members for category types, defining
|
|
4
|
+
// clause prose, formal-rule references, and the rendered spec URL.
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { versionArg } from "../_args.js";
|
|
7
|
+
import { loadTypes } from "../../spec/spec_data.js";
|
|
8
|
+
import { getType } from "../../parser/types.js";
|
|
9
|
+
export const typeGetSchema = {
|
|
10
|
+
name: z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1)
|
|
13
|
+
.describe("Type or type-form name. Concrete value types: `i32`, `i64`, `f32`, `f64`, `v128`, `funcref`, `externref`, … Type forms: `functype`, `limits`, `memtype`, `tabletype`, `globaltype`, `reftype`, `valtype`, `rectype`, `heaptype`, … Case-insensitive, exact match."),
|
|
14
|
+
version: versionArg,
|
|
15
|
+
};
|
|
16
|
+
export const typeGetExamples = [
|
|
17
|
+
{
|
|
18
|
+
q: "What is the i32 type?",
|
|
19
|
+
input: { name: "i32" },
|
|
20
|
+
note: "Returns kind=number, the sibling number types (i64/f32/f64), and the Number Types clause prose + URL.",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
q: "Describe a function type",
|
|
24
|
+
input: { name: "functype" },
|
|
25
|
+
note: "Type forms like functype/limits/memtype resolve to their defining clause in the types section.",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
export function typeGet(args) {
|
|
29
|
+
const catalog = loadTypes(args.version);
|
|
30
|
+
return getType(catalog, args.name);
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SpecClause } from "./sections.js";
|
|
2
|
+
/** Map a Bikeshed spec path + anchor to the rendered spec URL. */
|
|
3
|
+
export declare function bikeshedUrl(path: string, anchor: string | null): string;
|
|
4
|
+
/**
|
|
5
|
+
* Parse a Bikeshed document into clauses. `path` is the spec slug
|
|
6
|
+
* (`js-api` or `web-api`).
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseBikeshed(source: string, path: string): SpecClause[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Parse the Bikeshed (.bs) sources for the JS-API and Web-API specs
|
|
2
|
+
// into the same anchor-addressable `SpecClause` shape the RST parser
|
|
3
|
+
// produces, so the section query functions work unchanged across all
|
|
4
|
+
// three specs.
|
|
5
|
+
//
|
|
6
|
+
// Unlike the core spec (reStructuredText + SpecTec), the js-api and
|
|
7
|
+
// web-api specs are authored in Bikeshed and use plain HTML headings
|
|
8
|
+
// with stable ids — `<h2 id="modules">Modules</h2>` — which map
|
|
9
|
+
// directly to the fragment ids in the rendered spec. Bodies mix HTML,
|
|
10
|
+
// Web IDL blocks, and Bikeshed autolink shorthands; we strip those to
|
|
11
|
+
// readable prose and record the cross-reference targets.
|
|
12
|
+
const SPEC_BASE = "https://webassembly.github.io/spec";
|
|
13
|
+
/** Map a Bikeshed spec path + anchor to the rendered spec URL. */
|
|
14
|
+
export function bikeshedUrl(path, anchor) {
|
|
15
|
+
const page = `${SPEC_BASE}/${path}/`;
|
|
16
|
+
return anchor ? `${page}#${anchor}` : page;
|
|
17
|
+
}
|
|
18
|
+
/** Strip inline HTML tags and decode the few entities we encounter. */
|
|
19
|
+
function stripTags(s) {
|
|
20
|
+
return s
|
|
21
|
+
.replace(/<[^>]+>/g, "")
|
|
22
|
+
.replace(/</g, "<")
|
|
23
|
+
.replace(/>/g, ">")
|
|
24
|
+
.replace(/&/g, "&")
|
|
25
|
+
.replace(/"/g, '"')
|
|
26
|
+
.trim();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Clean a Bikeshed body block into readable prose, collecting
|
|
30
|
+
* cross-reference targets as a side effect. Removes fenced/`<pre>`
|
|
31
|
+
* code + IDL blocks, resolves Bikeshed autolink shorthands, and
|
|
32
|
+
* strips remaining HTML.
|
|
33
|
+
*/
|
|
34
|
+
function cleanBody(raw, crossrefs) {
|
|
35
|
+
let text = raw;
|
|
36
|
+
// Drop fenced code blocks (```...```), <pre>/<xmp> blocks (IDL,
|
|
37
|
+
// algorithms, examples) — they aren't prose.
|
|
38
|
+
text = text.replace(/```[\s\S]*?```/g, " ");
|
|
39
|
+
text = text.replace(/<(pre|xmp)\b[^>]*>[\s\S]*?<\/\1>/gi, " ");
|
|
40
|
+
// Bikeshed autolinks:
|
|
41
|
+
// [=term=] definition autolink
|
|
42
|
+
// [=term|display=] definition with custom text
|
|
43
|
+
// {{IdlThing}} IDL autolink
|
|
44
|
+
// [[#anchor]] local section ref
|
|
45
|
+
// [[BIBLIO]] bibliography ref
|
|
46
|
+
text = text.replace(/\[=([^=\]|]+)(?:\|([^=\]]+))?=\]/g, (_m, term, disp) => {
|
|
47
|
+
crossrefs.add(term.trim());
|
|
48
|
+
return (disp ?? term).trim();
|
|
49
|
+
});
|
|
50
|
+
text = text.replace(/\{\{([^}]+)\}\}/g, (_m, idl) => {
|
|
51
|
+
const name = idl.split("/")[0].trim();
|
|
52
|
+
crossrefs.add(name);
|
|
53
|
+
return name;
|
|
54
|
+
});
|
|
55
|
+
text = text.replace(/\[\[#([^\]]+)\]\]/g, (_m, anchor) => {
|
|
56
|
+
crossrefs.add(anchor.trim());
|
|
57
|
+
return anchor.trim();
|
|
58
|
+
});
|
|
59
|
+
text = text.replace(/\[\[!?([A-Z0-9-]+)\]\]/g, "$1");
|
|
60
|
+
// Bikeshed algorithm-variable markers: |source| → source.
|
|
61
|
+
text = text.replace(/\|([A-Za-z][A-Za-z0-9 _]*)\|/g, "$1");
|
|
62
|
+
// Markdown emphasis / inline code → plain text.
|
|
63
|
+
text = text.replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1");
|
|
64
|
+
text = text.replace(/`([^`]+)`/g, "$1");
|
|
65
|
+
// Remaining HTML tags + entities.
|
|
66
|
+
text = stripTags(text);
|
|
67
|
+
return text.replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
68
|
+
}
|
|
69
|
+
const HEADING_RE = /<h([1-6])\s+id="([^"]+)"[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
70
|
+
/**
|
|
71
|
+
* Parse a Bikeshed document into clauses. `path` is the spec slug
|
|
72
|
+
* (`js-api` or `web-api`).
|
|
73
|
+
*/
|
|
74
|
+
export function parseBikeshed(source, path) {
|
|
75
|
+
// Locate every heading with its byte offset so we can slice the
|
|
76
|
+
// body that follows each one (up to the next heading).
|
|
77
|
+
const heads = [];
|
|
78
|
+
for (const m of source.matchAll(HEADING_RE)) {
|
|
79
|
+
heads.push({
|
|
80
|
+
level: Number(m[1]),
|
|
81
|
+
anchor: m[2],
|
|
82
|
+
title: stripTags(m[3]),
|
|
83
|
+
start: m.index,
|
|
84
|
+
end: m.index + m[0].length,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const clauses = [];
|
|
88
|
+
for (let i = 0; i < heads.length; i++) {
|
|
89
|
+
const h = heads[i];
|
|
90
|
+
const bodyEnd = i + 1 < heads.length ? heads[i + 1].start : source.length;
|
|
91
|
+
const crossrefs = new Set();
|
|
92
|
+
const prose = cleanBody(source.slice(h.end, bodyEnd), crossrefs);
|
|
93
|
+
clauses.push({
|
|
94
|
+
id: h.anchor,
|
|
95
|
+
anchors: [h.anchor],
|
|
96
|
+
title: h.title || null,
|
|
97
|
+
level: h.level,
|
|
98
|
+
path,
|
|
99
|
+
prose,
|
|
100
|
+
crossrefs: [...crossrefs],
|
|
101
|
+
formal_refs: [],
|
|
102
|
+
url: bikeshedUrl(path, h.anchor),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return clauses;
|
|
106
|
+
}
|