@webjsdev/mcp 0.1.7 → 0.1.9

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.
Files changed (37) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -1
  3. package/resources/AGENTS.md +86 -72
  4. package/resources/SKILL.md +226 -0
  5. package/resources/references/auth-and-sessions.md +220 -0
  6. package/resources/references/built-ins.md +200 -0
  7. package/resources/references/client-router-and-streaming.md +204 -0
  8. package/resources/references/components.md +167 -0
  9. package/resources/references/data-and-actions.md +187 -0
  10. package/resources/references/muscle-memory-gotchas.md +170 -0
  11. package/resources/references/optimistic-ui.md +128 -0
  12. package/resources/references/routing-and-pages.md +158 -0
  13. package/resources/references/runtime.md +80 -0
  14. package/resources/references/service-worker.md +78 -0
  15. package/resources/references/styling.md +123 -0
  16. package/resources/references/testing.md +125 -0
  17. package/resources/references/typescript.md +148 -0
  18. package/src/mcp-docs.js +27 -14
  19. package/src/mcp-source.js +1 -1
  20. package/src/mcp.js +9 -47
  21. package/src/routes-report.js +90 -0
  22. package/resources/agent-docs/advanced.md +0 -1334
  23. package/resources/agent-docs/built-ins.md +0 -389
  24. package/resources/agent-docs/components-shadowing.md +0 -87
  25. package/resources/agent-docs/components.md +0 -734
  26. package/resources/agent-docs/configuration.md +0 -284
  27. package/resources/agent-docs/framework-dev.md +0 -73
  28. package/resources/agent-docs/lit-muscle-memory-gotchas.md +0 -470
  29. package/resources/agent-docs/metadata.md +0 -334
  30. package/resources/agent-docs/orm.md +0 -198
  31. package/resources/agent-docs/recipes.md +0 -664
  32. package/resources/agent-docs/runtime.md +0 -65
  33. package/resources/agent-docs/service-worker.md +0 -104
  34. package/resources/agent-docs/styling.md +0 -279
  35. package/resources/agent-docs/testing.md +0 -479
  36. package/resources/agent-docs/types-and-mutations.md +0 -171
  37. package/resources/agent-docs/typescript.md +0 -402
