Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions changelog/cli/0.10.70.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
package: "@webjsdev/cli"
version: 0.10.70
date: 2026-10-07T19:53:26.375Z
commit_count: 3
---
## Reverts

- **restore the read-the-skill, study-the-gallery agent flow** ([#1614](https://github.com/webjsdev/webjs/pull/1614)) [`1b65f46d`](https://github.com/webjsdev/webjs/commit/1b65f46d)
* The scaffold's AGENTS.md is the original flow again: read the skill, study
the gallery, then clear it. This withdraws the AGENTS.md primer that
shipped in 0.10.69 (#1592); its follow-ups (#1605, #1612) were reverted
before any release.

## Performance

- **build a production-only image from the scaffold Dockerfile** ([#1607](https://github.com/webjsdev/webjs/pull/1607)) [`687ccd0c`](https://github.com/webjsdev/webjs/commit/687ccd0c)
* perf: build a production-only image from the scaffold Dockerfile

The image a scaffolded app built carried every devDependency and its
package manager's cache: 435 MiB on Bun and 719 MiB on Node, while

## Fixes

- **never reload the dev page for files nothing serves** ([#1610](https://github.com/webjsdev/webjs/pull/1610)) [`b7a14ad1`](https://github.com/webjsdev/webjs/commit/b7a14ad1)
* fix: never reload the dev page for files nothing serves

`npm run dev > dev.log` in the app folder made every log line a rebuild
and a reload on both runtimes, which wiped streamed and live UI in the
13 changes: 13 additions & 0 deletions changelog/server/0.8.88.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
package: "@webjsdev/server"
version: 0.8.88
date: 2026-10-07T19:53:26.312Z
commit_count: 1
---
## Fixes

- **never reload the dev page for files nothing serves** ([#1610](https://github.com/webjsdev/webjs/pull/1610)) [`b7a14ad1`](https://github.com/webjsdev/webjs/commit/b7a14ad1)
* fix: never reload the dev page for files nothing serves

`npm run dev > dev.log` in the app folder made every log line a rebuild
and a reload on both runtimes, which wiped streamed and live UI in the
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@webjsdev/cli",
"version": "0.10.69",
"version": "0.10.70",
"type": "module",
"description": "The CLI for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Runs the dev and production servers, scaffolds apps, validates conventions, and drives the database. Node 24+ or Bun.",
"bin": {
Expand All @@ -18,7 +18,7 @@
],
"dependencies": {
"@webjsdev/mcp": "^0.1.0",
"@webjsdev/server": "^0.8.87",
"@webjsdev/server": "^0.8.88",
"@webjsdev/ui": "^0.3.15"
},
"publishConfig": {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ with metadata, Suspense, streaming) for HTML, or `api.js` /
| `dev-styles.js` | The stylesheet re-request for the in-place dev refresh (#1398, #1535), the BROWSER half, a browser-safe module (no node imports) that `reloadClientJs` inlines verbatim after an `export`-keyword strip, so the browser test drives the exact shipping code. `preloadStyles(root?, now?, timeoutMs?)` inserts a cache-busted replacement BESIDE every same-origin stylesheet and resolves once each has loaded or failed (or after `timeoutMs`, default 10s); the reload client swaps the markup in only THEN and calls the returned `commit()`, which drops each old link whose replacement loaded plus any same-identity copy the head merge appended. Swapping first (the old `refreshStyles` order) painted a new utility class with no rule for the whole `webjs.dev.regenerate` compile (56 frames for an 800ms compile, #1535). A replacement that fails removes itself and the working sheet stays (#936, #1400). De-dupes by every attribute except the href query, so a `media="print"` sibling is kept. Tests: `test/dev/browser/refresh-styles.test.js`, `test/e2e/dev-css-flash.test.mjs` (frame sampler, Node and `WEBJS_E2E_RUNTIME=bun`) |
| `port-in-use.js` | A taken port fails fast (#1527). `reusePortRequested()` reads `WEBJS_REUSE_PORT` (the only way either listener sets `SO_REUSEPORT`; Bun's own default with `development: false` is on, which let a second server bind silently). `portInUseError(port, cause)` builds the `EADDRINUSE` error both listeners throw, its message naming the holder found by `findPortHolder` (`/proc/net/tcp{,6}` LISTEN inode, then `/proc/*/fd`; best effort, Linux only, null otherwise). `startNodeListener` now awaits the bind, so a failure rejects `startServer`; the CLI prints it and exits 98, which the dev supervisor treats as final. Tests: `test/listener/port-in-use.test.js`, `test/bun/port-in-use.mjs`. |
| `dev/watch-tree.js` | The dev live-reload watcher (#1521). `watchTree(root, { ignore, onEvent, onError })` watches the root non-recursively plus each non-ignored top-level dir recursively through `dev/watch-recursive.js` (#1529: on Linux under Node one non-recursive watch per directory, because Node 24's per-file recursive watcher goes deaf to a file once `sed -i` or an atomic save replaces it; a byte-identical copy lives in `@webjsdev/cli` `lib/`) (a dir created later is followed), so `node_modules` is never walked (about 275ms and 10k per-file watches per boot on Node 24 Linux before), and every `fs.watch` error goes to `onError` instead of becoming an uncaughtException. `isWatchError(err)` (`syscall === 'watch'`) is what `installProcessHandlers` (`listener-core.js`) checks in dev to log a stray watcher error as a warning instead of shutting down; prod keeps every uncaught exception fatal. Tests: `test/dev/watch-tree.test.js`, `test/dev/watch-error-guard.test.js`, `test/bun/dev-watch-error.mjs`. |
| `dev/watch-ignore.js` | What the dev watcher never turns into a rebuild or a reload beyond the framework's own artefacts: tool output nothing serves (`*.log`, `coverage/`, `.cache/`, `.nyc_output/`, `test-results/`, `playwright-report/`, `.turbo/`, swap and backup files, `.DS_Store`) and whatever the watched root's `.gitignore` ignores (the common subset: comments, `!`, trailing `/`, anchoring `/`, `*` `**` `?`; `.env*` excluded since the server reads it). `npm run dev > dev.log` used to reload the page on every log line. The CLI supervisor's `shouldIgnoreRestartPath` skips the same output. Tests: `test/dev/watch-ignore.test.js`, cross-runtime `test/bun/dev-ignore-unserved.mjs`; the release gate writes a `dev.log` and a `coverage/` file after its edits and asserts no reload frame. |
| `dev/watch-ignore.js` | What the dev watcher never turns into a rebuild or a reload beyond the framework's own artefacts: tool output nothing serves (`*.log`, `coverage/`, `.cache/`, `.nyc_output/`, `test-results/`, `playwright-report/`, `.turbo/`, swap and backup files, `.DS_Store`) and whatever the watched root's `.gitignore` ignores (the common subset: comments, `!`, trailing `/`, anchoring `/`, `*` `**` `?`; `.env*` excluded since the server reads it). `npm run dev > dev.log` used to reload the page on every log line. The CLI supervisor's `shouldIgnoreRestartPath` skips the same fixed output list but not `.gitignore` (it only watches source dirs, so a gitignored file there still restarts Node). `.gitignore` is read once per watcher start, so an edited `.gitignore` takes effect after a dev restart. Tests: `test/dev/watch-ignore.test.js`, cross-runtime `test/bun/dev-ignore-unserved.mjs`; the release gate writes a `dev.log` and a `coverage/` file after its edits and asserts no reload frame. |
| `dev-classify.js` | The dev live-reload classifier (#1398). Pure, state-free, and import-free so the ladder is unit-testable without booting a server. `classifyChangedPath(abs, ctx)` returns `{ v, by, why }` off a first-match ladder: a cold analysis reloads (Vite's pessimistic seed), a path outside `appDir` is an extra `webjs.dev.watch` root and takes `shell`, a path in the SHIPPED closure reloads (the transitive answer, and the rung that catches a page which ships whole and an action a shipping component imports), a page file morphs, anything else the graph walked is server-only and takes `shell`, and everything else reloads (a new file, a deleted file, and every `public/` asset, which reloads because `public/` is outside the module graph so the server cannot tell what the file is or what depends on it). `strongerVerdict(a, b)` compares by index into `RELOAD_VERDICTS` (`['reload', 'shell', 'page']`, strongest first), treating `null` as weakest and an unrecognised name as strongest, so a batch can only ever over-reload |
| `dev-overlay.js` | The BROWSER half of the dev error overlay (#264), a browser-safe module (no node imports) that `reloadClientJs` inlines verbatim after an `export`-keyword strip, so the browser test drives the exact shipping code. `renderDevOverlay(frame, currentPath?)` builds the card with `createElement` + `textContent` ONLY (never `innerHTML`, so a hostile message / path / code frame is inert text); `dismissDevOverlay()` takes it down and forgets any held frame, which is what the Dismiss button calls. **URL scope (#1047):** a url-stamped `render` frame for a different path than `currentPath` (default `location.pathname + location.search`, compared encoding-tolerantly) is NOT rendered. The gate runs BEFORE any removal, so a refused frame neither paints nor wipes a live `rebuild` / `ts-strip` overlay. It cannot simply drop the frame either, because the SSE frame is pushed during the render, before the navigation response is sent, so it arrives while `location` is still the old page: a refused frame goes to a PENDING slot that `syncDevOverlayToLocation(currentPath?)` re-evaluates on the next navigation, which renders it only if the url matches AND it arrived during the navigation now finishing, and drops it otherwise (consume-once, so retention is bounded with no timer). The second half of that condition is what stops a frame held from an IDLE render (another tab's page, a background fetch of some other url, never a link prefetch since that reports nothing) painting on a later visit to that url, when the page may render fine; `markDevOverlayNavStart()` is the nav-start marker it counts. `installDevOverlayNavSync(opts?)` wires all of it to the client router's `webjs:navigate` (applied nav) + `popstate` (a snapshot-cache restore returns before `webjs:navigate`) + `webjs:before-cache` (the nav START, since `snapshotCurrent` runs at the top of every navigation and form submission), and returns an uninstall thunk. Two things keep a snapshot copy of the overlay from becoming an undismissable card (the router's tier-4 popstate restore `replaceChildren`s the body from cached HTML, and a parsed copy is not the node this module holds, so nothing could remove it and its Dismiss button has no listener). `before-cache` DETACHES the overlay across the router's synchronous `outerHTML` read and re-attaches it a microtask later, so the cached HTML carries none. That is what covers the hard ordering: under an opt-in view transition the router defers the body swap PAST the sync, so a copy the swap inserts would arrive after any sweep had already run. The sync additionally SWEEPS any `[data-webjs-error-overlay]` element it does not own, as the backstop for a copy from anywhere else (a snapshot cached before this client installed its listeners, or any other wholesale body replacement). What `before-cache` must NOT do is strip the overlay for good, which the event's own contract invites: it fires on EVERY navigation, so that would tear a `rebuild` / `ts-strip` overlay off the page on any link click while the build was still broken. `packages/core` is untouched: the fix rides existing events |
| `frame-render.js` | Server-side `<webjs-frame>` subtree extraction (#253). `requestedFrameId(req)` reads the `x-webjs-frame` header (null when absent, the normal full-page path); `extractFrameSubtree(html, id)` returns the `<webjs-frame id>...</webjs-frame>` slice from rendered HTML verbatim (so byte-equivalent by construction), balancing nested `<webjs-frame>` tags and reading the `id` attribute (not a substring match), or null when the id is absent. Used by `ssr.js`'s frame-render branch |
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@webjsdev/server",
"version": "0.8.87",
"version": "0.8.88",
"type": "module",
"description": "The server for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Provides the file-based router, SSR, server actions, route handlers, middleware, and live reload on Node 24+ or Bun.",
"main": "index.js",
Expand Down
Loading