-
Notifications
You must be signed in to change notification settings - Fork 261
fix(core): bump nitro to 3.0.260610-beta to fix dev-server cold-start crash #1294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ea1c088
e084e72
d46c0fb
62f20a9
3f03111
36b40b3
63490c2
216f657
c92a941
fd8f5bb
3ef4f6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| --- | ||
| "@agent-native/core": patch | ||
| --- | ||
|
|
||
| Bump nitro to 3.0.260610-beta to address a dev-server cold-start race where the | ||
| Nitro Vite worker could be hit before its entry module finished importing, | ||
| surfacing as `Vite environment "nitro" is unavailable` / `UND_ERR_SOCKET`. | ||
|
|
||
| Also raises the `jiti` dependency floor to `^2.7.0` to satisfy the new Nitro | ||
| beta's peer requirement for downstream consumers of the published package. | ||
|
|
||
| Fixes a dev-only 404 for extension-bearing framework endpoints such as | ||
| `/_agent-native/speculation-rules.json` and `/.well-known/agent-card.json`. | ||
| Nitro's Vite dev middleware classifies any request whose path has an asset-like | ||
| extension as a static asset (handing it to Vite) unless a Nitro _route_ matches | ||
| it. Framework endpoints are registered as h3 middleware, invisible to Nitro's | ||
| route table, so their `.json`/`.png` URLs were misrouted to Vite and 404'd | ||
| before reaching the server (extensionless routes like `/ping` were unaffected). | ||
| The framework now adds a dev Vite middleware that marks `/_agent-native/*` and | ||
| framework `/.well-known/*` requests as dynamic so Nitro's dev handler serves | ||
| them. Production builds don't run this heuristic and were never affected. | ||
|
|
||
| Also hardens the async-plugin cold-start path: h3 snapshots its middleware list | ||
| once per request (inside `handler()`), so a route registered by an async plugin | ||
| after that snapshot can 404 on the first request. The framework patches | ||
| `h3.config.onRequest` to await default-plugin bootstrap and tracked plugin inits | ||
| before the snapshot, so late-registered framework routes dispatch naturally on | ||
| every runtime (the dev stub wires neither Nitro hooks nor `onRequest`). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -414,6 +414,31 @@ describe("framework request handler", () => { | |
| return next(); | ||
| } | ||
|
|
||
| // Faithful model of h3 core's `~request()`: it awaits `config.onRequest` | ||
| // BEFORE `handler()` snapshots the middleware list. This is the gate that | ||
| // works on every runtime, including the dev stub that wires neither Nitro | ||
| // hooks nor its own onRequest. | ||
| async function dispatchViaH3Request(nitroApp: any, pathname: string) { | ||
| const event = { | ||
| method: "GET", | ||
| url: new URL(`http://example.test${pathname}`), | ||
| path: pathname, | ||
| context: {}, | ||
| res: { status: 200, headers: new Headers() }, | ||
| }; | ||
| const onRequest = nitroApp.h3?.config?.onRequest; | ||
| if (typeof onRequest === "function") await onRequest(event); | ||
| // handler(): snapshot the middleware list ONCE, then run that snapshot. | ||
| const snapshot = [...nitroApp.h3["~middleware"]]; | ||
| let index = 0; | ||
| const next = async (): Promise<unknown> => { | ||
| const mw = snapshot[index++]; | ||
| if (!mw) return { fellThrough: true }; | ||
| return mw(event, next); | ||
| }; | ||
| return next(); | ||
| } | ||
|
|
||
| it("(bug) middleware-only gate falls through to 404 when the route registers after the snapshot", async () => { | ||
| const nitroApp = createHookableNitroApp(); | ||
| let registerRoute!: () => void; | ||
|
|
@@ -467,6 +492,58 @@ describe("framework request handler", () => { | |
| await expect(pending).resolves.toEqual({ ok: true }); | ||
| }); | ||
|
|
||
| it("delivers a route registered during async init via the h3 onRequest gate (dev runtime, no Nitro hooks)", async () => { | ||
| // Plain nitroApp with no `hooks` — models Nitro's dev stub | ||
| // (`new H3Core({ onError })`, `hooks: undefined`). Only the | ||
| // `h3.config.onRequest` gate can rescue this path. | ||
| const nitroApp = createNitroApp(); | ||
| let registerRoute!: () => void; | ||
| const ready = new Promise<void>((resolve) => { | ||
| registerRoute = () => { | ||
| getH3App(nitroApp).use( | ||
| "/_agent-native/actions/update-visual-plan", | ||
| () => ({ ok: true }), | ||
| ); | ||
| resolve(); | ||
| }; | ||
| }); | ||
| trackPluginInit(nitroApp, ready, { paths: ["/_agent-native/actions"] }); | ||
|
|
||
| const pending = dispatchViaH3Request( | ||
| nitroApp, | ||
| "/_agent-native/actions/update-visual-plan", | ||
| ); | ||
| // Init completes while h3 awaits config.onRequest, before the snapshot. | ||
| await Promise.resolve(); | ||
| registerRoute(); | ||
|
|
||
| await expect(pending).resolves.toEqual({ ok: true }); | ||
| }); | ||
|
|
||
| it("serves a late-registered speculation-rules route via the onRequest gate", async () => { | ||
| const nitroApp = createNitroApp(); | ||
| let registerRoute!: () => void; | ||
| const ready = new Promise<void>((resolve) => { | ||
| registerRoute = () => { | ||
| getH3App(nitroApp).use("/_agent-native/speculation-rules.json", () => ({ | ||
| prefetch: [], | ||
| prerender: [], | ||
| })); | ||
| resolve(); | ||
| }; | ||
| }); | ||
| trackPluginInit(nitroApp, ready, { paths: ["/_agent-native"] }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Broad tracked prefix can shadow unrelated healthy framework routesTracking this init under Additional Info |
||
|
|
||
| const pending = dispatchViaH3Request( | ||
| nitroApp, | ||
| "/_agent-native/speculation-rules.json", | ||
| ); | ||
| await Promise.resolve(); | ||
| registerRoute(); | ||
|
|
||
| await expect(pending).resolves.toEqual({ prefetch: [], prerender: [] }); | ||
| }); | ||
|
|
||
| it("does not treat similar non-prefixed paths as framework routes", async () => { | ||
| process.env.APP_BASE_PATH = "/docs"; | ||
| const nitroApp = createNitroApp(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.