@nifrajs/cli 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +99 -0
- package/dist/check.d.ts +100 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +690 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +208 -0
- package/dist/cli.js.map +1 -0
- package/dist/docs-search.d.ts +30 -0
- package/dist/docs-search.d.ts.map +1 -0
- package/dist/docs-search.js +94 -0
- package/dist/docs-search.js.map +1 -0
- package/dist/doctor.d.ts +52 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +291 -0
- package/dist/doctor.js.map +1 -0
- package/dist/examples.d.ts +30 -0
- package/dist/examples.d.ts.map +1 -0
- package/dist/examples.js +70 -0
- package/dist/examples.js.map +1 -0
- package/dist/introspect.d.ts +64 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +243 -0
- package/dist/introspect.js.map +1 -0
- package/dist/load.d.ts +51 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +70 -0
- package/dist/load.js.map +1 -0
- package/dist/mcp-docs-tools.d.ts +13 -0
- package/dist/mcp-docs-tools.d.ts.map +1 -0
- package/dist/mcp-docs-tools.js +63 -0
- package/dist/mcp-docs-tools.js.map +1 -0
- package/dist/mcp-http.d.ts +43 -0
- package/dist/mcp-http.d.ts.map +1 -0
- package/dist/mcp-http.js +136 -0
- package/dist/mcp-http.js.map +1 -0
- package/dist/mcp-protocol.d.ts +97 -0
- package/dist/mcp-protocol.d.ts.map +1 -0
- package/dist/mcp-protocol.js +248 -0
- package/dist/mcp-protocol.js.map +1 -0
- package/dist/mcp-render.d.ts +17 -0
- package/dist/mcp-render.d.ts.map +1 -0
- package/dist/mcp-render.js +73 -0
- package/dist/mcp-render.js.map +1 -0
- package/dist/mcp-run.d.ts +24 -0
- package/dist/mcp-run.d.ts.map +1 -0
- package/dist/mcp-run.js +142 -0
- package/dist/mcp-run.js.map +1 -0
- package/dist/mcp-ws.d.ts +26 -0
- package/dist/mcp-ws.d.ts.map +1 -0
- package/dist/mcp-ws.js +258 -0
- package/dist/mcp-ws.js.map +1 -0
- package/dist/mcp.d.ts +42 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +712 -0
- package/dist/mcp.js.map +1 -0
- package/dist/openapi-tool.d.ts +6 -0
- package/dist/openapi-tool.d.ts.map +1 -0
- package/dist/openapi-tool.js +48 -0
- package/dist/openapi-tool.js.map +1 -0
- package/dist/scaffold.d.ts +35 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +140 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/search-terms.d.ts +17 -0
- package/dist/search-terms.d.ts.map +1 -0
- package/dist/search-terms.js +82 -0
- package/dist/search-terms.js.map +1 -0
- package/dist/test-tool.d.ts +29 -0
- package/dist/test-tool.d.ts.map +1 -0
- package/dist/test-tool.js +150 -0
- package/dist/test-tool.js.map +1 -0
- package/docs/examples.json +314 -0
- package/docs/llms-full.txt +4325 -0
- package/package.json +58 -0
- package/src/check.ts +790 -0
- package/src/cli.ts +237 -0
- package/src/docs-search.ts +111 -0
- package/src/doctor.ts +349 -0
- package/src/examples.ts +89 -0
- package/src/introspect.ts +284 -0
- package/src/load.ts +129 -0
- package/src/mcp-docs-tools.ts +72 -0
- package/src/mcp-http.ts +155 -0
- package/src/mcp-protocol.ts +326 -0
- package/src/mcp-render.ts +78 -0
- package/src/mcp-run.ts +153 -0
- package/src/mcp-ws.ts +328 -0
- package/src/mcp.ts +863 -0
- package/src/openapi-tool.ts +51 -0
- package/src/scaffold.ts +164 -0
- package/src/search-terms.ts +89 -0
- package/src/test-tool.ts +181 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Introspect a loaded nifra project into a Markdown brief — the API routes (`backend.routes()`), the
|
|
3
|
+
* page routes (`discoverRoutes`), and a conventions summary — for piping into an AI coding agent's
|
|
4
|
+
* prompt. Used by `nifra context` (stdout) and the `nifra mcp` server (a tool result), so an agent sees
|
|
5
|
+
* *this* project's actual surface, not just generic docs.
|
|
6
|
+
*/
|
|
7
|
+
import { discoverRoutes } from "@nifrajs/web/fs";
|
|
8
|
+
/** Read the backend's registered routes, if it's a `server()` with a `.routes()` method. */
|
|
9
|
+
function backendRoutes(backend) {
|
|
10
|
+
if (backend && typeof backend.routes === "function") {
|
|
11
|
+
try {
|
|
12
|
+
return backend.routes();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
/** The JSON Schema a `t`/Standard Schema carries (nifra's `t` is TypeBox-backed and exposes `.jsonSchema`),
|
|
21
|
+
* or `undefined` for a validator that doesn't expose one. This is the exact field-level contract. */
|
|
22
|
+
function jsonSchemaOf(schema) {
|
|
23
|
+
if (schema && typeof schema === "object" && "jsonSchema" in schema) {
|
|
24
|
+
const js = schema.jsonSchema;
|
|
25
|
+
if (js !== undefined && js !== null)
|
|
26
|
+
return js;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Render a JSON Schema as a compact TypeScript-like type (`{ id: string, name?: string }`) — the
|
|
32
|
+
* shape agents already think in, at a fraction of the raw JSON Schema's tokens (~70-80% smaller).
|
|
33
|
+
* Anything the renderer doesn't model falls back to the raw JSON for that subtree, so the output
|
|
34
|
+
* is always faithful, just not always minimal.
|
|
35
|
+
*/
|
|
36
|
+
export function tsTypeOf(schema, depth = 0) {
|
|
37
|
+
if (typeof schema !== "object" || schema === null || depth > 6)
|
|
38
|
+
return JSON.stringify(schema);
|
|
39
|
+
const node = schema;
|
|
40
|
+
const union = (node.anyOf ?? node.oneOf);
|
|
41
|
+
if (Array.isArray(union))
|
|
42
|
+
return union.map((u) => tsTypeOf(u, depth + 1)).join(" | ");
|
|
43
|
+
if (Array.isArray(node.enum))
|
|
44
|
+
return node.enum.map((v) => JSON.stringify(v)).join(" | ");
|
|
45
|
+
if (node.const !== undefined)
|
|
46
|
+
return JSON.stringify(node.const);
|
|
47
|
+
switch (node.type) {
|
|
48
|
+
case "string":
|
|
49
|
+
return "string";
|
|
50
|
+
case "number":
|
|
51
|
+
case "integer":
|
|
52
|
+
return "number";
|
|
53
|
+
case "boolean":
|
|
54
|
+
return "boolean";
|
|
55
|
+
case "null":
|
|
56
|
+
return "null";
|
|
57
|
+
case "array": {
|
|
58
|
+
const item = tsTypeOf(node.items, depth + 1);
|
|
59
|
+
// Parenthesize unions so `(A | B)[]` reads as the array it is.
|
|
60
|
+
return item.includes(" | ") ? `(${item})[]` : `${item}[]`;
|
|
61
|
+
}
|
|
62
|
+
case "object": {
|
|
63
|
+
const props = node.properties;
|
|
64
|
+
if (props === undefined) {
|
|
65
|
+
return node.additionalProperties === undefined || node.additionalProperties === false
|
|
66
|
+
? "{}"
|
|
67
|
+
: `Record<string, ${node.additionalProperties === true ? "unknown" : tsTypeOf(node.additionalProperties, depth + 1)}>`;
|
|
68
|
+
}
|
|
69
|
+
const required = new Set(Array.isArray(node.required) ? node.required : []);
|
|
70
|
+
const fields = Object.entries(props).map(([key, value]) => `${key}${required.has(key) ? "" : "?"}: ${tsTypeOf(value, depth + 1)}`);
|
|
71
|
+
return `{ ${fields.join(", ")} }`;
|
|
72
|
+
}
|
|
73
|
+
default:
|
|
74
|
+
return JSON.stringify(schema);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** One route's request/response field shapes, indented under its line. Reads the declared schemas off the
|
|
78
|
+
* descriptor so an agent gets the exact input + output contract — not just "this route is validated". */
|
|
79
|
+
function schemaLines(schema) {
|
|
80
|
+
const s = schema;
|
|
81
|
+
if (!s)
|
|
82
|
+
return [];
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const key of ["query", "body", "response"]) {
|
|
85
|
+
const declared = s[key];
|
|
86
|
+
if (!declared)
|
|
87
|
+
continue;
|
|
88
|
+
const js = jsonSchemaOf(declared);
|
|
89
|
+
out.push(js
|
|
90
|
+
? ` - ${key}: \`${tsTypeOf(js)}\``
|
|
91
|
+
: ` - ${key}: _(validated; shape not introspectable from this validator)_`);
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
const IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
96
|
+
/**
|
|
97
|
+
* The typed-client call form for a route — the exact `client<typeof app>` proxy chain an agent should
|
|
98
|
+
* write, derived from the same convention `@nifrajs/client` implements (so it never has to read the
|
|
99
|
+
* client tests to learn it): a static segment is a property (`.users`), a path param/wildcard is a call
|
|
100
|
+
* that appends the value (`({ id })`), the root path is `.index`, and the HTTP verb is the terminal call.
|
|
101
|
+
* Body verbs (POST/PUT/PATCH) take the body first then call-options; other verbs take call-options first —
|
|
102
|
+
* so the `{ query }` argument lands in the right slot for each.
|
|
103
|
+
*/
|
|
104
|
+
export function clientCall(method, path, schema) {
|
|
105
|
+
const s = schema;
|
|
106
|
+
const verb = method.toLowerCase();
|
|
107
|
+
const segs = path.split("/").filter((seg) => seg !== "");
|
|
108
|
+
let chain = "api";
|
|
109
|
+
if (segs.length === 0)
|
|
110
|
+
chain += ".index";
|
|
111
|
+
else
|
|
112
|
+
for (const seg of segs) {
|
|
113
|
+
if (seg.startsWith(":") || seg.startsWith("*")) {
|
|
114
|
+
const name = seg.replace(/^[:*]/, "") || "value";
|
|
115
|
+
chain += `({ ${name} })`;
|
|
116
|
+
}
|
|
117
|
+
else
|
|
118
|
+
chain += IDENT.test(seg) ? `.${seg}` : `[${JSON.stringify(seg)}]`;
|
|
119
|
+
}
|
|
120
|
+
const isBodyVerb = verb === "post" || verb === "put" || verb === "patch";
|
|
121
|
+
let call;
|
|
122
|
+
if (isBodyVerb) {
|
|
123
|
+
// body is the 1st arg; if a route has a query but no body, the body slot must still be filled.
|
|
124
|
+
const bodyArg = s?.body ? "body" : s?.query ? "undefined" : "";
|
|
125
|
+
const opts = s?.query ? (bodyArg ? ", { query }" : "{ query }") : "";
|
|
126
|
+
call = `.${verb}(${bodyArg}${opts})`;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
call = `.${verb}(${s?.query ? "{ query }" : ""})`;
|
|
130
|
+
}
|
|
131
|
+
return `await ${chain}${call}`;
|
|
132
|
+
}
|
|
133
|
+
/** Markdown section listing the backend's API routes with their request + response field shapes. */
|
|
134
|
+
export function apiRoutesSection(routes) {
|
|
135
|
+
if (routes.length === 0) {
|
|
136
|
+
return "## API routes\n\nNo `backend.ts` server routes found (this app may be frontend-only).";
|
|
137
|
+
}
|
|
138
|
+
const lines = [...routes]
|
|
139
|
+
.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method))
|
|
140
|
+
.flatMap((r) => [
|
|
141
|
+
`- \`${r.method} ${r.path}\``,
|
|
142
|
+
...schemaLines(r.schema),
|
|
143
|
+
` - call: \`${clientCall(r.method, r.path, r.schema)}\` → \`{ ok, status, data, error }\``,
|
|
144
|
+
]);
|
|
145
|
+
return `## API routes (backend.ts)\n\nEach route's \`body\`/\`query\`/\`response\` shape is its contract — the typed client derives request inputs and \`res.data\` from these, so a screen built on \`client<typeof app>\` stays in sync automatically. The \`call\` line is the exact \`client<typeof app>\` form: static path segments are properties, a path param is a call (\`({ id })\`), the verb is the terminal call (body first for POST/PUT/PATCH), and every call returns the never-throwing \`{ ok, status, data, error }\` Result.\n\n${lines.join("\n")}`;
|
|
146
|
+
}
|
|
147
|
+
/** Markdown section listing the file-routed pages (URL pattern → source file). */
|
|
148
|
+
export function pageRoutesSection(manifest) {
|
|
149
|
+
if (!manifest || manifest.routes.length === 0) {
|
|
150
|
+
return "## Page routes\n\nNo file routes found under `routes/`.";
|
|
151
|
+
}
|
|
152
|
+
const lines = [...manifest.routes]
|
|
153
|
+
.sort((a, b) => a.pattern.localeCompare(b.pattern))
|
|
154
|
+
.map((r) => `- \`${r.pattern}\` → \`${r.file}\``);
|
|
155
|
+
return `## Page routes (routes/)\n\n${lines.join("\n")}`;
|
|
156
|
+
}
|
|
157
|
+
const CONVENTIONS = `## Conventions (summary)
|
|
158
|
+
|
|
159
|
+
- **Backend:** \`server()\` from \`@nifrajs/core\`; returning a value sends JSON. Validate untrusted input with
|
|
160
|
+
a route schema (\`{ body, query, params }\` using \`t\` from \`@nifrajs/schema\` or any Standard Schema) and read
|
|
161
|
+
the typed \`c.body\` / \`c.query\` / \`c.params\` — never hand-parse.
|
|
162
|
+
- **Response contract (no drift):** declare \`{ response: t.object({...}) }\` on a route to lock its output
|
|
163
|
+
shape — the handler is type-checked against it and the client sees exactly that shape. The contract above
|
|
164
|
+
is the single source of truth for both sides.
|
|
165
|
+
- **Client — ALWAYS use this for API calls:** \`client<typeof app>(url)\` derives request inputs AND
|
|
166
|
+
\`res.data\` from the backend's route types, so the compiler catches any frontend/backend drift. Never
|
|
167
|
+
hand-roll \`fetch\` + ad-hoc response types for an internal API — that's exactly how screens drift. It
|
|
168
|
+
never throws: branch on \`res.ok ? res.data : res.error\`.
|
|
169
|
+
- **Pages:** file-routed under \`routes/\`; \`loader\`/\`action\` are server-only but the module is also bundled
|
|
170
|
+
for the browser — **never top-level-import server-only code** (DB, secrets, \`process.env\`) into a route
|
|
171
|
+
file; reach it via \`ctx.api\` / \`ctx.env\`.
|
|
172
|
+
- \`app.fetch(Request)\` is the universal entry. Full reference: this repo's \`AGENTS.md\`, or \`llms-full.txt\`.`;
|
|
173
|
+
/** Build the project brief (API routes + page routes + conventions) as Markdown — optionally
|
|
174
|
+
* narrowed by {@link ContextFilter} for cheap follow-up calls. */
|
|
175
|
+
export function describeProject(app, filter) {
|
|
176
|
+
let manifest;
|
|
177
|
+
try {
|
|
178
|
+
manifest = discoverRoutes(app.routesDir);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// No (or unreadable) routes/ — the page-routes section reports that.
|
|
182
|
+
}
|
|
183
|
+
const prefix = filter?.path;
|
|
184
|
+
let routes = backendRoutes(app.backend);
|
|
185
|
+
let pages = manifest;
|
|
186
|
+
if (prefix !== undefined && prefix !== "") {
|
|
187
|
+
routes = routes.filter((r) => r.path.startsWith(prefix));
|
|
188
|
+
if (manifest !== undefined) {
|
|
189
|
+
pages = { ...manifest, routes: manifest.routes.filter((r) => r.pattern.startsWith(prefix)) };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const narrowed = filter?.kind !== undefined || (prefix !== undefined && prefix !== "");
|
|
193
|
+
const sections = [`# nifra project: ${app.cwd.slice(app.cwd.lastIndexOf("/") + 1)}`];
|
|
194
|
+
if (!narrowed) {
|
|
195
|
+
sections.push("A full-stack nifra app — Web-standard, one app on Bun, Node, Deno, and the edge.",
|
|
196
|
+
// Point agents at the verified surface, not recalled APIs — nifra is young, so training data is the
|
|
197
|
+
// wrong source. The MCP tools (nifra_docs / nifra_example) are checked against the installed version.
|
|
198
|
+
"> **Build against THIS surface + the verified tools — do not write nifra APIs from memory** (the\n> framework is young; recalled APIs drift). For framework code, call `nifra_example` (snippets\n> typechecked against the installed version) or `nifra_docs`; verify edits with `nifra_run` + `nifra_check`.");
|
|
199
|
+
}
|
|
200
|
+
if (filter?.kind !== "pages")
|
|
201
|
+
sections.push(apiRoutesSection(routes));
|
|
202
|
+
if (filter?.kind !== "api")
|
|
203
|
+
sections.push(pageRoutesSection(pages));
|
|
204
|
+
if (!narrowed)
|
|
205
|
+
sections.push(CONVENTIONS);
|
|
206
|
+
return sections.join("\n\n");
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The backend's API routes as structured JSON — what the `nifra_routes` MCP tool returns, so an agent
|
|
210
|
+
* consumes the contract programmatically (`list_routes` / `get_route_schema`) instead of parsing the
|
|
211
|
+
* Markdown brief. Optionally filtered to routes whose path starts with `pathPrefix`. Reuses the exact
|
|
212
|
+
* same `call` form and TS-shaped contracts the Markdown brief shows.
|
|
213
|
+
*/
|
|
214
|
+
export function routesToJson(app, pathPrefix) {
|
|
215
|
+
const shape = (v) => {
|
|
216
|
+
if (!v)
|
|
217
|
+
return undefined;
|
|
218
|
+
const js = jsonSchemaOf(v);
|
|
219
|
+
return js ? tsTypeOf(js) : "(validated; shape not introspectable from this validator)";
|
|
220
|
+
};
|
|
221
|
+
let routes = backendRoutes(app.backend);
|
|
222
|
+
if (pathPrefix !== undefined && pathPrefix !== "") {
|
|
223
|
+
routes = routes.filter((r) => r.path.startsWith(pathPrefix));
|
|
224
|
+
}
|
|
225
|
+
return [...routes]
|
|
226
|
+
.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method))
|
|
227
|
+
.map((r) => {
|
|
228
|
+
const s = r.schema;
|
|
229
|
+
const body = shape(s?.body);
|
|
230
|
+
const query = shape(s?.query);
|
|
231
|
+
const response = shape(s?.response);
|
|
232
|
+
// Spread only the shapes that exist, so an unschematized route has no body/query/response key.
|
|
233
|
+
return {
|
|
234
|
+
method: r.method,
|
|
235
|
+
path: r.path,
|
|
236
|
+
call: clientCall(r.method, r.path, r.schema),
|
|
237
|
+
...(body !== undefined && { body }),
|
|
238
|
+
...(query !== undefined && { query }),
|
|
239
|
+
...(response !== undefined && { response }),
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=introspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"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"}
|
package/dist/load.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A plugin list — either an array, or a **thunk** that returns one (optionally async). Both forms are
|
|
3
|
+
* accepted; {@link resolvePlugins} normalizes them. The thunk is just a convenience for deferring a
|
|
4
|
+
* plugin's construction — it does NOT make a plugin edge-safe (Bun.build follows a dynamic `import()`
|
|
5
|
+
* even from an unused export, so a Vite/compiler import in an edge-bundled file fails regardless). Edge
|
|
6
|
+
* safety comes from WHERE the config lives: keep these fields in `nifra.config.ts` (CLI-only), not in
|
|
7
|
+
* the edge-imported `framework.ts`. See the module header.
|
|
8
|
+
*/
|
|
9
|
+
export type PluginsField = readonly unknown[] | (() => readonly unknown[] | Promise<readonly unknown[]>);
|
|
10
|
+
/**
|
|
11
|
+
* The CLI's framework wiring — exported from `nifra.config.ts` (or `framework.ts` as a fallback).
|
|
12
|
+
* `create-nifra` generates it. Only `adapter` + `clientModule` are required; the plugin/condition
|
|
13
|
+
* fields are framework-specific extras (Vue/Svelte/Solid need them; React/Preact don't).
|
|
14
|
+
*/
|
|
15
|
+
export interface NifraFramework {
|
|
16
|
+
/** The render adapter, e.g. `reactAdapter` from `@nifrajs/web-react`. */
|
|
17
|
+
readonly adapter: unknown;
|
|
18
|
+
/** The client runtime module providing `mountRouter`, e.g. `"@nifrajs/web-react/client"`. */
|
|
19
|
+
readonly clientModule: string;
|
|
20
|
+
/** Vite plugins for `nifra dev`'s HMR, e.g. `[react()]` / `() => import("@vitejs/plugin-vue")…`. */
|
|
21
|
+
readonly vitePlugins?: PluginsField;
|
|
22
|
+
/** Bun build plugins that compile routes for the CLIENT bundle (`nifra build`), e.g.
|
|
23
|
+
* `[vueBunPlugin("dom")]`. React/Preact JSX is Bun-native → none. */
|
|
24
|
+
readonly clientPlugins?: PluginsField;
|
|
25
|
+
/** Bun runtime plugins that compile routes for SSR — `nifra dev` (Bun-native route import) and
|
|
26
|
+
* `nifra start` register them via `Bun.plugin`, e.g. `[vueBunPlugin("ssr")]`. React/Preact → none. */
|
|
27
|
+
readonly serverPlugins?: PluginsField;
|
|
28
|
+
/** Extra Bun.build / Vite resolve conditions, e.g. Solid's `["solid"]`. */
|
|
29
|
+
readonly conditions?: readonly string[];
|
|
30
|
+
/** Compile-time `define` replacements, e.g. Vue's `__VUE_*` flags. */
|
|
31
|
+
readonly define?: Readonly<Record<string, string>>;
|
|
32
|
+
}
|
|
33
|
+
/** Normalize a {@link PluginsField} (array | thunk | undefined) to a fresh array, awaiting a thunk. */
|
|
34
|
+
export declare function resolvePlugins(field: PluginsField | undefined): Promise<unknown[]>;
|
|
35
|
+
export interface LoadedApp {
|
|
36
|
+
readonly cwd: string;
|
|
37
|
+
readonly routesDir: string;
|
|
38
|
+
/** Build output dir (also where `nifra start` reads `manifest.json` + serves `/assets/*`). */
|
|
39
|
+
readonly outDir: string;
|
|
40
|
+
readonly framework: NifraFramework;
|
|
41
|
+
/** The `backend` export from `backend.ts`, or `undefined` if there's no `backend.ts`. */
|
|
42
|
+
readonly backend: unknown;
|
|
43
|
+
}
|
|
44
|
+
export interface LoadAppOptions {
|
|
45
|
+
/** Optional dynamic-import query used by long-lived MCP processes to bust Bun's module cache. */
|
|
46
|
+
readonly importQuery?: string;
|
|
47
|
+
}
|
|
48
|
+
/** Discover + validate the app conventions rooted at `cwd`. Prefers `nifra.config.ts`, falls back to
|
|
49
|
+
* `framework.ts`. Throws a clear, actionable error if neither exists or the config is malformed. */
|
|
50
|
+
export declare function loadApp(cwd: string, outDirName?: string, options?: LoadAppOptions): Promise<LoadedApp>;
|
|
51
|
+
//# sourceMappingURL=load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAiBA;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GACpB,SAAS,OAAO,EAAE,GAClB,CAAC,MAAM,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC,CAAA;AAE5D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,6FAA6F;IAC7F,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,YAAY,CAAA;IACnC;yEACqE;IACrE,QAAQ,CAAC,aAAa,CAAC,EAAE,YAAY,CAAA;IACrC;0GACsG;IACtG,QAAQ,CAAC,aAAa,CAAC,EAAE,YAAY,CAAA;IACrC,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACnD;AAED,uGAAuG;AACvG,wBAAsB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAGxF;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;IAClC,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAOD;oGACoG;AACpG,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,UAAU,SAAS,EACnB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,SAAS,CAAC,CA2CpB"}
|
package/dist/load.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Zero-config app discovery. The CLI reads a few conventions from the project root:
|
|
11
|
+
* - `routes/` — the file-based routes (per `@nifrajs/web/fs`).
|
|
12
|
+
* - `nifra.config.ts` — the CLI's framework wiring (see {@link NifraFramework}); `framework.ts` is
|
|
13
|
+
* used as a fallback. One of the two is required.
|
|
14
|
+
* - `backend.ts` — exports `backend` (a `@nifrajs/core` server, the contract). Optional.
|
|
15
|
+
*
|
|
16
|
+
* Why two filenames: a multi-target app's edge entry (`_worker.ts`) imports `framework.ts` for the
|
|
17
|
+
* render adapter, so `framework.ts` must stay edge-bundlable — it can't reference Vite plugins or the
|
|
18
|
+
* SFC compiler (Bun would pull them into the `target:"browser"` worker and fail on `child_process`).
|
|
19
|
+
* `nifra.config.ts` holds that build/dev tooling instead: it's imported ONLY by this CLI (which runs on
|
|
20
|
+
* Bun), so its Vite plugin + SFC-compiler imports (`@vitejs/plugin-vue`, `@nifrajs/web-vue/plugin`) never
|
|
21
|
+
* reach the edge bundle. A simple single-target app with no edge build can put it all in `framework.ts`.
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync } from "node:fs";
|
|
24
|
+
import { resolve } from "node:path";
|
|
25
|
+
/** Normalize a {@link PluginsField} (array | thunk | undefined) to a fresh array, awaiting a thunk. */
|
|
26
|
+
export async function resolvePlugins(field) {
|
|
27
|
+
const value = typeof field === "function" ? await field() : field;
|
|
28
|
+
return value ? [...value] : [];
|
|
29
|
+
}
|
|
30
|
+
const isAdapter = (v) => typeof v === "object" && v !== null;
|
|
31
|
+
const importWithQuery = (path, query) => import(__rewriteRelativeImportExtension(query === undefined || query === "" ? path : `${path}?${query}`));
|
|
32
|
+
/** Discover + validate the app conventions rooted at `cwd`. Prefers `nifra.config.ts`, falls back to
|
|
33
|
+
* `framework.ts`. Throws a clear, actionable error if neither exists or the config is malformed. */
|
|
34
|
+
export async function loadApp(cwd, outDirName = "dist", options = {}) {
|
|
35
|
+
// nifra.config.ts is the CLI's config (it may import Vite plugins / the SFC compiler, which a
|
|
36
|
+
// multi-target app keeps OUT of the edge-imported framework.ts). framework.ts is the fallback for a
|
|
37
|
+
// simple single-target app that has no edge build.
|
|
38
|
+
const configFile = existsSync(resolve(cwd, "nifra.config.ts"))
|
|
39
|
+
? "nifra.config.ts"
|
|
40
|
+
: "framework.ts";
|
|
41
|
+
const configPath = resolve(cwd, configFile);
|
|
42
|
+
if (!existsSync(configPath)) {
|
|
43
|
+
throw new Error(`[nifra] no nifra.config.ts or framework.ts found in ${cwd}.\n` +
|
|
44
|
+
" nifra is zero-config but needs one of them exporting at least:\n" +
|
|
45
|
+
" export const adapter = reactAdapter // from @nifrajs/web-react\n" +
|
|
46
|
+
' export const clientModule = "@nifrajs/web-react/client"\n' +
|
|
47
|
+
" (create-nifra scaffolds this for you.)");
|
|
48
|
+
}
|
|
49
|
+
if (!existsSync(resolve(cwd, "routes"))) {
|
|
50
|
+
throw new Error(`[nifra] no routes/ directory in ${cwd} — nifra apps are file-routed under routes/.`);
|
|
51
|
+
}
|
|
52
|
+
const fw = (await importWithQuery(configPath, options.importQuery));
|
|
53
|
+
if (!isAdapter(fw.adapter) || typeof fw.clientModule !== "string") {
|
|
54
|
+
throw new Error(`[nifra] ${configFile} must export \`adapter\` (a render adapter object) and \`clientModule\` (a string).`);
|
|
55
|
+
}
|
|
56
|
+
let backend;
|
|
57
|
+
const backendPath = resolve(cwd, "backend.ts");
|
|
58
|
+
if (existsSync(backendPath)) {
|
|
59
|
+
backend = (await importWithQuery(backendPath, options.importQuery))
|
|
60
|
+
.backend;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
cwd,
|
|
64
|
+
routesDir: resolve(cwd, "routes"),
|
|
65
|
+
outDir: resolve(cwd, outDirName),
|
|
66
|
+
framework: fw,
|
|
67
|
+
backend,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=load.js.map
|
package/dist/load.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":";;;;;;;;AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAsCnC,uGAAuG;AACvG,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAA+B;IAClE,MAAM,KAAK,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IACjE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAChC,CAAC;AAiBD,MAAM,SAAS,GAAG,CAAC,CAAU,EAAW,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAA;AAE9E,MAAM,eAAe,GAAG,CAAC,IAAY,EAAE,KAAyB,EAAoB,EAAE,CACpF,MAAM,kCAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,EAAC,CAAA;AAEzE;oGACoG;AACpG,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAW,EACX,UAAU,GAAG,MAAM,EACnB,UAA0B,EAAE;IAE5B,8FAA8F;IAC9F,oGAAoG;IACpG,mDAAmD;IACnD,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QAC5D,CAAC,CAAC,iBAAiB;QACnB,CAAC,CAAC,cAAc,CAAA;IAClB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAA;IAC3C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,uDAAuD,GAAG,KAAK;YAC7D,wEAAwE;YACxE,qFAAqF;YACrF,mEAAmE;YACnE,8CAA8C,CACjD,CAAA;IACH,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,mCAAmC,GAAG,8CAA8C,CACrF,CAAA;IACH,CAAC;IACD,MAAM,EAAE,GAAG,CAAC,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAA4B,CAAA;IAC9F,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,WAAW,UAAU,qFAAqF,CAC3G,CAAA;IACH,CAAC;IAED,IAAI,OAAgB,CAAA;IACpB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;IAC9C,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAI,CAAC,MAAM,eAAe,CAAC,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,CAA2B;aAC3F,OAAO,CAAA;IACZ,CAAC;IAED,OAAO;QACL,GAAG;QACH,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC;QACjC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC;QAChC,SAAS,EAAE,EAAoB;QAC/B,OAAO;KACR,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two project-INDEPENDENT MCP tools — `nifra_docs` + `nifra_example` — as a single factory shared
|
|
3
|
+
* by every transport: the project stdio server ({@link projectTools}), the CLI HTTP server
|
|
4
|
+
* (`nifra docs-mcp`), and the site's Cloudflare-Pages worker (`/mcp`). The corpus source is injected as
|
|
5
|
+
* loaders, so the SAME tool definitions work whether the corpus comes from disk (`Bun.file`, on the
|
|
6
|
+
* CLI) or a cached same-origin fetch (on the edge, no filesystem). One source → the tools can never be
|
|
7
|
+
* described differently across surfaces.
|
|
8
|
+
*/
|
|
9
|
+
import { type Example } from "./examples.js";
|
|
10
|
+
import type { McpTool } from "./mcp-protocol.js";
|
|
11
|
+
/** Build `nifra_docs` + `nifra_example` over injected corpus loaders. */
|
|
12
|
+
export declare function docsTools(loadDocs: () => Promise<string | undefined>, loadExamples: () => Promise<Example[] | undefined>): McpTool[];
|
|
13
|
+
//# sourceMappingURL=mcp-docs-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-docs-tools.d.ts","sourceRoot":"","sources":["../src/mcp-docs-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,KAAK,OAAO,EAAwB,MAAM,eAAe,CAAA;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAKhD,yEAAyE;AACzE,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,EAC3C,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,GACjD,OAAO,EAAE,CAmDX"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two project-INDEPENDENT MCP tools — `nifra_docs` + `nifra_example` — as a single factory shared
|
|
3
|
+
* by every transport: the project stdio server ({@link projectTools}), the CLI HTTP server
|
|
4
|
+
* (`nifra docs-mcp`), and the site's Cloudflare-Pages worker (`/mcp`). The corpus source is injected as
|
|
5
|
+
* loaders, so the SAME tool definitions work whether the corpus comes from disk (`Bun.file`, on the
|
|
6
|
+
* CLI) or a cached same-origin fetch (on the edge, no filesystem). One source → the tools can never be
|
|
7
|
+
* described differently across surfaces.
|
|
8
|
+
*/
|
|
9
|
+
import { renderDocsResult } from "./docs-search.js";
|
|
10
|
+
import { renderExamplesResult } from "./examples.js";
|
|
11
|
+
const clamp = (n, lo, hi, dflt) => Math.min(Math.max(typeof n === "number" ? n : dflt, lo), hi);
|
|
12
|
+
/** Build `nifra_docs` + `nifra_example` over injected corpus loaders. */
|
|
13
|
+
export function docsTools(loadDocs, loadExamples) {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
name: "nifra_docs",
|
|
17
|
+
description: 'Search nifra\'s framework documentation and get back ONLY the matching sections — auth, uploads, ISR, WebSockets, loaders, deployment, anything. Call with no query for the cheap section index; pass query (e.g. "isr revalidate") for the top matching sections. Use this instead of reading llms-full.txt (~150 KB) whole.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
query: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Keywords to match against doc sections (omit for the index).",
|
|
24
|
+
},
|
|
25
|
+
limit: { type: "number", description: "Max sections to return (default 3, max 5)." },
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
},
|
|
29
|
+
handler: async (args) => {
|
|
30
|
+
const corpus = await loadDocs();
|
|
31
|
+
if (corpus === undefined) {
|
|
32
|
+
return "docs corpus missing — run `bun run gen:llms` in the nifra repo (published builds ship it).";
|
|
33
|
+
}
|
|
34
|
+
const { query, limit } = args;
|
|
35
|
+
return renderDocsResult(corpus, query, clamp(limit, 1, 5, 3));
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "nifra_example",
|
|
40
|
+
description: 'Get a VERIFIED, copy-pasteable nifra code example for a task — auth route, file upload, ISR page, loader/action, typed client, SSE, deployment, etc. Every snippet is typechecked against the installed nifra version, so it compiles as-is. PREFER THIS over writing nifra code from memory (training data drifts). Call with no query for the grouped index; pass query (e.g. "protected route", "upload", "isr revalidate") for matching snippets.',
|
|
41
|
+
inputSchema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
query: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "What you want an example of (omit for the index).",
|
|
47
|
+
},
|
|
48
|
+
limit: { type: "number", description: "Max examples to return (default 3, max 5)." },
|
|
49
|
+
},
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
},
|
|
52
|
+
handler: async (args) => {
|
|
53
|
+
const corpus = await loadExamples();
|
|
54
|
+
if (corpus === undefined) {
|
|
55
|
+
return "examples corpus missing — run `bun run gen:llms` in the nifra repo (published builds ship it).";
|
|
56
|
+
}
|
|
57
|
+
const { query, limit } = args;
|
|
58
|
+
return renderExamplesResult(corpus, query, clamp(limit, 1, 5, 3));
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=mcp-docs-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-docs-tools.js","sourceRoot":"","sources":["../src/mcp-docs-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAgB,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAGlE,MAAM,KAAK,GAAG,CAAC,CAAqB,EAAE,EAAU,EAAE,EAAU,EAAE,IAAY,EAAU,EAAE,CACpF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;AAE9D,yEAAyE;AACzE,MAAM,UAAU,SAAS,CACvB,QAA2C,EAC3C,YAAkD;IAElD,OAAO;QACL;YACE,IAAI,EAAE,YAAY;YAClB,WAAW,EACT,+TAA+T;YACjU,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,8DAA8D;qBAC5E;oBACD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4CAA4C,EAAE;iBACrF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;gBACtB,MAAM,MAAM,GAAG,MAAM,QAAQ,EAAE,CAAA;gBAC/B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,OAAO,4FAA4F,CAAA;gBACrG,CAAC;gBACD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAA0C,CAAA;gBACnE,OAAO,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAC/D,CAAC;SACF;QACD;YACE,IAAI,EAAE,eAAe;YACrB,WAAW,EACT,ubAAub;YACzb,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,mDAAmD;qBACjE;oBACD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4CAA4C,EAAE;iBACrF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;gBACtB,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAA;gBACnC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,OAAO,gGAAgG,CAAA;gBACzG,CAAC;gBACD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAA0C,CAAA;gBACnE,OAAO,oBAAoB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YACnE,CAAC;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PUBLIC nifra docs MCP — Streamable-HTTP transport exposing the project-INDEPENDENT tools
|
|
3
|
+
* (`nifra_docs` + `nifra_example`) so any remote AI agent can learn nifra without a local checkout.
|
|
4
|
+
* Both tools read the bundled corpus (llms-full.txt / examples.json shipped in this package) and ignore
|
|
5
|
+
* `cwd`, so they reuse the exact definitions from {@link projectTools} — one source, no drift.
|
|
6
|
+
*
|
|
7
|
+
* {@link handleMcpHttp} is a plain Web `fetch` handler, so it self-hosts ANYWHERE Bun runs — the
|
|
8
|
+
* simplest path is `nifra docs-mcp` on a VPS behind a reverse proxy (Caddy/nginx + TLS), where the
|
|
9
|
+
* bundled corpus reads straight from disk (`Bun.file`). `export default { port, fetch }` also lets
|
|
10
|
+
* `bun run mcp-http.ts` serve it directly, and the same handler runs on Cloudflare/Vercel edge — only
|
|
11
|
+
* there must you inline the two corpus files as string imports (a Worker has no filesystem). The
|
|
12
|
+
* dispatch is the shared, transport-agnostic {@link handleRpc}.
|
|
13
|
+
*/
|
|
14
|
+
import { type McpTool } from "./mcp-protocol.js";
|
|
15
|
+
export type { Example } from "./examples.js";
|
|
16
|
+
export { docsTools } from "./mcp-docs-tools.js";
|
|
17
|
+
export interface McpHttpOptions {
|
|
18
|
+
/** Maximum JSON-RPC request body size in bytes. Default 1 MB. */
|
|
19
|
+
readonly maxBodyBytes?: number;
|
|
20
|
+
}
|
|
21
|
+
/** The two project-independent tools, reading the package's bundled corpus from disk (CLI use). */
|
|
22
|
+
export declare function publicDocsTools(): McpTool[];
|
|
23
|
+
/**
|
|
24
|
+
* The transport core, shared by every HTTP host (CLI `docs-mcp`, the site's edge worker): handle one
|
|
25
|
+
* MCP request against the given `tools`. POST a JSON-RPC body → JSON-RPC response; GET is a health page;
|
|
26
|
+
* OPTIONS is the CORS preflight. Never throws — a bad body becomes a JSON-RPC parse error. Pass the
|
|
27
|
+
* tools so each host supplies its own corpus source (disk vs cached fetch) behind identical defs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function respondMcpHttp(request: Request, tools: McpTool[], options?: McpHttpOptions): Promise<Response>;
|
|
30
|
+
/** The CLI HTTP handler: serves the disk-backed corpus tools. (`nifra docs-mcp` / `bun run` this file.) */
|
|
31
|
+
export declare function handleMcpHttp(request: Request): Promise<Response>;
|
|
32
|
+
/**
|
|
33
|
+
* Worker/edge + local entry. `export default { fetch }` is the universal server shape: Cloudflare /
|
|
34
|
+
* Vercel edge / Deno deploy use `fetch` (and ignore `port`); `bun run mcp-http.ts` auto-serves it on
|
|
35
|
+
* `port` (PORT env, default 8787) — Bun serves a module's default-exported server, so NO manual
|
|
36
|
+
* `Bun.serve` here (that would double-bind the port).
|
|
37
|
+
*/
|
|
38
|
+
declare const _default: {
|
|
39
|
+
port: number;
|
|
40
|
+
fetch: typeof handleMcpHttp;
|
|
41
|
+
};
|
|
42
|
+
export default _default;
|
|
43
|
+
//# sourceMappingURL=mcp-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-http.d.ts","sourceRoot":"","sources":["../src/mcp-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAkC,KAAK,OAAO,EAAY,MAAM,mBAAmB,CAAA;AAE1F,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAG5C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAc/C,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAuDD,mGAAmG;AACnG,wBAAgB,eAAe,IAAI,OAAO,EAAE,CAE3C;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,OAAO,EAAE,EAChB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA6BnB;AAED,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEjE;AAED;;;;;GAKG;;;;;AACH,wBAGC"}
|