@webjsdev/mcp 0.1.15 → 0.1.16
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Model Context Protocol server for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Gives AI coding agents live app introspection over routes, actions, components, and convention checks, plus a knowledge layer of docs, recipes, and framework source.",
|
|
6
6
|
"bin": {
|
package/resources/AGENTS.md
CHANGED
|
@@ -415,8 +415,6 @@ Derive the type at every boundary: a DB row from the schema (`typeof todos.$infe
|
|
|
415
415
|
|
|
416
416
|
## Styling: Tailwind-first
|
|
417
417
|
|
|
418
|
-
`webjs doctor` carries the dark-theme convention as `DARK_THEME_UNREACHABLE` (a warning by default; the scaffold gates it `error` in its `package.json` because the scaffold AGENTS.md mandates the tokens, #1628): an app that defines colour tokens (`--background` / `--foreground` in `app/layout.{js,ts}`, `public/*.css` or `styles/**.css`) should make the dark half reachable with `light-dark()` tokens under `color-scheme: light dark`, an `@media (prefers-color-scheme: dark)` rule, or a root-layout head script that applies the saved theme; otherwise the stylesheet's `.dark` block is dead CSS and the OS dark setting is ignored. Silent when no tokens are defined. It is a design convention, not source correctness, so it is not a `webjs check` rule.
|
|
419
|
-
|
|
420
418
|
**Tailwind is the strong default for pages AND light-DOM components.** The lit reflex to scope CSS in a shadow root with `static styles` is the habit to resist in light DOM. When a class bundle repeats, extract it into a helper returning an `` html`...` `` fragment (SSR-time), NOT a CSS class (no `@apply`); it lives in `modules/<feature>/utils/ui/` when one feature consumes it and `lib/utils/ui.ts` when the whole app does. Choose between a fragment and a display-only component on STRUCTURE, not bytes: a component is a tag in the DOM, so reach for the fragment where that extra wrapper cannot exist at all (a `<table>` child, which the parser foster-parents out per the HTML spec, and string output such as a `<webjs-stream>` payload) or where it lands wrong (a `<ul>` / `<dl>` / `<select>` child, which breaks `ul > li` and `select > option`, and a grid or flex child, which makes the wrapper the item), and prefer the component everywhere else. On cost they tie under a page (inert page, elided component) and both ship under an island, where the component additionally carries an element class, its registration, a per-instance upgrade, and the display-only components it renders. Reserve raw CSS for what utilities cannot express (design tokens / `@theme`, `@property` + `@keyframes`, scrollbar, `prefers-reduced-motion`, complex `color-mix()` / gradients); in light DOM the tag-prefix invariant (#7) still holds, and shadow-DOM components legitimately use `static styles = css\`\``. **Pin a header with `position: fixed`, never `position: sticky`** (a sticky header flickers on iOS WebKit during a client-router nav, #610, because the preserved header plus the scroll-to-top trips a WebKit sticky-repaint bug that the GPU-promotion hacks do NOT fix, so reserve the header height on the content with a `--header-height` offset). See `references/styling.md`.
|
|
421
419
|
|
|
422
420
|
---
|
package/resources/SKILL.md
CHANGED
|
@@ -104,12 +104,12 @@ Common bundles:
|
|
|
104
104
|
1. **Classify the change.** Route contract, data model, server mutation, auth, or only UI?
|
|
105
105
|
2. **Start from the server.** Add the page/route and its server action or query before wiring interactive UI. A page render or a `<form>` POST should already return correct HTML before any component hydrates.
|
|
106
106
|
3. **Put code in the narrowest owner.** Route-local first (`modules/<feature>/`), promote to `lib/` or `components/` only when reuse is real.
|
|
107
|
-
4. **Keep server-only code behind `.server.ts
|
|
107
|
+
4. **Keep server-only code behind `.server.ts`.** The DB driver, secrets, and `node:*` never belong in a page, layout, or component.
|
|
108
108
|
5. **Add interactivity per behaviour.** Reach for a component (and a signal or `@event`) only where the UI is genuinely interactive. A display-only component is elided from the browser. Then wrap the interactive part and STOP: the static markup around it stays in the page, where it costs nothing.
|
|
109
109
|
6. **Validate input at the boundary.** Declare `export const validate` on an action; the RPC and `route()` boundaries run it.
|
|
110
110
|
7. **Default mutations to optimistic UI** where the client can predict the result (`optimistic()` from `@webjsdev/core`).
|
|
111
|
-
8. **Type every boundary from its source, never `unknown` or `any`.** The row type comes from the schema (`typeof todos.$inferSelect`), the action's input from a named `interface` and its result from `ActionResult<T>`, the routing files from `PageProps` / `LayoutProps` / `RouteHandlerContext`. `unknown` belongs on a payload nothing has vouched for yet that the next line narrows, and on a parameter of your own helper that forwards into an `html` template hole. Everywhere else, including a layout's `children`, it is a missing type.
|
|
112
|
-
9. **Test the narrowest meaningful layer**, and
|
|
111
|
+
8. **Type every boundary from its source, never `unknown` or `any`.** The row type comes from the schema (`typeof todos.$inferSelect`), the action's input from a named `interface` and its result from `ActionResult<T>`, the routing files from `PageProps` / `LayoutProps` / `RouteHandlerContext`. `unknown` belongs on a payload nothing has vouched for yet that the next line narrows, and on a parameter of your own helper that forwards into an `html` template hole. Everywhere else, including a layout's `children`, it is a missing type. See `references/typescript.md`.
|
|
112
|
+
9. **Test the narrowest meaningful layer**, and render the app in a real browser for any UI change (static checks do not catch a collapsed layout).
|
|
113
113
|
|
|
114
114
|
## Project Layout
|
|
115
115
|
|
|
@@ -137,7 +137,7 @@ App-internal imports use the `#` root alias (`import { db } from '#db/connection
|
|
|
137
137
|
## Core WebJs Rules (invariants)
|
|
138
138
|
|
|
139
139
|
1. Server-only code lives in `.server.ts`, `route.ts`, or `middleware.ts`. Never in a page, layout, or component (it crashes the browser at module load).
|
|
140
|
-
2. `'use server'` exports are `async` functions returning serializer-safe values
|
|
140
|
+
2. `'use server'` exports are `async` functions returning serializer-safe values. Files without `'use server'` are server-only utilities.
|
|
141
141
|
3. Custom element tag names contain a hyphen. Pass the tag to `Class.register('tag-name')`.
|
|
142
142
|
4. Event (`@`), property (`.`), and boolean (`?`) holes in `html` are UNQUOTED: `@click=${fn}`, never `@click="${fn}"`.
|
|
143
143
|
5. Signals are the default state primitive. Import `signal` / `computed` from `@webjsdev/core`, read via `signal.get()` inside `render()`. The base-class factory `WebComponent({ ... })` is only for values riding an HTML attribute or arriving via SSR hydration.
|
|
@@ -147,7 +147,7 @@ App-internal imports use the `#` root alias (`import { db } from '#db/connection
|
|
|
147
147
|
9. No backtick characters inside an `html\`...\`` body, even in comments (it closes the literal and 500s).
|
|
148
148
|
10. TypeScript must be erasable (`erasableSyntaxOnly: true`): no `enum`, no value `namespace`, no constructor parameter properties, no legacy decorators.
|
|
149
149
|
11. Reactive properties are declared ONLY through the base-class factory `extends WebComponent({ count: Number })`. Never a `static properties` block, never a class-field initializer (it clobbers the reactive accessor).
|
|
150
|
-
12. A form that writes binds its action: `<form action=${importedAction}>`, or a per-button `<button formaction=${importedAction}>`. A bound submitter is SELF-SUFFICIENT (#1307): the renderer puts `formmethod="post"` and `formenctype` on the button itself, so it needs no bound form around it and works inside any form or none. Quoted bindings, non-submit controls, `<input type="submit">` (the identity needs its `value`, which is also its label, so use a `<button>`), submitter `name` / `value` / `form` / static `formaction` attributes
|
|
150
|
+
12. A form that writes binds its action: `<form action=${importedAction}>`, or a per-button `<button formaction=${importedAction}>`. A bound submitter is SELF-SUFFICIENT (#1307): the renderer puts `formmethod="post"` and `formenctype` on the button itself, so it needs no bound form around it and works inside any form or none. Quoted bindings, non-submit controls, `<input type="submit">` (the identity needs its `value`, which is also its label, so use a `<button>`), submitter `name` / `value` / `form` / static `formaction` attributes, a `.prop` spelling of any of those, `action=${fn}` off a `<form>`, a bound form with `method="get"`, a BOUND submitter's own non-post `formmethod` or unparseable `formenctype`, and a non-action function all throw. A PLAIN button's own `formmethod` / `formenctype` is a legal native override and is left alone. A page has no `action` export, so a bare `<form method="post">` is a 405.
|
|
151
151
|
|
|
152
152
|
## Export Map
|
|
153
153
|
|
|
@@ -272,12 +272,18 @@ Success is a 303 (PRG); failure re-renders the page at 422 with the result on `a
|
|
|
272
272
|
|
|
273
273
|
## Common Mistakes To Avoid
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
- Treating a page or layout like a React component and expecting its markup to hydrate. It runs server-only; put interactivity in a component.
|
|
277
276
|
- Promoting a whole page section to a component so that one control inside it can be interactive. The island should wrap the control and the state it reads. An oversized island ships its own JS AND un-elides every display-only component inside it, so the cost is not linear in what you moved.
|
|
277
|
+
- Importing a `.server.ts` utility (no `'use server'`) directly into a shipping component. Its browser stub throws at load; reach it through a `'use server'` action.
|
|
278
|
+
- Using a `static properties` block or a class-field initializer for reactive props instead of the `WebComponent({ ... })` factory.
|
|
279
|
+
- Quoting an event / property / boolean hole (`@click="${fn}"`).
|
|
278
280
|
- Writing `fetch()` to call your own server instead of importing the action.
|
|
281
|
+
- Writing a bare `<form method="post">` and expecting a page `action` export to catch it. There is no such export; bind the action with `action=${fn}` or the submission is a 405.
|
|
282
|
+
- Putting a submitter's `formaction=${fn}` on anything that is not a submit control, or on a button carrying its own `name` / `value`. The identity IS the button's name/value pair, so both halves are spoken for.
|
|
283
|
+
- Writing `formmethod="get"` or `formenctype="text/plain"` on a button that BINDS an action. Neither can carry that action's body, so the pair contradicts itself and throws. On a button that binds nothing it is a legal native override and is honoured.
|
|
279
284
|
- Binding an action whose file declares `export const method = 'GET'`. Form-bound actions strictly require POST (default). Binding a GET action to a form is a 405 at runtime and a `webjs check` error (`form-action-not-a-get-action`).
|
|
280
285
|
- Leaving read-only RPC server query actions as default `POST`. Always export `export const method = 'GET'` for RPC data queries so arguments ride URL params, ETags/304 caching work, and CSRF is safely bypassed.
|
|
286
|
+
- Writing `method="get"` on a bound `<form action=${fn}>`. WebJs supplies `method="post"` and `formenctype` automatically, and a bound form declaring `method="get"` is REFUSED at render (a thrown error, not a warning), because a GET sends no body for the action to read.
|
|
281
287
|
- Throwing `redirect()` / `notFound()` inside a `route.ts` handler (uncaught 500). Return a `Response` there.
|
|
282
288
|
- A placeholder first paint that fetches in `connectedCallback`. SSR does not call `connectedCallback`; put first-paint data in the constructor (server-known inputs) or use `async render()`.
|
|
283
289
|
- A browser global (`window`, `document`, `localStorage`) in the constructor or `render()`. It throws at SSR; do browser-only work in `connectedCallback`.
|
package/resources/corpus.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@webjsdev/mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"sha": "
|
|
5
|
-
"copiedAt": "2026-10-
|
|
3
|
+
"version": "0.1.16",
|
|
4
|
+
"sha": "8486559f7d327bfa083547955e513cf28f118902",
|
|
5
|
+
"copiedAt": "2026-10-08T16:42:45.301Z"
|
|
6
6
|
}
|
|
@@ -298,8 +298,6 @@ Two guarantees worth knowing. A result that could not check (a network or toolch
|
|
|
298
298
|
|
|
299
299
|
`APP_ICON` warns while the favicon is still the scaffold's placeholder `app/icon.svg` (marked `data-webjs-placeholder`). Replace it with the app's own icon (`references/routing-and-pages.md`, "App icon and manifest").
|
|
300
300
|
|
|
301
|
-
`DARK_THEME_UNREACHABLE` warns when the app defines colour tokens with a light value only and nothing applies a dark half: no `light-dark()`, no `@media (prefers-color-scheme: dark)` rule, no theme script in the root layout. The scaffold gates it `error` in its `package.json` because its AGENTS.md mandates `light-dark()` tokens; the fix is the token block in `references/styling.md`.
|
|
302
|
-
|
|
303
301
|
### Dependency audit allowlist
|
|
304
302
|
|
|
305
303
|
`webjs audit` runs `npm audit` or `bun audit` (by the nearest lockfile, so a workspace member uses the root's) and fails on any advisory at or above `webjs.audit.level` (default `high`) that `webjs.audit.ignore` does not list. The scaffold's `Security: dependency audit` CI step runs it.
|
|
@@ -193,7 +193,7 @@ Whichever form you use, a token nothing references is dropped in both, so an unu
|
|
|
193
193
|
</style>
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
-
`light-dark()` is a native CSS function (CSS Color 5, Baseline 2024), not a library, so nothing to import. A single-theme app drops the `[data-theme]` rules and gives each token one colour.
|
|
196
|
+
`light-dark()` is a native CSS function (CSS Color 5, Baseline 2024), not a library, so nothing to import. A single-theme app drops the `[data-theme]` rules and gives each token one colour.
|
|
197
197
|
|
|
198
198
|
**A manual theme toggle** writes `data-theme` on `<html>` (`light` / `dark`, or removes it for "follow the OS"). If you use `@webjsdev/ui` components, ALSO keep the `.dark` class in sync (the ui kit keys its own tokens off `.dark`), and apply the saved choice in a tiny inline `<script>` in the layout head so there is no first-paint flash. Verify dark mode in a real browser. Light mode passing proves nothing about dark.
|
|
199
199
|
|