@silkweave/nextjs 2.4.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/LICENSE +21 -0
- package/README.md +161 -0
- package/build/index.d.mts +141 -0
- package/build/index.d.mts.map +1 -0
- package/build/index.mjs +149 -0
- package/build/index.mjs.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Silkweave
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# @silkweave/nextjs
|
|
2
|
+
|
|
3
|
+
Next.js **App Router** adapter for Silkweave. Define a set of Actions once and
|
|
4
|
+
project them onto Next.js route handlers - **MCP tools** for agents and a
|
|
5
|
+
**tRPC endpoint** for your own frontend - from a single source of truth.
|
|
6
|
+
|
|
7
|
+
This package is action-first and additive: it adds route files to an existing
|
|
8
|
+
Next.js app, it doesn't restructure anything. Under the hood it wraps
|
|
9
|
+
[`@silkweave/vercel`](../vercel) (MCP over Web-Standard Streamable HTTP) and
|
|
10
|
+
[`@silkweave/trpc`](../trpc) (fetch handler), adding the Next.js glue -
|
|
11
|
+
catch-all path normalization, ergonomic route factories, and end-to-end tRPC
|
|
12
|
+
types.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @silkweave/nextjs @silkweave/core
|
|
18
|
+
# for the typed tRPC client on the frontend:
|
|
19
|
+
pnpm add @trpc/client
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Next.js 13.4+ (App Router). The package itself has no `next`/`react`
|
|
23
|
+
dependency - it only deals with Web-Standard `Request`/`Response`.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### 1. Define your actions and the app (single source of truth)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// silkweave/actions.ts
|
|
31
|
+
import { createAction } from '@silkweave/core'
|
|
32
|
+
import { z } from 'zod'
|
|
33
|
+
|
|
34
|
+
export const listUsers = createAction({
|
|
35
|
+
name: 'list-users',
|
|
36
|
+
kind: 'query',
|
|
37
|
+
description: 'List users',
|
|
38
|
+
input: z.object({ activeOnly: z.boolean().optional() }),
|
|
39
|
+
run: async ({ activeOnly }) => ({ users: activeOnly ? [] : [{ id: '1' }] })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const banUser = createAction({
|
|
43
|
+
name: 'ban-user',
|
|
44
|
+
description: 'Ban a user',
|
|
45
|
+
input: z.object({ id: z.string(), reason: z.string().min(3) }),
|
|
46
|
+
run: async ({ id, reason }) => ({ banned: id, reason })
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// silkweave/server.ts
|
|
52
|
+
import { defineSilkweave } from '@silkweave/nextjs'
|
|
53
|
+
import { banUser, listUsers } from './actions'
|
|
54
|
+
|
|
55
|
+
export const app = defineSilkweave({
|
|
56
|
+
name: 'my-app',
|
|
57
|
+
description: 'My app exposed to agents + frontend',
|
|
58
|
+
version: '1.0.0',
|
|
59
|
+
actions: [listUsers, banUser]
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Type-only export for the tRPC client:
|
|
63
|
+
export type AppRouter = typeof app.Router
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Mount the MCP route (one catch-all file)
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// app/api/mcp/[[...mcp]]/route.ts
|
|
70
|
+
import { app } from '@/silkweave/server'
|
|
71
|
+
|
|
72
|
+
export const { GET, POST, DELETE, OPTIONS } = app.mcp({ basePath: '/api/mcp' })
|
|
73
|
+
|
|
74
|
+
export const dynamic = 'force-dynamic'
|
|
75
|
+
export const runtime = 'nodejs'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The single `[[...mcp]]` catch-all file serves the MCP transport (`/api/mcp`)
|
|
79
|
+
plus any OAuth and protected-resource-metadata sub-paths
|
|
80
|
+
(`/api/mcp/.well-known/...`, `/api/mcp/authorize`, ...). `basePath` is stripped
|
|
81
|
+
from incoming requests so the underlying MCP handler - which matches absolute
|
|
82
|
+
paths - resolves correctly regardless of where you mount it.
|
|
83
|
+
|
|
84
|
+
### 3. Mount the tRPC route
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// app/api/trpc/[trpc]/route.ts
|
|
88
|
+
import { app } from '@/silkweave/server'
|
|
89
|
+
|
|
90
|
+
export const { GET, POST, OPTIONS } = app.trpc({ endpoint: '/api/trpc' })
|
|
91
|
+
|
|
92
|
+
export const dynamic = 'force-dynamic'
|
|
93
|
+
export const runtime = 'nodejs'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 4. Call it from your frontend with full type safety
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// lib/trpc.ts
|
|
100
|
+
import { createTRPCClient, httpBatchLink } from '@trpc/client'
|
|
101
|
+
import type { AppRouter } from '@/silkweave/server'
|
|
102
|
+
|
|
103
|
+
export const trpc = createTRPCClient<AppRouter>({
|
|
104
|
+
links: [httpBatchLink({ url: '/api/trpc' })]
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// const { users } = await trpc.listUsers.query({ activeOnly: true })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## API
|
|
111
|
+
|
|
112
|
+
### `defineSilkweave(options)`
|
|
113
|
+
|
|
114
|
+
| Option | Type | Description |
|
|
115
|
+
|--------|------|-------------|
|
|
116
|
+
| `name` / `description` / `version` | `string` | Server identity (`SilkweaveOptions`). |
|
|
117
|
+
| `actions` | `Action[]` | The action set projected onto every surface. |
|
|
118
|
+
|
|
119
|
+
Returns a `SilkweaveApp` with `.mcp()`, `.trpc()`, and a type-only `Router`
|
|
120
|
+
phantom for client inference (`typeof app.Router`).
|
|
121
|
+
|
|
122
|
+
### `app.mcp(options)` → `{ GET, POST, DELETE, OPTIONS }`
|
|
123
|
+
|
|
124
|
+
| Option | Type | Default | Description |
|
|
125
|
+
|--------|------|---------|-------------|
|
|
126
|
+
| `basePath` | `string` | - (required) | URL prefix; **must equal the route file's directory** (e.g. `/api/mcp`). |
|
|
127
|
+
| `auth` | `AuthConfig` | - | Bearer-token / OAuth 2.1 config (see `@silkweave/auth`). |
|
|
128
|
+
| `enableJsonResponse` | `boolean` | `false` | Return a single JSON response instead of an SSE stream when possible. |
|
|
129
|
+
|
|
130
|
+
### `app.trpc(options)` → `{ GET, POST, OPTIONS }`
|
|
131
|
+
|
|
132
|
+
| Option | Type | Default | Description |
|
|
133
|
+
|--------|------|---------|-------------|
|
|
134
|
+
| `endpoint` | `string` | - (required) | tRPC prefix; **must equal the route file's directory minus `[trpc]`** (e.g. `/api/trpc`). |
|
|
135
|
+
| `auth` | `AuthConfig` | - | Bearer-token / OAuth 2.1 config. |
|
|
136
|
+
| `cors` | `boolean` | `false` | Add permissive CORS + an `OPTIONS` handler. Enable only for cross-origin clients - a same-origin Next.js frontend needs none. |
|
|
137
|
+
|
|
138
|
+
Lower-level building blocks are also exported (`buildMcpRoute`,
|
|
139
|
+
`buildTrpcRoute`, `normalizeBasePath`, `rewriteRequestPath`) for custom wiring.
|
|
140
|
+
|
|
141
|
+
## Notes & gotchas
|
|
142
|
+
|
|
143
|
+
- **`basePath` must match the file location.** There's no reliable way to read
|
|
144
|
+
the mounted URL at module load, so you pass it explicitly and it must equal
|
|
145
|
+
the route directory (`/api/mcp` ⇄ `app/api/mcp/[[...mcp]]/route.ts`).
|
|
146
|
+
- **Runtime.** Use `runtime = 'nodejs'` (the MCP transport needs Node APIs) and
|
|
147
|
+
`dynamic = 'force-dynamic'` (these handlers are never statically cached).
|
|
148
|
+
- **RFC 9728 well-known location.** Mounting under `/api/mcp` serves the
|
|
149
|
+
protected-resource metadata under that prefix. MCP discovery is driven by the
|
|
150
|
+
URLs you advertise in `auth`, so self-consistent mounting works. For strict
|
|
151
|
+
spec compliance you can additionally serve
|
|
152
|
+
`/.well-known/oauth-protected-resource` from a root route file.
|
|
153
|
+
- **Streaming actions over tRPC** register as subscriptions; consuming them
|
|
154
|
+
needs an SSE/WS tRPC link, not `httpBatchLink`. MCP streams via progress
|
|
155
|
+
notifications as usual.
|
|
156
|
+
- **One source, many surfaces.** `.mcp()` and `.trpc()` each build their own
|
|
157
|
+
internal Silkweave instance, so mounting both from the same `app` is safe.
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
ISC
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Action, Silkweave, SilkweaveOptions } from "@silkweave/core";
|
|
2
|
+
import { InferTrpcRouter } from "@silkweave/trpc";
|
|
3
|
+
import { AuthConfig } from "@silkweave/auth";
|
|
4
|
+
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
/** A Next.js App Router route handler: receives a Web `Request`, returns a `Response`. */
|
|
7
|
+
type NextRouteHandler = (request: Request) => Promise<Response>;
|
|
8
|
+
/** Options for `app.mcp()` - mounts MCP Streamable HTTP under a catch-all route. */
|
|
9
|
+
interface McpRouteOptions {
|
|
10
|
+
/**
|
|
11
|
+
* The URL prefix this route is mounted at - it MUST equal the route file's
|
|
12
|
+
* directory. For `app/api/mcp/[[...mcp]]/route.ts` this is `'/api/mcp'`.
|
|
13
|
+
*
|
|
14
|
+
* The adapter strips this prefix from incoming requests so the MCP transport
|
|
15
|
+
* (`/api/mcp`), OAuth routes (`/api/mcp/authorize`, `/api/mcp/token`, ...) and
|
|
16
|
+
* the protected-resource metadata (`/api/mcp/.well-known/...`) all resolve
|
|
17
|
+
* from this single catch-all file.
|
|
18
|
+
*/
|
|
19
|
+
basePath: string;
|
|
20
|
+
/** Optional bearer-token / OAuth 2.1 configuration (see `@silkweave/auth`). */
|
|
21
|
+
auth?: AuthConfig;
|
|
22
|
+
/** Return a single JSON response instead of an SSE stream when possible. */
|
|
23
|
+
enableJsonResponse?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Options for `app.trpc()` - mounts a tRPC endpoint under a `[trpc]` route. */
|
|
26
|
+
interface TrpcRouteOptions {
|
|
27
|
+
/**
|
|
28
|
+
* The tRPC endpoint prefix - it MUST equal the route file's directory minus
|
|
29
|
+
* the `[trpc]` segment. For `app/api/trpc/[trpc]/route.ts` this is
|
|
30
|
+
* `'/api/trpc'`. tRPC strips this prefix itself when routing procedures.
|
|
31
|
+
*/
|
|
32
|
+
endpoint: string;
|
|
33
|
+
/** Optional bearer-token / OAuth 2.1 configuration (see `@silkweave/auth`). */
|
|
34
|
+
auth?: AuthConfig;
|
|
35
|
+
/**
|
|
36
|
+
* Add permissive CORS headers + an `OPTIONS` preflight handler. Default
|
|
37
|
+
* `false` - a Next.js frontend calling its own `/api/trpc` is same-origin and
|
|
38
|
+
* needs no CORS. Enable only for cross-origin tRPC clients.
|
|
39
|
+
*/
|
|
40
|
+
cors?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** Handlers to re-export from `app/api/mcp/[[...mcp]]/route.ts`. */
|
|
43
|
+
interface McpRouteHandlers {
|
|
44
|
+
GET: NextRouteHandler;
|
|
45
|
+
POST: NextRouteHandler;
|
|
46
|
+
DELETE: NextRouteHandler;
|
|
47
|
+
OPTIONS: NextRouteHandler;
|
|
48
|
+
}
|
|
49
|
+
/** Handlers to re-export from `app/api/trpc/[trpc]/route.ts`. */
|
|
50
|
+
interface TrpcRouteHandlers {
|
|
51
|
+
GET: NextRouteHandler;
|
|
52
|
+
POST: NextRouteHandler;
|
|
53
|
+
OPTIONS: NextRouteHandler;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/lib/defineSilkweave.d.ts
|
|
57
|
+
/** Maps the actions tuple into the `Record<name, Action>` shape `Silkweave` carries. */
|
|
58
|
+
type ActionsRecord<Arr extends readonly Action[]> = { [K in Arr[number] as K['name']]: K };
|
|
59
|
+
/** The typed `Silkweave` instance equivalent to `silkweave(...).actions(arr)`. */
|
|
60
|
+
type AppServer<Arr extends readonly Action[]> = Silkweave<ActionsRecord<Arr>>;
|
|
61
|
+
/** Configuration for {@link defineSilkweave}: server identity + the action set. */
|
|
62
|
+
type DefineSilkweaveOptions<Arr extends readonly Action[]> = SilkweaveOptions & {
|
|
63
|
+
actions: Arr;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* A Silkweave app projected onto Next.js App Router route handlers. Define it
|
|
67
|
+
* once, then mount the surfaces you need from their respective route files.
|
|
68
|
+
*/
|
|
69
|
+
interface SilkweaveApp<Arr extends readonly Action[]> {
|
|
70
|
+
/** Build handlers for `app/<basePath>/[[...slug]]/route.ts` (MCP tools). */
|
|
71
|
+
mcp(options: McpRouteOptions): McpRouteHandlers;
|
|
72
|
+
/** Build handlers for `app/<endpoint>/[trpc]/route.ts` (tRPC procedures). */
|
|
73
|
+
trpc(options: TrpcRouteOptions): TrpcRouteHandlers;
|
|
74
|
+
/**
|
|
75
|
+
* Type-only phantom for the tRPC client. Use as
|
|
76
|
+
* `export type AppRouter = typeof app.Router`. Accessing the value at runtime
|
|
77
|
+
* returns `undefined` - it exists purely to carry the inferred router type.
|
|
78
|
+
*/
|
|
79
|
+
readonly Router: InferTrpcRouter<AppServer<Arr>>;
|
|
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
|
+
declare function defineSilkweave<const Arr extends readonly Action[]>(options: DefineSilkweaveOptions<Arr>): SilkweaveApp<Arr>;
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/lib/mcpRoute.d.ts
|
|
98
|
+
/**
|
|
99
|
+
* Build the MCP route handlers for a single catch-all Next.js route file.
|
|
100
|
+
*
|
|
101
|
+
* Wires the actions through `@silkweave/vercel` (stateless MCP over Web Standard
|
|
102
|
+
* Streamable HTTP) and wraps its handler with a prefix-stripping rewrite so the
|
|
103
|
+
* transport, OAuth routes and protected-resource metadata all resolve from one
|
|
104
|
+
* `app/<basePath>/[[...slug]]/route.ts` file.
|
|
105
|
+
*/
|
|
106
|
+
declare function buildMcpRoute(identity: SilkweaveOptions, actions: readonly Action[], options: McpRouteOptions): McpRouteHandlers;
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/lib/trpcRoute.d.ts
|
|
109
|
+
/**
|
|
110
|
+
* Build the tRPC route handlers for a `[trpc]` Next.js route file.
|
|
111
|
+
*
|
|
112
|
+
* Wires the actions through `@silkweave/trpc`'s fetch handler. tRPC strips the
|
|
113
|
+
* `endpoint` prefix itself, so no URL rewriting is needed. CORS is opt-in
|
|
114
|
+
* (`options.cors`) since a Next.js frontend hitting its own `/api/trpc` is
|
|
115
|
+
* same-origin.
|
|
116
|
+
*/
|
|
117
|
+
declare function buildTrpcRoute(identity: SilkweaveOptions, actions: readonly Action[], options: TrpcRouteOptions): TrpcRouteHandlers;
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/lib/stripPrefix.d.ts
|
|
120
|
+
/**
|
|
121
|
+
* Normalize a user-supplied mount path: ensure a single leading slash and no
|
|
122
|
+
* trailing slash. `'api/mcp/'` -> `'/api/mcp'`, `'/'` -> `''`.
|
|
123
|
+
*/
|
|
124
|
+
declare function normalizeBasePath(basePath: string): string;
|
|
125
|
+
/**
|
|
126
|
+
* Rewrite a Web `Request`'s URL so the inner `@silkweave/vercel` MCP handler -
|
|
127
|
+
* which matches absolute pathnames (`/mcp`, `/authorize`, `/.well-known/...`) -
|
|
128
|
+
* sees canonical paths regardless of where the Next.js route is mounted.
|
|
129
|
+
*
|
|
130
|
+
* Given `basePath = '/api/mcp'`:
|
|
131
|
+
* `/api/mcp` -> `/mcp` (the transport root)
|
|
132
|
+
* `/api/mcp/authorize` -> `/authorize`
|
|
133
|
+
* `/api/mcp/.well-known/oauth-...` -> `/.well-known/oauth-...`
|
|
134
|
+
*
|
|
135
|
+
* The method, headers, body and abort signal are preserved. A streaming body
|
|
136
|
+
* requires `duplex: 'half'` under Node's undici `fetch` implementation.
|
|
137
|
+
*/
|
|
138
|
+
declare function rewriteRequestPath(request: Request, basePath: string, fallback?: string): Request;
|
|
139
|
+
//#endregion
|
|
140
|
+
export { type DefineSilkweaveOptions, type McpRouteHandlers, type McpRouteOptions, type NextRouteHandler, type SilkweaveApp, type TrpcRouteHandlers, type TrpcRouteOptions, buildMcpRoute, buildTrpcRoute, defineSilkweave, normalizeBasePath, rewriteRequestPath };
|
|
141
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +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;;;;KC9CN,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;EDpBkB;ECsBnE,GAAA,CAAI,OAAA,EAAS,eAAA,GAAkB,gBAAA;EDnBD;ECqB9B,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,iBAAA;EDThB;;;;;EAAA,SCeR,MAAA,EAAQ,eAAA,CAAgB,SAAA,CAAU,GAAA;AAAA;ADT7C;;;;;;;;;;AAkBA;;;;AAlBA,iBC0BgB,eAAA,4BAA2C,MAAA,GAAA,CACzD,OAAA,EAAS,sBAAA,CAAuB,GAAA,IAC/B,YAAA,CAAa,GAAA;;;;;;ADjDhB;;;;;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
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { silkweave } from "@silkweave/core";
|
|
2
|
+
import { vercel } from "@silkweave/vercel";
|
|
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/vercel` 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/vercel` (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 } = vercel({
|
|
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
|
|
123
|
+
//#region src/lib/defineSilkweave.ts
|
|
124
|
+
/**
|
|
125
|
+
* Define a Silkweave app from a single set of Actions and project it onto
|
|
126
|
+
* Next.js App Router route handlers - MCP (for agents) and tRPC (for your
|
|
127
|
+
* frontend) - from one source of truth.
|
|
128
|
+
*
|
|
129
|
+
* ```ts
|
|
130
|
+
* // silkweave/server.ts
|
|
131
|
+
* export const app = defineSilkweave({
|
|
132
|
+
* name: 'my-app', description: '...', version: '1.0.0',
|
|
133
|
+
* actions: [listUsers, getUser]
|
|
134
|
+
* })
|
|
135
|
+
* export type AppRouter = typeof app.Router
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
function defineSilkweave(options) {
|
|
139
|
+
const { actions, ...identity } = options;
|
|
140
|
+
return {
|
|
141
|
+
mcp: (mcpOptions) => buildMcpRoute(identity, actions, mcpOptions),
|
|
142
|
+
trpc: (trpcOptions) => buildTrpcRoute(identity, actions, trpcOptions),
|
|
143
|
+
Router: void 0
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { buildMcpRoute, buildTrpcRoute, defineSilkweave, normalizeBasePath, rewriteRequestPath };
|
|
148
|
+
|
|
149
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/stripPrefix.ts","../src/lib/mcpRoute.ts","../src/lib/trpcRoute.ts","../src/lib/defineSilkweave.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/vercel` 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 { vercel } from '@silkweave/vercel'\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/vercel` (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 } = vercel({\n auth: options.auth,\n enableJsonResponse: options.enableJsonResponse\n })\n\n // Resolve the adapter's `_ready` promise. The handler awaits it internally, so\n // we don't need to await here - a floating start is safe and avoids forcing a\n // top-level `await` into the consumer's route module.\n void silkweave(identity).actions(actions).adapter(adapter).start()\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","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 void silkweave(identity).actions(actions).adapter(adapter).start()\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","import { Action, Silkweave, SilkweaveOptions } from '@silkweave/core'\nimport type { InferTrpcRouter } from '@silkweave/trpc'\nimport { McpRouteHandlers, McpRouteOptions, TrpcRouteHandlers, TrpcRouteOptions } from '../types.js'\nimport { buildMcpRoute } from './mcpRoute.js'\nimport { buildTrpcRoute } from './trpcRoute.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 */\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) => buildMcpRoute(identity, actions, mcpOptions),\n trpc: (trpcOptions) => buildTrpcRoute(identity, actions, trpcOptions),\n Router: undefined as unknown as InferTrpcRouter<AppServer<Arr>>\n }\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,OAAO;EAClC,MAAM,QAAQ;EACd,oBAAoB,QAAQ;EAC7B,CAAC;AAKG,WAAU,SAAS,CAAC,QAAQ,QAAQ,CAAC,QAAQ,QAAQ,CAAC,OAAO;CAElE,MAAM,SAAS,YAAwC,QAAQ,mBAAmB,SAAS,SAAS,CAAC;AAErG,QAAO;EAAE,KAAK;EAAO,MAAM;EAAO,QAAQ;EAAO,SAAS;EAAO;;;;AC3BnE,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;AAEvF,WAAU,SAAS,CAAC,QAAQ,QAAQ,CAAC,QAAQ,QAAQ,CAAC,OAAO;CAElE,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;;;;;;;;;;;;;;;;;;ACO9E,SAAgB,gBACd,SACmB;CACnB,MAAM,EAAE,SAAS,GAAG,aAAa;AAEjC,QAAO;EACL,MAAM,eAAe,cAAc,UAAU,SAAS,WAAW;EACjE,OAAO,gBAAgB,eAAe,UAAU,SAAS,YAAY;EACrE,QAAQ,KAAA;EACT"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@silkweave/nextjs",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "Silkweave Next.js App Router Adapter",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://www.silkweave.dev",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/silkweave/silkweave/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+ssh://git@github.com/silkweave/silkweave.git",
|
|
13
|
+
"directory": "packages/nextjs"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./build/index.mjs",
|
|
17
|
+
"types": "./build/index.d.mts",
|
|
18
|
+
"files": [
|
|
19
|
+
"build"
|
|
20
|
+
],
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"@silkweave/source": "./src/index.ts",
|
|
24
|
+
"types": "./build/index.d.mts",
|
|
25
|
+
"default": "./build/index.mjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@trpc/server": "^11.7.1",
|
|
30
|
+
"zod": "^3.25.0",
|
|
31
|
+
"@silkweave/auth": "2.4.0",
|
|
32
|
+
"@silkweave/core": "2.4.0",
|
|
33
|
+
"@silkweave/trpc": "2.4.0",
|
|
34
|
+
"@silkweave/vercel": "2.4.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@eslint/js": "^10.0.1",
|
|
38
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"eslint": "^10.4.0",
|
|
41
|
+
"rimraf": "^6.1.3",
|
|
42
|
+
"tsdown": "^0.21.8",
|
|
43
|
+
"tsx": "^4.21.0",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"typescript-eslint": "^8.56.1"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"clean": "rimraf build",
|
|
49
|
+
"build": "tsdown",
|
|
50
|
+
"watch": "tsdown --watch",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"lint": "eslint",
|
|
53
|
+
"check": "pnpm lint && pnpm typecheck"
|
|
54
|
+
}
|
|
55
|
+
}
|