@@ -1,402 +0,0 @@
1
- # TypeScript without a build step + full-stack type safety
2
-
3
- Files ending in `.ts` / `.mts` are supported everywhere `.js` / `.mjs`
4
- are. Same routing conventions, same server-action behaviour. No `tsc`
5
- run is part of the user-visible workflow, no separate build step:
6
-
7
- - **Editor** (VS Code) runs the TypeScript language server continuously. Red-squiggle on wrong types, including non-erasable syntax (see below).
8
- - **CI** (optional) runs `tsc --noEmit` against `tsconfig.json`. Type-check only. Also catches non-erasable syntax via `erasableSyntaxOnly`.
9
- - **Dev + prod server** (runtime, both directions): the runtime's TypeScript type-stripping handles server-side `.ts` imports automatically (on Node, `process.features.typescript === 'strip'`; Bun runs `.ts` natively). Browser-bound `.ts` requests go through the pluggable stripper in `packages/server/src/ts-strip.js` (#508) on the dev server, which performs whitespace replacement: every (line, column) in the source maps to the same position in the stripped output, so no sourcemap needs to be shipped and stack traces are byte-exact. The transform is cached by mtime (~microseconds per cache hit). Implementation backing: on **Node 24+** it is the built-in `module.stripTypeScriptTypes` (which itself wraps the [`amaro`](https://github.com/nodejs/amaro) package, SWC's WASM TypeScript transform in position-preserving strip-only mode); on **Bun** (no such built-in) it is `amaro` loaded directly, an `optionalDependency` of `@webjsdev/server`, producing byte-identical output. `WEBJS_TS_STRIPPER=builtin|amaro` forces a backend. Edge runtimes without a filesystem are a separate, later target.
10
-
11
- ## TypeScript feature support: erasable only
12
-
13
- The framework strips with Node 24+'s built-in `module.stripTypeScriptTypes`
14
- (or `amaro` on Bun, the same engine), which only supports **erasable
15
- TypeScript**: type annotations,
16
- `interface`, `type`, `declare`, generics, `import type`, `as` casts,
17
- and `satisfies`. Non-erasable syntax is rejected.
18
-
19
- Use the **erasable equivalents** instead:
20
-
21
- ```ts
22
- // Not allowed (rejected at compile + runtime)
23
- enum Color { Red, Green, Blue }
24
- class Foo { constructor(public x: number) {} }
25
- namespace Util { export const helper = ...; }
26
- import = require('something');
27
- @legacyDecorator class C {}
28
-
29
- // Allowed (canonical erasable forms)
30
- const Color = { Red: 'Red', Green: 'Green', Blue: 'Blue' } as const;
31
- type Color = typeof Color[keyof typeof Color];
32
-
33
- class Foo {
34
- x: number;
35
- constructor(x: number) { this.x = x; }
36
- }
37
-
38
- const Util = { helper: ... };
39
-
40
- import { thing } from './thing.ts';
41
- ```
42
-
43
- Enforce this at edit time by setting `erasableSyntaxOnly: true` in
44
- `tsconfig.json`. The TypeScript compiler then flags any non-erasable
45
- syntax as a red squiggle in the editor and `tsc --noEmit` error in CI.
46
-
47
- The `erasable-typescript-only` convention check verifies the flag is
48
- set. Run `webjs check` to confirm.
49
-
50
- ### Fallback for third-party `.ts` dependencies
51
-
52
- If a third-party package ships `.ts` source using non-erasable
53
- syntax (rare; most npm packages publish compiled `.js`), the dev
54
- server fails at strip time and returns a 500 naming the file and
55
- pointing at the `no-non-erasable-typescript` lint rule. webjs is
56
- buildless end-to-end and has no bundler fallback. Your own code
57
- never hits this as long as `erasableSyntaxOnly` is set.
58
-
59
- If you manually turn `erasableSyntaxOnly` off and write non-erasable
60
- syntax in your own code, the dev server fails the same way. The
61
- `erasable-typescript-only` convention check warns when the flag is
62
- off so you catch the configuration drift before runtime.
63
-
64
- ## Import convention
65
-
66
- Use explicit `.ts` extensions in imports. The runtime's type-stripping
67
- and the dev server's HTTP handler both key on the
68
- file URL ending in `.ts` / `.mts`. For mixed codebases, `.js` imports
69
- that point at a `.ts` sibling also resolve in the dev server. Still
70
- prefer explicit `.ts`.
71
-
72
- ```ts
73
- // modules/posts/queries/list-posts.server.ts
74
- import { db } from '../../../db/connection.server.js'; // .js specifier resolves the .ts sibling
75
- import { formatPost } from '../utils/slugify.ts'; // TS file
76
- ```
77
-
78
- ## Minimum viable `tsconfig.json`
79
-
80
- ```json
81
- {
82
- "compilerOptions": {
83
- "target": "ES2022",
84
- "module": "NodeNext",
85
- "moduleResolution": "NodeNext",
86
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
87
- "strict": true,
88
- "noEmit": true,
89
- "checkJs": true,
90
- "allowJs": true,
91
- "allowImportingTsExtensions": true,
92
- "skipLibCheck": true,
93
- "erasableSyntaxOnly": true
94
- }
95
- }
96
- ```
97
-
98
- The `erasableSyntaxOnly: true` line is the non-negotiable one. It
99
- aligns the TypeScript compiler's accepted syntax with what Node's
100
- strip-types accepts, so violations surface as editor diagnostics
101
- instead of a runtime 500.
102
-
103
- ## Full-stack type safety
104
-
105
- ### Server actions: type-safe automatically
106
-
107
- Calling a server action from a client component resolves at type-check
108
- time to the action's real source file. The dev server's runtime stub
109
- replacement is invisible to the type checker.
110
-
111
- ```ts
112
- // modules/posts/actions/create-post.server.ts
113
- export async function createPost(
114
- input: { title: string; body: string },
115
- ): Promise<ActionResult<PostFormatted>> { /* … */ }
116
-
117
- // modules/posts/components/new-post.ts
118
- import { createPost } from '../actions/create-post.server.ts';
119
- const r = await createPost({ title, body });
120
- // ^ Promise<ActionResult<PostFormatted>>
121
- if (r.success) r.data.title; // ← PostFormatted.title: string
122
- ```
123
-
124
- **Runtime matches types** because the RPC wire uses webjs's built-in
125
- ESM serializer: `Date` → `Date`, `Map` → `Map`, `BigInt` → `BigInt`.
126
- Supported: `Date`, `Map`, `Set`, `BigInt`, `Error`, `undefined`,
127
- `NaN`/`Infinity`/`-0`, `TypedArray`, `ArrayBuffer`, `DataView`, `Blob`,
128
- `File`, `FormData`, `Symbol.for(...)`, reference cycles. Class
129
- instances come through as plain objects, with prototypes lost and methods
130
- gone (matches React Server Actions).
131
-
132
- #### Catching a non-serializable signature at compile time (`SerializableActionFn`)
133
-
134
- The class-instance / function caveat above is a silent runtime surprise: a
135
- method-valued field or a function argument typechecks fine but vanishes on the
136
- wire. The opt-in `SerializableActionFn` annotation turns it into a compile-time
137
- error. webjs actions stay plain `export async function`s (the framework rewrites
138
- the client import to an RPC stub at runtime, it does not wrap the authored
139
- function), so the guard is an OPTIONAL type annotation the author applies when
140
- they want it:
141
-
142
- ```ts
143
- import type { SerializableActionFn } from '@webjsdev/core';
144
-
145
- export const getUser: SerializableActionFn<(id: number) => Promise<User>> =
146
- async (id) => db.user.find(id);
147
- // ^ if User carries a method (or any arg is a function), this is a type error
148
- // pointing at the offending member, instead of a value that silently loses
149
- // it on the wire.
150
- ```
151
-
152
- `Serializable<T>` (also exported) maps a fully serializable `T` to itself and a
153
- non-serializable position (a function / method) to a branded `NonSerializable`
154
- marker; `SerializableArgs` / `SerializableResult` apply it across an action's
155
- parameters and (promise-unwrapped) return. All types-only, erased at runtime.
156
-
157
- ### API routes: opt in via content negotiation
158
-
159
- `route.ts` handlers use standard JSON by default so external consumers
160
- keep working. Opt into rich types for your own UI code:
161
-
162
- ```ts
163
- // app/api/posts/route.ts: server
164
- import { json } from '@webjsdev/server';
165
- import { listPosts } from '../../../modules/posts/queries/list-posts.server.ts';
166
-
167
- export async function GET() {
168
- return json(await listPosts()); // content-negotiates automatically
169
- }
170
- ```
171
-
172
- ```ts
173
- // caller: client
174
- import { richFetch } from '@webjsdev/core';
175
- const posts = await richFetch<Post[]>('/api/posts');
176
- // posts[0].createdAt is a Date here.
177
- ```
178
-
179
- The `json()` helper reads the in-flight Request via AsyncLocalStorage:
180
- - `Accept: application/vnd.webjs+json` → encoded with the webjs serializer, served with `Content-Type: application/vnd.webjs+json` and `Vary: Accept`.
181
- - Otherwise → plain JSON.
182
-
183
- Request bodies parse with `readBody(req)` from `@webjsdev/server`.
184
-
185
- ### Page metadata: the `Metadata` type
186
-
187
- A page or layout exports `metadata` (static) or `generateMetadata(ctx)`
188
- (request-scoped). Annotate the return with the exported `Metadata` type so
189
- a misspelled field or a wrong-typed value is a compile-time error, the
190
- same ergonomics as Next.js's `import type { Metadata } from 'next'`.
191
-
192
- ```ts
193
- import type { Metadata, MetadataContext } from '@webjsdev/core';
194
-
195
- export const metadata: Metadata = {
196
- title: 'Blog',
197
- description: 'Latest posts',
198
- openGraph: { type: 'website', image: '/og.png' },
199
- twitter: { card: 'summary_large_image' },
200
- };
201
-
202
- export async function generateMetadata(ctx: MetadataContext): Promise<Metadata> {
203
- return { title: `Post: ${ctx.params.slug}`, metadataBase: new URL(ctx.url).origin };
204
- }
205
- ```
206
-
207
- `Metadata` covers every field the SSR pipeline reads (see
208
- `agent-docs/metadata.md`); each field is optional, and string-or-object
209
- fields (`title`, `viewport`, `robots`, `appleWebApp`, `icons`) are unions.
210
- It is a pure type in `packages/core/src/metadata.d.ts`, so it is erased at
211
- runtime with zero build cost. `MetadataContext` types the
212
- `generateMetadata` argument (`{ params, searchParams, url, actionData }`,
213
- where `actionData` is set only on a failed-page-action re-render).
214
-
215
- ### Typed page / layout / route-handler props (`PageProps`, `LayoutProps`, `RouteHandlerContext`)
216
-
217
- A page default-export receives `{ params, searchParams, url, actionData }`; a
218
- layout receives the same plus `children`; a `route.{js,ts}` handler receives
219
- `(request, { params })`. Type each with the exported helpers so a typo in a
220
- param name or a wrong-typed field is a compile-time error.
221
-
222
- ```ts
223
- import type { PageProps, LayoutProps, RouteHandlerContext } from '@webjsdev/core';
224
-
225
- // A static route: `params` is `Record<string, string>`.
226
- export default function About({ searchParams }: PageProps) { /* ... */ }
227
-
228
- // A dynamic route: pass the route literal to narrow `params`.
229
- export default function Post({ params }: PageProps<'/blog/[slug]'>) {
230
- const slug = params.slug; // typed `string`
231
- /* ... */
232
- }
233
-
234
- // A layout adds `children: TemplateResult`.
235
- export default function RootLayout({ children }: LayoutProps) { /* ... */ }
236
-
237
- // A route handler's 2nd arg.
238
- export async function GET(req: Request, ctx: RouteHandlerContext) {
239
- return Response.json({ id: ctx.params.id });
240
- }
241
- ```
242
-
243
- `PageProps<R>` / `LayoutProps<R>` / `RouteHandlerContext<R>` take an optional
244
- route literal `R`. With no `R` (or in an app that has not generated route
245
- types), `params` is `Record<string, string>`, the runtime default. With `R`
246
- set to a generated dynamic route, `params` narrows to its exact shape
247
- (`{ slug: string }`, `{ rest: string[] }`, `{ slug?: string[] }`). The shapes
248
- mirror what `packages/server/src/ssr.js` and `packages/server/src/api.js`
249
- actually pass, NOT Next.js's superset. Pure types in
250
- `packages/core/src/routes.d.ts`, erased at runtime with zero build cost.
251
-
252
- ### The generated route union (`webjs types`) types `navigate()` and catches bad hrefs
253
-
254
- Run `webjs types` to generate `.webjs/routes.d.ts`, an opt-in overlay that
255
- augments `@webjsdev/core` with one key per route in `app/`. It narrows two
256
- things at tsserver time:
257
-
258
- - The `Route` href type: `navigate('/blog/anything')` is accepted,
259
- `navigate('/nonexistent')` is a type error. (Until you generate the types,
260
- `Route` is `string`, so `navigate()` is unconstrained, non-breaking for
261
- JSDoc apps and un-generated apps alike.)
262
- - Per-route `params`: `PageProps<'/blog/[slug]'>['params']` becomes
263
- `{ slug: string }`, derived from the generated `RouteParamMap`.
264
-
265
- ```sh
266
- webjs types # writes .webjs/routes.d.ts (count of routes printed)
267
- ```
268
-
269
- `webjs dev` also emits it automatically at startup and re-emits after each
270
- route rebuild, so an editor always has fresh route types. The file is
271
- gitignored (regenerated per machine, like Next's `.next/types`); the scaffold
272
- `tsconfig.json` lists `.webjs/routes.d.ts` in `include` so tsserver picks it
273
- up. To opt in for an existing app, run `webjs types` once and ensure your
274
- `tsconfig.json` `include` lists `.webjs/routes.d.ts`.
275
-
276
- This is webjs's no-build equivalent of Next 15's `typedRoutes`, achieved via
277
- interface declaration-merging (`declare module '@webjsdev/core'`) rather than a
278
- bundler. The mechanism is `generateRouteTypes(appDir)` in
279
- `packages/server/src/route-types.js`, which reuses the one route enumerator
280
- (`buildRouteTable`). Output is deterministic (sorted keys), so re-running
281
- yields a byte-identical file.
282
-
283
- ### The `webjs` package.json config block: `WebjsConfig` + JSON Schema
284
-
285
- The `webjs` object in `package.json` (the `elide` / `headers` / `redirects` /
286
- `trailingSlash` / `csp` knobs plus the ingress body-size and timeout caps) has
287
- two typed references, so a typo'd key is diagnosed instead of silently dropped:
288
-
289
- - **A JSON Schema**, `packages/server/webjs-config.schema.json` (shipped in the
290
- `@webjsdev/server` package). The scaffold's `.vscode/settings.json` associates
291
- it with the `webjs` property of `package.json`, so VS Code flags an unknown
292
- key natively while you edit the JSON. `additionalProperties: false` on the
293
- block is what turns a typo into an editor warning.
294
- - **The `WebjsConfig` type**, exported from `@webjsdev/core`, a typed reference
295
- for an agent or human authoring the block (with `WebjsHeaderRule`,
296
- `WebjsRedirectRule`, `WebjsCspConfig`, `WebjsTrailingSlash` for the nested
297
- shapes).
298
-
299
- ```ts
300
- import type { WebjsConfig } from '@webjsdev/core';
301
-
302
- const config: WebjsConfig = {
303
- trailingSlash: 'never',
304
- csp: true,
305
- redirects: [{ source: '/old', destination: '/new' }],
306
- };
307
- ```
308
-
309
- The schema and the type mirror what the server readers actually consume
310
- (`readElideEnabled`, `compileHeaderRules`, `compileRedirectRules` /
311
- `readTrailingSlashPolicy`, `readCspConfig`, `readBodyLimits` /
312
- `computeServerTimeouts`). Adding a `webjs.*` key means updating the schema, the
313
- type, AND the reader in lockstep, the one procedure documented in
314
- `packages/server/AGENTS.md`. A drift test
315
- (`packages/server/test/config/webjs-config-schema.test.js`) fails if the schema
316
- and the reader key set diverge.
317
-
318
- ### Both runtime packages ship a type overlay
319
-
320
- Both `@webjsdev/core` AND `@webjsdev/server` ship a hand-authored `.d.ts`
321
- overlay plus a `types` export condition, so a `strict` + `nodenext` app
322
- resolves real types for either import with no TS7016 ("could not find a
323
- declaration file") error. The server overlay (`packages/server/index.d.ts`,
324
- with `src/check.d.ts` and `src/testing.d.ts` for the `./check` / `./testing`
325
- subpaths) types the full public surface (`createRequestHandler`, `startServer`,
326
- `cors`, `cache`, `createAuth`, `rateLimit`, `sitemap`, `Session`, `json`,
327
- `readBody`, the `revalidate*` family, the context helpers, the cache stores, the
328
- auth providers, the test harness, and the convention validator), reusing the
329
- core prop / metadata types rather than redefining them. The runtime stays plain
330
- `.js` + JSDoc; the overlay is types-only with zero runtime cost. A drift test
331
- keeps `index.d.ts` in lockstep with `index.js`'s runtime exports.
332
-
333
- ### Typing the `auth()` session user
334
-
335
- `auth()` resolves `{ user }`, and by default `user` is `Record<string,
336
- unknown>`, so reading a custom field your `session`/`jwt` callbacks set
337
- (such as `session.user.id`) needs a cast and a typo is not caught. Two
338
- opt-in ways make `user` typed, both types-only.
339
-
340
- Augment the `AuthUser` interface (NextAuth/Auth.js style) to type every
341
- `auth()` call across the app:
342
-
343
- ```ts
344
- declare module '@webjsdev/server' {
345
- interface AuthUser {
346
- id: string;
347
- role: 'admin' | 'member';
348
- }
349
- }
350
- ```
351
-
352
- Or parameterise the factory to type one instance without a global
353
- augmentation. `createAuth<AppUser>(...)` returns an `auth()` whose `user`
354
- is `AppUser`:
355
-
356
- ```ts
357
- const { auth } = createAuth<AppUser>({ secret, providers });
358
- const session = await auth();
359
- session?.user.role; // typed, no cast
360
- ```
361
-
362
- Un-augmented and un-parameterised, `AuthUser` is empty and resolves back
363
- to `Record<string, unknown>`, so pre-existing untyped code keeps
364
- compiling. The declared fields should mirror what the callbacks write
365
- onto `session.user`.
366
-
367
- ### TypeScript is not required
368
-
369
- JS + JSDoc gets the same call-site type safety. The TypeScript language
370
- server reads `@typedef` / `@param` / `@returns` identically to `.ts`
371
- syntax. Add `"checkJs": true` to enforce types in editor + CI.
372
-
373
- ## Editor plugin: `@webjsdev/intellisense`
374
-
375
- **Editor-only. Not required for the framework to run.** The runtime
376
- has no dependency on it.
377
-
378
- A single plugin with its OWN in-template intelligence (no Lit dependency; the `webjs` VSCode extension bundles it). List one entry:
379
-
380
- ```jsonc
381
- "plugins": [
382
- { "name": "@webjsdev/intellisense" }
383
- ]
384
- ```
385
-
386
- Gives you, inside `` html`` `` templates:
387
- - **Go-to-definition** from `<my-counter>` to the class registered via `MyCounter.register('my-counter')`, from an attribute / property / event name to its class member, and from a CSS class in `class="…"` to its `` css`` `` rule.
388
- - **Completions**: reachable custom-element tag names after `<`, and binding-aware attribute completions: `.` offers property names, plain / `?` offer hyphenated attribute names (`maxLength` → `max-length`), `@event` is permissive.
389
- - **Diagnostics**: attribute / property value type-checks (`<my-counter .count=${expr}>` assignability-checks `typeof expr` against the property's type from the `WebComponent({ count: Number })` factory shape, and `@click=${fn}` must be callable), unquoted `@`/`.`/`?` bindings (invariant 4), and expressionless `.prop` bindings.
390
- - **Hover**: a tag shows its component class; an attribute / property / event shows its declared type.
391
-
392
- Every feature is gated on import-graph reachability: a tag is recognized only if the file registering it is reachable from the file you're editing. There is deliberately no blanket "unknown tag / attribute" diagnostic (webjs has no element type map, so it would false-positive on third-party custom elements).
393
-
394
- ### The `webjs` VSCode extension (recommended over a manual tsconfig plugin)
395
-
396
- The `webjs` extension (`packages/editors/vscode`, on the VS Marketplace and Open VSX) is the all-in-one editor setup. It bundles the tsserver plugin and auto-registers it via `contributes.typescriptServerPlugins`, so you get the intelligence above **without editing `tsconfig.json`**, plus:
397
-
398
- - `` html` `` / `` css` `` / `` svg` `` template highlighting via original TextMate injection grammars (no separate Lit / lit-html extension needed).
399
- - Snippets for the common recipes (`wjpage`, `wjcomponent`, `wjaction`, and more).
400
- - Commands (`webjs: Run check`, `webjs: Create a new app`, `webjs: Open documentation`).
401
-
402
- It works in VSCode and its forks (Cursor, Antigravity, Windsurf, VSCodium), which pull from Open VSX. The bundled plugin is standalone (no Lit dependency), so it is the whole webjs language service. Install via the Extensions view (search "webjs"), or for editors without a UI, `code --install-extension webjs.vsix`. **Neovim** has its own plugin, `webjs.nvim` (`packages/editors/nvim`, install `webjsdev/webjs.nvim` via lazy.nvim / packer): treesitter `html` / `css` / `svg` template highlighting plus the same tsserver plugin wired through `ts_ls`. JetBrains uses the manual `tsconfig.json` `plugins` entry above.