@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.
- package/README.md +1 -1
- package/package.json +4 -1
- package/resources/AGENTS.md +86 -72
- package/resources/SKILL.md +226 -0
- package/resources/references/auth-and-sessions.md +220 -0
- package/resources/references/built-ins.md +200 -0
- package/resources/references/client-router-and-streaming.md +204 -0
- package/resources/references/components.md +167 -0
- package/resources/references/data-and-actions.md +187 -0
- package/resources/references/muscle-memory-gotchas.md +170 -0
- package/resources/references/optimistic-ui.md +128 -0
- package/resources/references/routing-and-pages.md +158 -0
- package/resources/references/runtime.md +80 -0
- package/resources/references/service-worker.md +78 -0
- package/resources/references/styling.md +123 -0
- package/resources/references/testing.md +125 -0
- package/resources/references/typescript.md +148 -0
- package/src/mcp-docs.js +27 -14
- package/src/mcp-source.js +1 -1
- package/src/mcp.js +9 -47
- package/src/routes-report.js +90 -0
- package/resources/agent-docs/advanced.md +0 -1334
- package/resources/agent-docs/built-ins.md +0 -389
- package/resources/agent-docs/components-shadowing.md +0 -87
- package/resources/agent-docs/components.md +0 -734
- package/resources/agent-docs/configuration.md +0 -284
- package/resources/agent-docs/framework-dev.md +0 -73
- package/resources/agent-docs/lit-muscle-memory-gotchas.md +0 -470
- package/resources/agent-docs/metadata.md +0 -334
- package/resources/agent-docs/orm.md +0 -198
- package/resources/agent-docs/recipes.md +0 -664
- package/resources/agent-docs/runtime.md +0 -65
- package/resources/agent-docs/service-worker.md +0 -104
- package/resources/agent-docs/styling.md +0 -279
- package/resources/agent-docs/testing.md +0 -479
- package/resources/agent-docs/types-and-mutations.md +0 -171
- 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.
|