@silkweave/nextjs 3.2.0 → 4.0.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/build/index.d.mts +0 -14
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +27 -124
- package/build/index.mjs.map +1 -1
- package/build/mcpRoute-Bj68_Fem.mjs +79 -0
- package/build/mcpRoute-Bj68_Fem.mjs.map +1 -0
- package/build/rolldown-runtime-wcPFST8Q.mjs +13 -0
- package/build/trpcRoute--ilYwTJu.mjs +58 -0
- package/build/trpcRoute--ilYwTJu.mjs.map +1 -0
- package/package.json +19 -7
package/build/index.d.mts
CHANGED
|
@@ -78,20 +78,6 @@ interface SilkweaveApp<Arr extends readonly Action[]> {
|
|
|
78
78
|
*/
|
|
79
79
|
readonly Router: InferTrpcRouter<AppServer<Arr>>;
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Define a Silkweave app from a single set of Actions and project it onto
|
|
83
|
-
* Next.js App Router route handlers - MCP (for agents) and tRPC (for your
|
|
84
|
-
* frontend) - from one source of truth.
|
|
85
|
-
*
|
|
86
|
-
* ```ts
|
|
87
|
-
* // silkweave/server.ts
|
|
88
|
-
* export const app = defineSilkweave({
|
|
89
|
-
* name: 'my-app', description: '...', version: '1.0.0',
|
|
90
|
-
* actions: [listUsers, getUser]
|
|
91
|
-
* })
|
|
92
|
-
* export type AppRouter = typeof app.Router
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
81
|
declare function defineSilkweave<const Arr extends readonly Action[]>(options: DefineSilkweaveOptions<Arr>): SilkweaveApp<Arr>;
|
|
96
82
|
//#endregion
|
|
97
83
|
//#region src/lib/mcpRoute.d.ts
|
package/build/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/lib/defineSilkweave.ts","../src/lib/mcpRoute.ts","../src/lib/trpcRoute.ts","../src/lib/stripPrefix.ts"],"mappings":";;;;;;KAGY,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;;UAG5C,eAAA;EAHL;;;;;;;;;EAaV,QAAA;EAbmD;EAenD,IAAA,GAAO,UAAA;EAf4D;EAiBnE,kBAAA;AAAA;;UAIe,gBAAA;EANE;;;;;EAYjB,QAAA;EAVkB;EAYlB,IAAA,GAAO,UAAA;EARwB;;;;;EAc/B,IAAA;AAAA;;UAIe,gBAAA;EACf,GAAA,EAAK,gBAAA;EACL,IAAA,EAAM,gBAAA;EACN,MAAA,EAAQ,gBAAA;EACR,OAAA,EAAS,gBAAA;AAAA;;UAIM,iBAAA;EACf,GAAA,EAAK,gBAAA;EACL,IAAA,EAAM,gBAAA;EACN,OAAA,EAAS,gBAAA;AAAA;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/lib/defineSilkweave.ts","../src/lib/mcpRoute.ts","../src/lib/trpcRoute.ts","../src/lib/stripPrefix.ts"],"mappings":";;;;;;KAGY,gBAAA,IAAoB,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;;UAG5C,eAAA;EAHL;;;;;;;;;EAaV,QAAA;EAbmD;EAenD,IAAA,GAAO,UAAA;EAf4D;EAiBnE,kBAAA;AAAA;;UAIe,gBAAA;EANE;;;;;EAYjB,QAAA;EAVkB;EAYlB,IAAA,GAAO,UAAA;EARwB;;;;;EAc/B,IAAA;AAAA;;UAIe,gBAAA;EACf,GAAA,EAAK,gBAAA;EACL,IAAA,EAAM,gBAAA;EACN,MAAA,EAAQ,gBAAA;EACR,OAAA,EAAS,gBAAA;AAAA;;UAIM,iBAAA;EACf,GAAA,EAAK,gBAAA;EACL,IAAA,EAAM,gBAAA;EACN,OAAA,EAAS,gBAAA;AAAA;;;;KChDN,aAAA,sBAAmC,MAAA,cAChC,GAAA,YAAe,CAAA,WAAY,CAAA;;KAI9B,SAAA,sBAA+B,MAAA,MAAY,SAAA,CAAU,aAAA,CAAc,GAAA;;KAG5D,sBAAA,sBAA4C,MAAA,MAAY,gBAAA;EAClE,OAAA,EAAS,GAAA;AAAA;;;;;UAOM,YAAA,sBAAkC,MAAA;EDlBkB;ECoBnE,GAAA,CAAI,OAAA,EAAS,eAAA,GAAkB,gBAAA;EDjBD;ECmB9B,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,iBAAA;EDPhB;;;;;EAAA,SCaR,MAAA,EAAQ,eAAA,CAAgB,SAAA,CAAU,GAAA;AAAA;AAAA,iBAqC7B,eAAA,4BAA2C,MAAA,GAAA,CACzD,OAAA,EAAS,sBAAA,CAAuB,GAAA,IAC/B,YAAA,CAAa,GAAA;;;;;;ADnEhB;;;;;iBEUgB,aAAA,CACd,QAAA,EAAU,gBAAA,EACV,OAAA,WAAkB,MAAA,IAClB,OAAA,EAAS,eAAA,GACR,gBAAA;;;;;;AFdH;;;;;iBGyBgB,cAAA,CACd,QAAA,EAAU,gBAAA,EACV,OAAA,WAAkB,MAAA,IAClB,OAAA,EAAS,gBAAA,GACR,iBAAA;;;;;;;iBC5Ba,iBAAA,CAAkB,QAAA;AJDlC;;;;;;;;;;;;;AAAA,iBIoBgB,kBAAA,CAAmB,OAAA,EAAS,OAAA,EAAS,QAAA,UAAkB,QAAA,YAAoB,OAAA"}
|
package/build/index.mjs
CHANGED
|
@@ -1,125 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { trpcFetch } from "@silkweave/trpc";
|
|
4
|
-
//#region src/lib/stripPrefix.ts
|
|
5
|
-
/**
|
|
6
|
-
* Normalize a user-supplied mount path: ensure a single leading slash and no
|
|
7
|
-
* trailing slash. `'api/mcp/'` -> `'/api/mcp'`, `'/'` -> `''`.
|
|
8
|
-
*/
|
|
9
|
-
function normalizeBasePath(basePath) {
|
|
10
|
-
const trimmed = basePath.trim().replace(/\/+$/, "");
|
|
11
|
-
if (trimmed === "" || trimmed === "/") return "";
|
|
12
|
-
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Rewrite a Web `Request`'s URL so the inner `@silkweave/edge` MCP handler -
|
|
16
|
-
* which matches absolute pathnames (`/mcp`, `/authorize`, `/.well-known/...`) -
|
|
17
|
-
* sees canonical paths regardless of where the Next.js route is mounted.
|
|
18
|
-
*
|
|
19
|
-
* Given `basePath = '/api/mcp'`:
|
|
20
|
-
* `/api/mcp` -> `/mcp` (the transport root)
|
|
21
|
-
* `/api/mcp/authorize` -> `/authorize`
|
|
22
|
-
* `/api/mcp/.well-known/oauth-...` -> `/.well-known/oauth-...`
|
|
23
|
-
*
|
|
24
|
-
* The method, headers, body and abort signal are preserved. A streaming body
|
|
25
|
-
* requires `duplex: 'half'` under Node's undici `fetch` implementation.
|
|
26
|
-
*/
|
|
27
|
-
function rewriteRequestPath(request, basePath, fallback = "/mcp") {
|
|
28
|
-
const base = normalizeBasePath(basePath);
|
|
29
|
-
const url = new URL(request.url);
|
|
30
|
-
let rest = url.pathname;
|
|
31
|
-
if (base !== "") {
|
|
32
|
-
if (rest === base) rest = fallback;
|
|
33
|
-
else if (rest.startsWith(`${base}/`)) rest = rest.slice(base.length);
|
|
34
|
-
}
|
|
35
|
-
if (rest === "" || rest === "/") rest = fallback;
|
|
36
|
-
url.pathname = rest;
|
|
37
|
-
const init = {
|
|
38
|
-
method: request.method,
|
|
39
|
-
headers: request.headers,
|
|
40
|
-
signal: request.signal
|
|
41
|
-
};
|
|
42
|
-
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
43
|
-
init.body = request.body;
|
|
44
|
-
init.duplex = "half";
|
|
45
|
-
}
|
|
46
|
-
return new Request(url.toString(), init);
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
//#region src/lib/mcpRoute.ts
|
|
50
|
-
/**
|
|
51
|
-
* Build the MCP route handlers for a single catch-all Next.js route file.
|
|
52
|
-
*
|
|
53
|
-
* Wires the actions through `@silkweave/edge` (stateless MCP over Web Standard
|
|
54
|
-
* Streamable HTTP) and wraps its handler with a prefix-stripping rewrite so the
|
|
55
|
-
* transport, OAuth routes and protected-resource metadata all resolve from one
|
|
56
|
-
* `app/<basePath>/[[...slug]]/route.ts` file.
|
|
57
|
-
*/
|
|
58
|
-
function buildMcpRoute(identity, actions, options) {
|
|
59
|
-
const basePath = normalizeBasePath(options.basePath);
|
|
60
|
-
const { adapter, handler } = edge({
|
|
61
|
-
auth: options.auth,
|
|
62
|
-
enableJsonResponse: options.enableJsonResponse
|
|
63
|
-
});
|
|
64
|
-
silkweave(identity).actions(actions).adapter(adapter).start();
|
|
65
|
-
const route = (request) => handler(rewriteRequestPath(request, basePath));
|
|
66
|
-
return {
|
|
67
|
-
GET: route,
|
|
68
|
-
POST: route,
|
|
69
|
-
DELETE: route,
|
|
70
|
-
OPTIONS: route
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/lib/trpcRoute.ts
|
|
75
|
-
const CORS_HEADERS = {
|
|
76
|
-
"Access-Control-Allow-Origin": "*",
|
|
77
|
-
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
78
|
-
"Access-Control-Allow-Headers": "*",
|
|
79
|
-
"Access-Control-Max-Age": "86400"
|
|
80
|
-
};
|
|
81
|
-
function withCors(handler) {
|
|
82
|
-
return async (request) => {
|
|
83
|
-
const response = await handler(request);
|
|
84
|
-
const headers = new Headers(response.headers);
|
|
85
|
-
for (const [key, value] of Object.entries(CORS_HEADERS)) headers.set(key, value);
|
|
86
|
-
return new Response(response.body, {
|
|
87
|
-
status: response.status,
|
|
88
|
-
statusText: response.statusText,
|
|
89
|
-
headers
|
|
90
|
-
});
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Build the tRPC route handlers for a `[trpc]` Next.js route file.
|
|
95
|
-
*
|
|
96
|
-
* Wires the actions through `@silkweave/trpc`'s fetch handler. tRPC strips the
|
|
97
|
-
* `endpoint` prefix itself, so no URL rewriting is needed. CORS is opt-in
|
|
98
|
-
* (`options.cors`) since a Next.js frontend hitting its own `/api/trpc` is
|
|
99
|
-
* same-origin.
|
|
100
|
-
*/
|
|
101
|
-
function buildTrpcRoute(identity, actions, options) {
|
|
102
|
-
const { adapter, GET, POST } = trpcFetch({
|
|
103
|
-
endpoint: options.endpoint,
|
|
104
|
-
auth: options.auth
|
|
105
|
-
});
|
|
106
|
-
silkweave(identity).actions(actions).adapter(adapter).start();
|
|
107
|
-
const optionsHandler = () => Promise.resolve(new Response(null, {
|
|
108
|
-
status: 204,
|
|
109
|
-
headers: options.cors ? CORS_HEADERS : {}
|
|
110
|
-
}));
|
|
111
|
-
if (!options.cors) return {
|
|
112
|
-
GET,
|
|
113
|
-
POST,
|
|
114
|
-
OPTIONS: optionsHandler
|
|
115
|
-
};
|
|
116
|
-
return {
|
|
117
|
-
GET: withCors(GET),
|
|
118
|
-
POST: withCors(POST),
|
|
119
|
-
OPTIONS: optionsHandler
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
//#endregion
|
|
1
|
+
import { i as rewriteRequestPath, r as normalizeBasePath, t as buildMcpRoute } from "./mcpRoute-Bj68_Fem.mjs";
|
|
2
|
+
import { t as buildTrpcRoute } from "./trpcRoute--ilYwTJu.mjs";
|
|
123
3
|
//#region src/lib/defineSilkweave.ts
|
|
124
4
|
/**
|
|
125
5
|
* Define a Silkweave app from a single set of Actions and project it onto
|
|
@@ -135,11 +15,34 @@ function buildTrpcRoute(identity, actions, options) {
|
|
|
135
15
|
* export type AppRouter = typeof app.Router
|
|
136
16
|
* ```
|
|
137
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Build route handlers whose implementing module is `import()`ed lazily on first
|
|
20
|
+
* request, so an app that mounts only one surface never loads the other's stack:
|
|
21
|
+
* an MCP-only app never pulls in `@silkweave/trpc` (an optional peer), and a
|
|
22
|
+
* tRPC-only app never pulls in `@silkweave/edge`. `build()` memoizes across
|
|
23
|
+
* methods, so the underlying `silkweave().start()` runs at most once.
|
|
24
|
+
*/
|
|
25
|
+
function lazyHandlers(methods, load) {
|
|
26
|
+
let built;
|
|
27
|
+
const build = () => built ??= load();
|
|
28
|
+
const handlers = {};
|
|
29
|
+
for (const method of methods) handlers[method] = async (request) => (await build())[method](request);
|
|
30
|
+
return handlers;
|
|
31
|
+
}
|
|
138
32
|
function defineSilkweave(options) {
|
|
139
33
|
const { actions, ...identity } = options;
|
|
140
34
|
return {
|
|
141
|
-
mcp: (mcpOptions) =>
|
|
142
|
-
|
|
35
|
+
mcp: (mcpOptions) => lazyHandlers([
|
|
36
|
+
"GET",
|
|
37
|
+
"POST",
|
|
38
|
+
"DELETE",
|
|
39
|
+
"OPTIONS"
|
|
40
|
+
], async () => (await import("./mcpRoute-Bj68_Fem.mjs").then((n) => n.n)).buildMcpRoute(identity, actions, mcpOptions)),
|
|
41
|
+
trpc: (trpcOptions) => lazyHandlers([
|
|
42
|
+
"GET",
|
|
43
|
+
"POST",
|
|
44
|
+
"OPTIONS"
|
|
45
|
+
], async () => (await import("./trpcRoute--ilYwTJu.mjs").then((n) => n.n)).buildTrpcRoute(identity, actions, trpcOptions)),
|
|
143
46
|
Router: void 0
|
|
144
47
|
};
|
|
145
48
|
}
|
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/defineSilkweave.ts"],"sourcesContent":["import { Action, Silkweave, SilkweaveOptions } from '@silkweave/core'\nimport type { InferTrpcRouter } from '@silkweave/trpc'\nimport { McpRouteHandlers, McpRouteOptions, NextRouteHandler, TrpcRouteHandlers, TrpcRouteOptions } from '../types.js'\n\n/** Maps the actions tuple into the `Record<name, Action>` shape `Silkweave` carries. */\ntype ActionsRecord<Arr extends readonly Action[]> = {\n [K in Arr[number] as K['name']]: K\n}\n\n/** The typed `Silkweave` instance equivalent to `silkweave(...).actions(arr)`. */\ntype AppServer<Arr extends readonly Action[]> = Silkweave<ActionsRecord<Arr>>\n\n/** Configuration for {@link defineSilkweave}: server identity + the action set. */\nexport type DefineSilkweaveOptions<Arr extends readonly Action[]> = SilkweaveOptions & {\n actions: Arr\n}\n\n/**\n * A Silkweave app projected onto Next.js App Router route handlers. Define it\n * once, then mount the surfaces you need from their respective route files.\n */\nexport interface SilkweaveApp<Arr extends readonly Action[]> {\n /** Build handlers for `app/<basePath>/[[...slug]]/route.ts` (MCP tools). */\n mcp(options: McpRouteOptions): McpRouteHandlers\n /** Build handlers for `app/<endpoint>/[trpc]/route.ts` (tRPC procedures). */\n trpc(options: TrpcRouteOptions): TrpcRouteHandlers\n /**\n * Type-only phantom for the tRPC client. Use as\n * `export type AppRouter = typeof app.Router`. Accessing the value at runtime\n * returns `undefined` - it exists purely to carry the inferred router type.\n */\n readonly Router: InferTrpcRouter<AppServer<Arr>>\n}\n\n/**\n * Define a Silkweave app from a single set of Actions and project it onto\n * Next.js App Router route handlers - MCP (for agents) and tRPC (for your\n * frontend) - from one source of truth.\n *\n * ```ts\n * // silkweave/server.ts\n * export const app = defineSilkweave({\n * name: 'my-app', description: '...', version: '1.0.0',\n * actions: [listUsers, getUser]\n * })\n * export type AppRouter = typeof app.Router\n * ```\n */\n/**\n * Build route handlers whose implementing module is `import()`ed lazily on first\n * request, so an app that mounts only one surface never loads the other's stack:\n * an MCP-only app never pulls in `@silkweave/trpc` (an optional peer), and a\n * tRPC-only app never pulls in `@silkweave/edge`. `build()` memoizes across\n * methods, so the underlying `silkweave().start()` runs at most once.\n */\nfunction lazyHandlers<K extends string>(\n methods: readonly K[],\n load: () => Promise<Record<K, NextRouteHandler>>\n): Record<K, NextRouteHandler> {\n let built: Promise<Record<K, NextRouteHandler>> | undefined\n const build = () => (built ??= load())\n const handlers = {} as Record<K, NextRouteHandler>\n for (const method of methods) {\n handlers[method] = async (request) => (await build())[method](request)\n }\n return handlers\n}\n\nexport function defineSilkweave<const Arr extends readonly Action[]>(\n options: DefineSilkweaveOptions<Arr>\n): SilkweaveApp<Arr> {\n const { actions, ...identity } = options\n\n return {\n mcp: (mcpOptions) => lazyHandlers(['GET', 'POST', 'DELETE', 'OPTIONS'], async () =>\n (await import('./mcpRoute.js')).buildMcpRoute(identity, actions, mcpOptions)),\n trpc: (trpcOptions) => lazyHandlers(['GET', 'POST', 'OPTIONS'], async () =>\n (await import('./trpcRoute.js')).buildTrpcRoute(identity, actions, trpcOptions)),\n Router: undefined as unknown as InferTrpcRouter<AppServer<Arr>>\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuDA,SAAS,aACP,SACA,MAC6B;CAC7B,IAAI;CACJ,MAAM,cAAe,UAAU,MAAM;CACrC,MAAM,WAAW,EAAE;AACnB,MAAK,MAAM,UAAU,QACnB,UAAS,UAAU,OAAO,aAAa,MAAM,OAAO,EAAE,QAAQ,QAAQ;AAExE,QAAO;;AAGT,SAAgB,gBACd,SACmB;CACnB,MAAM,EAAE,SAAS,GAAG,aAAa;AAEjC,QAAO;EACL,MAAM,eAAe,aAAa;GAAC;GAAO;GAAQ;GAAU;GAAU,EAAE,aACrE,MAAM,OAAO,2BAAA,MAAA,MAAA,EAAA,EAAA,EAAkB,cAAc,UAAU,SAAS,WAAW,CAAC;EAC/E,OAAO,gBAAgB,aAAa;GAAC;GAAO;GAAQ;GAAU,EAAE,aAC7D,MAAM,OAAO,4BAAA,MAAA,MAAA,EAAA,EAAA,EAAmB,eAAe,UAAU,SAAS,YAAY,CAAC;EAClF,QAAQ,KAAA;EACT"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
|
|
2
|
+
import { silkweave } from "@silkweave/core";
|
|
3
|
+
import { edge } from "@silkweave/edge";
|
|
4
|
+
//#region src/lib/stripPrefix.ts
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a user-supplied mount path: ensure a single leading slash and no
|
|
7
|
+
* trailing slash. `'api/mcp/'` -> `'/api/mcp'`, `'/'` -> `''`.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeBasePath(basePath) {
|
|
10
|
+
const trimmed = basePath.trim().replace(/\/+$/, "");
|
|
11
|
+
if (trimmed === "" || trimmed === "/") return "";
|
|
12
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Rewrite a Web `Request`'s URL so the inner `@silkweave/edge` MCP handler -
|
|
16
|
+
* which matches absolute pathnames (`/mcp`, `/authorize`, `/.well-known/...`) -
|
|
17
|
+
* sees canonical paths regardless of where the Next.js route is mounted.
|
|
18
|
+
*
|
|
19
|
+
* Given `basePath = '/api/mcp'`:
|
|
20
|
+
* `/api/mcp` -> `/mcp` (the transport root)
|
|
21
|
+
* `/api/mcp/authorize` -> `/authorize`
|
|
22
|
+
* `/api/mcp/.well-known/oauth-...` -> `/.well-known/oauth-...`
|
|
23
|
+
*
|
|
24
|
+
* The method, headers, body and abort signal are preserved. A streaming body
|
|
25
|
+
* requires `duplex: 'half'` under Node's undici `fetch` implementation.
|
|
26
|
+
*/
|
|
27
|
+
function rewriteRequestPath(request, basePath, fallback = "/mcp") {
|
|
28
|
+
const base = normalizeBasePath(basePath);
|
|
29
|
+
const url = new URL(request.url);
|
|
30
|
+
let rest = url.pathname;
|
|
31
|
+
if (base !== "") {
|
|
32
|
+
if (rest === base) rest = fallback;
|
|
33
|
+
else if (rest.startsWith(`${base}/`)) rest = rest.slice(base.length);
|
|
34
|
+
}
|
|
35
|
+
if (rest === "" || rest === "/") rest = fallback;
|
|
36
|
+
url.pathname = rest;
|
|
37
|
+
const init = {
|
|
38
|
+
method: request.method,
|
|
39
|
+
headers: request.headers,
|
|
40
|
+
signal: request.signal
|
|
41
|
+
};
|
|
42
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
43
|
+
init.body = request.body;
|
|
44
|
+
init.duplex = "half";
|
|
45
|
+
}
|
|
46
|
+
return new Request(url.toString(), init);
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/lib/mcpRoute.ts
|
|
50
|
+
var mcpRoute_exports = /* @__PURE__ */ __exportAll({ buildMcpRoute: () => buildMcpRoute });
|
|
51
|
+
/**
|
|
52
|
+
* Build the MCP route handlers for a single catch-all Next.js route file.
|
|
53
|
+
*
|
|
54
|
+
* Wires the actions through `@silkweave/edge` (stateless MCP over Web Standard
|
|
55
|
+
* Streamable HTTP) and wraps its handler with a prefix-stripping rewrite so the
|
|
56
|
+
* transport, OAuth routes and protected-resource metadata all resolve from one
|
|
57
|
+
* `app/<basePath>/[[...slug]]/route.ts` file.
|
|
58
|
+
*/
|
|
59
|
+
function buildMcpRoute(identity, actions, options) {
|
|
60
|
+
const basePath = normalizeBasePath(options.basePath);
|
|
61
|
+
const { adapter, handler } = edge({
|
|
62
|
+
auth: options.auth,
|
|
63
|
+
enableJsonResponse: options.enableJsonResponse
|
|
64
|
+
});
|
|
65
|
+
silkweave(identity).actions(actions).adapter(adapter).start().catch((error) => {
|
|
66
|
+
console.error("[silkweave/nextjs] MCP adapter failed to start:", error);
|
|
67
|
+
});
|
|
68
|
+
const route = (request) => handler(rewriteRequestPath(request, basePath));
|
|
69
|
+
return {
|
|
70
|
+
GET: route,
|
|
71
|
+
POST: route,
|
|
72
|
+
DELETE: route,
|
|
73
|
+
OPTIONS: route
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { rewriteRequestPath as i, mcpRoute_exports as n, normalizeBasePath as r, buildMcpRoute as t };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=mcpRoute-Bj68_Fem.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRoute-Bj68_Fem.mjs","names":[],"sources":["../src/lib/stripPrefix.ts","../src/lib/mcpRoute.ts"],"sourcesContent":["/**\n * Normalize a user-supplied mount path: ensure a single leading slash and no\n * trailing slash. `'api/mcp/'` -> `'/api/mcp'`, `'/'` -> `''`.\n */\nexport function normalizeBasePath(basePath: string): string {\n const trimmed = basePath.trim().replace(/\\/+$/, '')\n if (trimmed === '' || trimmed === '/') { return '' }\n return trimmed.startsWith('/') ? trimmed : `/${trimmed}`\n}\n\n/**\n * Rewrite a Web `Request`'s URL so the inner `@silkweave/edge` MCP handler -\n * which matches absolute pathnames (`/mcp`, `/authorize`, `/.well-known/...`) -\n * sees canonical paths regardless of where the Next.js route is mounted.\n *\n * Given `basePath = '/api/mcp'`:\n * `/api/mcp` -> `/mcp` (the transport root)\n * `/api/mcp/authorize` -> `/authorize`\n * `/api/mcp/.well-known/oauth-...` -> `/.well-known/oauth-...`\n *\n * The method, headers, body and abort signal are preserved. A streaming body\n * requires `duplex: 'half'` under Node's undici `fetch` implementation.\n */\nexport function rewriteRequestPath(request: Request, basePath: string, fallback = '/mcp'): Request {\n const base = normalizeBasePath(basePath)\n const url = new URL(request.url)\n\n let rest = url.pathname\n if (base !== '') {\n if (rest === base) {\n rest = fallback\n } else if (rest.startsWith(`${base}/`)) {\n rest = rest.slice(base.length)\n }\n }\n if (rest === '' || rest === '/') { rest = fallback }\n url.pathname = rest\n\n const init: RequestInit & { duplex?: 'half' } = {\n method: request.method,\n headers: request.headers,\n signal: request.signal\n }\n if (request.method !== 'GET' && request.method !== 'HEAD') {\n init.body = request.body\n init.duplex = 'half'\n }\n return new Request(url.toString(), init)\n}\n","import { Action, silkweave, SilkweaveOptions } from '@silkweave/core'\nimport { edge } from '@silkweave/edge'\nimport { McpRouteHandlers, McpRouteOptions } from '../types.js'\nimport { normalizeBasePath, rewriteRequestPath } from './stripPrefix.js'\n\n/**\n * Build the MCP route handlers for a single catch-all Next.js route file.\n *\n * Wires the actions through `@silkweave/edge` (stateless MCP over Web Standard\n * Streamable HTTP) and wraps its handler with a prefix-stripping rewrite so the\n * transport, OAuth routes and protected-resource metadata all resolve from one\n * `app/<basePath>/[[...slug]]/route.ts` file.\n */\nexport function buildMcpRoute(\n identity: SilkweaveOptions,\n actions: readonly Action[],\n options: McpRouteOptions\n): McpRouteHandlers {\n const basePath = normalizeBasePath(options.basePath)\n const { adapter, handler } = edge({\n auth: options.auth,\n enableJsonResponse: options.enableJsonResponse\n })\n\n // Kick off the adapter's `_ready` promise (the handler awaits it internally).\n // We don't await here - that would force a top-level `await` into the route\n // module - but we MUST catch: an unhandled start() rejection (e.g. an invalid\n // `disposition: 'structured'` action) would otherwise crash the Next server.\n // On boot failure edge()'s `_ready` rejects, so requests get a 500, not a hang.\n silkweave(identity).actions(actions).adapter(adapter).start().catch((error: unknown) => {\n console.error('[silkweave/nextjs] MCP adapter failed to start:', error)\n })\n\n const route = (request: Request): Promise<Response> => handler(rewriteRequestPath(request, basePath))\n\n return { GET: route, POST: route, DELETE: route, OPTIONS: route }\n}\n"],"mappings":";;;;;;;;AAIA,SAAgB,kBAAkB,UAA0B;CAC1D,MAAM,UAAU,SAAS,MAAM,CAAC,QAAQ,QAAQ,GAAG;AACnD,KAAI,YAAY,MAAM,YAAY,IAAO,QAAO;AAChD,QAAO,QAAQ,WAAW,IAAI,GAAG,UAAU,IAAI;;;;;;;;;;;;;;;AAgBjD,SAAgB,mBAAmB,SAAkB,UAAkB,WAAW,QAAiB;CACjG,MAAM,OAAO,kBAAkB,SAAS;CACxC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAEhC,IAAI,OAAO,IAAI;AACf,KAAI,SAAS;MACP,SAAS,KACX,QAAO;WACE,KAAK,WAAW,GAAG,KAAK,GAAG,CACpC,QAAO,KAAK,MAAM,KAAK,OAAO;;AAGlC,KAAI,SAAS,MAAM,SAAS,IAAO,QAAO;AAC1C,KAAI,WAAW;CAEf,MAAM,OAA0C;EAC9C,QAAQ,QAAQ;EAChB,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EACjB;AACD,KAAI,QAAQ,WAAW,SAAS,QAAQ,WAAW,QAAQ;AACzD,OAAK,OAAO,QAAQ;AACpB,OAAK,SAAS;;AAEhB,QAAO,IAAI,QAAQ,IAAI,UAAU,EAAE,KAAK;;;;;;;;;;;;;AClC1C,SAAgB,cACd,UACA,SACA,SACkB;CAClB,MAAM,WAAW,kBAAkB,QAAQ,SAAS;CACpD,MAAM,EAAE,SAAS,YAAY,KAAK;EAChC,MAAM,QAAQ;EACd,oBAAoB,QAAQ;EAC7B,CAAC;AAOF,WAAU,SAAS,CAAC,QAAQ,QAAQ,CAAC,QAAQ,QAAQ,CAAC,OAAO,CAAC,OAAO,UAAmB;AACtF,UAAQ,MAAM,mDAAmD,MAAM;GACvE;CAEF,MAAM,SAAS,YAAwC,QAAQ,mBAAmB,SAAS,SAAS,CAAC;AAErG,QAAO;EAAE,KAAK;EAAO,MAAM;EAAO,QAAQ;EAAO,SAAS;EAAO"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
|
|
2
|
+
import { silkweave } from "@silkweave/core";
|
|
3
|
+
import { trpcFetch } from "@silkweave/trpc";
|
|
4
|
+
//#region src/lib/trpcRoute.ts
|
|
5
|
+
var trpcRoute_exports = /* @__PURE__ */ __exportAll({ buildTrpcRoute: () => buildTrpcRoute });
|
|
6
|
+
const CORS_HEADERS = {
|
|
7
|
+
"Access-Control-Allow-Origin": "*",
|
|
8
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
9
|
+
"Access-Control-Allow-Headers": "*",
|
|
10
|
+
"Access-Control-Max-Age": "86400"
|
|
11
|
+
};
|
|
12
|
+
function withCors(handler) {
|
|
13
|
+
return async (request) => {
|
|
14
|
+
const response = await handler(request);
|
|
15
|
+
const headers = new Headers(response.headers);
|
|
16
|
+
for (const [key, value] of Object.entries(CORS_HEADERS)) headers.set(key, value);
|
|
17
|
+
return new Response(response.body, {
|
|
18
|
+
status: response.status,
|
|
19
|
+
statusText: response.statusText,
|
|
20
|
+
headers
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the tRPC route handlers for a `[trpc]` Next.js route file.
|
|
26
|
+
*
|
|
27
|
+
* Wires the actions through `@silkweave/trpc`'s fetch handler. tRPC strips the
|
|
28
|
+
* `endpoint` prefix itself, so no URL rewriting is needed. CORS is opt-in
|
|
29
|
+
* (`options.cors`) since a Next.js frontend hitting its own `/api/trpc` is
|
|
30
|
+
* same-origin.
|
|
31
|
+
*/
|
|
32
|
+
function buildTrpcRoute(identity, actions, options) {
|
|
33
|
+
const { adapter, GET, POST } = trpcFetch({
|
|
34
|
+
endpoint: options.endpoint,
|
|
35
|
+
auth: options.auth
|
|
36
|
+
});
|
|
37
|
+
silkweave(identity).actions(actions).adapter(adapter).start().catch((error) => {
|
|
38
|
+
console.error("[silkweave/nextjs] tRPC adapter failed to start:", error);
|
|
39
|
+
});
|
|
40
|
+
const optionsHandler = () => Promise.resolve(new Response(null, {
|
|
41
|
+
status: 204,
|
|
42
|
+
headers: options.cors ? CORS_HEADERS : {}
|
|
43
|
+
}));
|
|
44
|
+
if (!options.cors) return {
|
|
45
|
+
GET,
|
|
46
|
+
POST,
|
|
47
|
+
OPTIONS: optionsHandler
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
GET: withCors(GET),
|
|
51
|
+
POST: withCors(POST),
|
|
52
|
+
OPTIONS: optionsHandler
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { trpcRoute_exports as n, buildTrpcRoute as t };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=trpcRoute--ilYwTJu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trpcRoute--ilYwTJu.mjs","names":[],"sources":["../src/lib/trpcRoute.ts"],"sourcesContent":["import { Action, silkweave, SilkweaveOptions } from '@silkweave/core'\nimport { trpcFetch } from '@silkweave/trpc'\nimport { NextRouteHandler, TrpcRouteHandlers, TrpcRouteOptions } from '../types.js'\n\nconst CORS_HEADERS: Record<string, string> = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': '*',\n 'Access-Control-Max-Age': '86400'\n}\n\nfunction withCors(handler: NextRouteHandler): NextRouteHandler {\n return async (request) => {\n const response = await handler(request)\n const headers = new Headers(response.headers)\n for (const [key, value] of Object.entries(CORS_HEADERS)) { headers.set(key, value) }\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers })\n }\n}\n\n/**\n * Build the tRPC route handlers for a `[trpc]` Next.js route file.\n *\n * Wires the actions through `@silkweave/trpc`'s fetch handler. tRPC strips the\n * `endpoint` prefix itself, so no URL rewriting is needed. CORS is opt-in\n * (`options.cors`) since a Next.js frontend hitting its own `/api/trpc` is\n * same-origin.\n */\nexport function buildTrpcRoute(\n identity: SilkweaveOptions,\n actions: readonly Action[],\n options: TrpcRouteOptions\n): TrpcRouteHandlers {\n const { adapter, GET, POST } = trpcFetch({ endpoint: options.endpoint, auth: options.auth })\n\n // Catch boot failures: an unhandled start() rejection would crash the Next\n // server. trpcFetch's handler serves a 503 until `_ready` resolves.\n silkweave(identity).actions(actions).adapter(adapter).start().catch((error: unknown) => {\n console.error('[silkweave/nextjs] tRPC adapter failed to start:', error)\n })\n\n const optionsHandler: NextRouteHandler = () =>\n Promise.resolve(new Response(null, { status: 204, headers: options.cors ? CORS_HEADERS : {} }))\n\n if (!options.cors) {\n return { GET, POST, OPTIONS: optionsHandler }\n }\n return { GET: withCors(GET), POST: withCors(POST), OPTIONS: optionsHandler }\n}\n"],"mappings":";;;;;AAIA,MAAM,eAAuC;CAC3C,+BAA+B;CAC/B,gCAAgC;CAChC,gCAAgC;CAChC,0BAA0B;CAC3B;AAED,SAAS,SAAS,SAA6C;AAC7D,QAAO,OAAO,YAAY;EACxB,MAAM,WAAW,MAAM,QAAQ,QAAQ;EACvC,MAAM,UAAU,IAAI,QAAQ,SAAS,QAAQ;AAC7C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,aAAa,CAAI,SAAQ,IAAI,KAAK,MAAM;AAClF,SAAO,IAAI,SAAS,SAAS,MAAM;GAAE,QAAQ,SAAS;GAAQ,YAAY,SAAS;GAAY;GAAS,CAAC;;;;;;;;;;;AAY7G,SAAgB,eACd,UACA,SACA,SACmB;CACnB,MAAM,EAAE,SAAS,KAAK,SAAS,UAAU;EAAE,UAAU,QAAQ;EAAU,MAAM,QAAQ;EAAM,CAAC;AAI5F,WAAU,SAAS,CAAC,QAAQ,QAAQ,CAAC,QAAQ,QAAQ,CAAC,OAAO,CAAC,OAAO,UAAmB;AACtF,UAAQ,MAAM,oDAAoD,MAAM;GACxE;CAEF,MAAM,uBACJ,QAAQ,QAAQ,IAAI,SAAS,MAAM;EAAE,QAAQ;EAAK,SAAS,QAAQ,OAAO,eAAe,EAAE;EAAE,CAAC,CAAC;AAEjG,KAAI,CAAC,QAAQ,KACX,QAAO;EAAE;EAAK;EAAM,SAAS;EAAgB;AAE/C,QAAO;EAAE,KAAK,SAAS,IAAI;EAAE,MAAM,SAAS,KAAK;EAAE,SAAS;EAAgB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silkweave/nextjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Silkweave Next.js App Router Adapter",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.silkweave.dev",
|
|
@@ -26,23 +26,35 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@trpc/server": "^11.7.1",
|
|
30
29
|
"zod": "^3.25.0",
|
|
31
|
-
"@silkweave/auth": "
|
|
32
|
-
"@silkweave/core": "
|
|
33
|
-
"@silkweave/
|
|
34
|
-
|
|
30
|
+
"@silkweave/auth": "4.0.0",
|
|
31
|
+
"@silkweave/core": "4.0.0",
|
|
32
|
+
"@silkweave/edge": "4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@trpc/server": "^11.7.1",
|
|
36
|
+
"@silkweave/trpc": "4.0.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@silkweave/trpc": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"@trpc/server": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
35
45
|
},
|
|
36
46
|
"devDependencies": {
|
|
37
47
|
"@eslint/js": "^10.0.1",
|
|
38
48
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
49
|
+
"@trpc/server": "^11.7.1",
|
|
39
50
|
"@types/node": "^22.0.0",
|
|
40
51
|
"eslint": "^10.4.0",
|
|
41
52
|
"rimraf": "^6.1.3",
|
|
42
53
|
"tsdown": "^0.21.8",
|
|
43
54
|
"tsx": "^4.21.0",
|
|
44
55
|
"typescript": "^5.9.3",
|
|
45
|
-
"typescript-eslint": "^8.56.1"
|
|
56
|
+
"typescript-eslint": "^8.56.1",
|
|
57
|
+
"@silkweave/trpc": "4.0.0"
|
|
46
58
|
},
|
|
47
59
|
"scripts": {
|
|
48
60
|
"clean": "rimraf build",
|