Skip to content

fix(devframe): let a devframe at base / fall through to the host app - #478

Merged
antfu merged 1 commit into
devframes:mainfrom
zerone0x:fix/issue-456-bridge-root-fallthrough
Nov 2, 2026
Merged

antfu merged 1 commit into
devframes:mainfrom
zerone0x:fix/issue-456-bridge-root-fallthrough

Conversation

@zerone0x

@zerone0x zerone0x commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Fixes #456

Problem

Step 3 of the tutorial mounts devframeViteBridge(def, { base: '/', auth: false }). Opening the Vite URL returns:

{"status":404,"message":"Cannot find any route matching [GET] http://localhost:5174/"}

I could reproduce this on main with the tutorial files copied as written. There were two causes:

  1. The bridge took over every path. The bridge's nodeMiddleware runs before Vite's own middlewares. At base /, every request is inside the base, so h3 answered /, /main.tsx and every other path with its 404. next() was never called, so Vite never served the page. Setting a correct root alone does not fix this.
  2. The tutorial config had no root. npx vite --config app/vite.config.ts uses the current folder as the Vite root. That folder has no index.html; the file is in app/. The config's build.outDir: '../dist/client' and Step 4's clientAssets path both expect the root to be app/.

Fix

  • createInstanceShell (used by nodeMiddleware): when the base is /, a request that no devframe route answers now goes to the host's next() instead of h3's 404. A devframe at / shares the origin root with its host app, so it cannot own every path. This is done by one middleware at the end of the chain. It uses h3's fromNodeHandler and waits until the host stack has sent its response. Bases like /__<id>/ do not change: inside its own base the devframe still owns the namespace. Existing tests check this, for example the reserved embedded.js 404 and the unmounted __sse 404. The web handler does not change either.
  • Tutorial: add root: fileURLToPath(new URL('.', import.meta.url)) to app/vite.config.ts, the same way examples/files-inspector does it.
  • Update the nodeMiddleware JSDoc to describe the base / case.

Test plan

  • New test in packages/devframe/src/adapters/__tests__/initiate.test.ts: at base /, nodeMiddleware sends /, /main.tsx and /nested/page?x=1 to the host's next(). __connection.json still answers, and handler still returns 404 for a miss. This test fails without the fix.
  • New test in packages/vite/test/single.test.ts: devframeViteBridge at base / leaves pages to the middleware registered after it.
  • Manual check with the tutorial project (Steps 1 to 3, React, Vite 8.3.5) and the local builds. npx vite --config app/vite.config.ts now serves / and /main.tsx with 200. __connection.json answers, and over /__ws, data-inspector:get-meta and data-inspector:query('users.0.name') return the expected values. npx vite build --config app/vite.config.ts writes dist/client/.
  • pnpm lint, pnpm knip, pnpm typecheck, pnpm build, vitest run (160 files, 1829 passed) and pnpm docs:build all pass.

A devframe mounted at `/` through `nodeMiddleware` answered every
unmatched path with h3's 404, so `devframeViteBridge(def, { base: '/' })`
hid the Vite app's own pages. Unmatched requests at base `/` now call
the host's `next()`. Bases like `/__<id>/` keep owning their namespace.

The tutorial's Vite config also sets `root` to `app/`, so
`vite --config app/vite.config.ts` serves `app/index.html`.

Fixes devframes#456
@vercel

vercel Bot commented Nov 1, 2026

Copy link
Copy Markdown

@zerone0x is attempting to deploy a commit to the NuxtLabs Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

👁️‍🗨️ Review this pull request with grouped, summarized diffs at:
👉 https://pulls.review/gh/devframes/devframe/478?from=github-actions[bot]

raw result

💭 analyzed by vercel-ai-gateway/anthropic/claude-sonnet-5
🕰️ 2026-11-01 16:30 UTC
🔗 head 463f7d7
🤖 automated by pulls.review

{
  "headSha": "463f7d765d2bb5d1d2429faebc73f0c4ce7c9238",
  "result": {
    "overallSummary": "Fixes a bug where mounting a devframe at base `/` (as in the tutorial) caused every request, including the host app's own pages, to be answered by the devframe's h3 404 instead of falling through to the host (e.g. Vite). The core fix adds an end-of-chain fall-through middleware for base `/`, with new tests covering both the devframe adapter and the Vite bridge, plus a tutorial docs fix for a missing Vite `root`.",
    "groups": [
      {
        "key": "fallthrough-fix",
        "label": "Base `/` fall-through fix",
        "summary": "When a devframe is mounted at base `/`, it previously answered every unmatched request with its own 404 instead of letting the host app (e.g. Vite) handle it. Adds a fall-through middleware, mounted last in the chain, that forwards unmatched requests to the host's `next()` when base is `/`, while leaving the `/__<id>/` base behavior unchanged.",
        "category": "core",
        "filePaths": [
          "packages/devframe/src/node/instance-shell.ts",
          "packages/devframe/src/adapters/initiate.ts"
        ],
        "critical": true,
        "fileNotes": [
          {
            "path": "packages/devframe/src/node/instance-shell.ts",
            "text": "`hostFallthrough` tracks the pending `next` per in-flight request via a WeakMap keyed on the raw node request, consumed once by the end-of-chain middleware registered in `mountHostFallthrough`."
          }
        ]
      },
      {
        "key": "tests",
        "label": "Fall-through tests",
        "summary": "New tests exercising the base `/` fall-through: unmatched paths reach the host's `next()`/Vite's own middlewares while devframe routes like `__connection.json` keep answering, and `handler` still 404s when there's no `next` to fall back to.",
        "category": "tests",
        "filePaths": [
          "packages/devframe/src/adapters/__tests__/initiate.test.ts",
          "packages/vite/test/single.test.ts"
        ]
      },
      {
        "key": "docs",
        "label": "Tutorial root fix",
        "summary": "Adds a `root` option to the tutorial's `vite.config.ts` snippet so running `vite` from a different cwd still finds `index.html` and matches the configured `outDir`.",
        "category": "docs",
        "filePaths": [
          "docs/content/1.guide/1.tutorial-server-data-inspector.md"
        ]
      }
    ],
    "schemaVersion": 1,
    "source": "llm",
    "generatedAt": "2026-11-01T16:30:27.139Z",
    "model": "vercel-ai-gateway/anthropic/claude-sonnet-5",
    "locale": "en"
  }
}

@antfu
antfu merged commit 4eeac40 into devframes:main Nov 2, 2026
10 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: Tutorial step 3: opening the Vite URL returns "Cannot find any route matching"

2 participants