Skip to content

Commit fd58255

Browse files
committed
docs(next): document the @devframes/next helper; drop completed plan 027
Add docs/helpers/next.md (sidebar + helpers index + example cross-links), flagged experimental. Remove the plan-027 spec file now that it's DONE — the plans/README.md row remains the record, pointing at the proposal notes.
1 parent a91a26e commit fd58255

5 files changed

Lines changed: 99 additions & 103 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function helpersItems(prefix: string) {
5555
{ text: 'Utilities', link: `${prefix}/helpers/utilities` },
5656
{ text: 'Vite Bridge', link: `${prefix}/helpers/vite-bridge` },
5757
{ text: 'Nuxt Module', link: `${prefix}/helpers/nuxt` },
58+
{ text: 'Next Helper', link: `${prefix}/helpers/next` },
5859
{ text: 'Open Helpers', link: `${prefix}/helpers/open-helpers` },
5960
{ text: 'Interactive Auth', link: `${prefix}/helpers/interactive-auth` },
6061
] satisfies DefaultTheme.NavItemWithLink[]

docs/examples/minimal-next-devframe-hub.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Package: `minimal-next-devframe-hub` · framework: **React (Next.js)**
1111
## What it proves
1212

1313
- `createHubContext()` boots a hub without any Vite-specific code path.
14-
- A `DevframeHost` implementation plugs the Next host specifics into the hub uniformly.
14+
- The [`@devframes/next`](/helpers/next) bridge supplies the `DevframeHost` and a single `fetch` handler both route handlers delegate to — serving every mounted SPA and its connection meta through devframe's own static handler.
1515
- `mountDevframe(ctx, def)` registers any `DevframeDefinition` as a dock.
1616
- The built-in `hub:commands:execute` RPC dispatches any registered server command, regardless of how the host was constructed.
1717
- The browser-side `connectDevframe({ baseURL: '/__hub/' })` discovers the WS endpoint via the Next route handler at `/__hub/__connection.json`, which starts the singleton host on demand.
@@ -27,6 +27,10 @@ pnpm --filter minimal-next-devframe-hub dev
2727

2828
Open the printed URL to see the docks, commands, messages, and terminals lists, plus a button that dispatches a sample command through `hub:commands:execute`.
2929

30+
## See also
31+
32+
- [Next Helper](/helpers/next) — the `@devframes/next` host bridge this example runs on
33+
3034
## Source
3135

3236
[`examples/minimal-next-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/minimal-next-devframe-hub)

docs/helpers/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Helpers are the optional, opt-in surface around the core `defineDevframe` API: s
1111
| [Utilities](./utilities) | `devframe/utils/*` | Bundled small utilities — terminal colors, hashing, editor launch, structured-clone serialization, and more. |
1212
| [Vite Bridge](./vite-bridge) | `devframe/helpers/vite` | Vite plugin for mounting a devframe inside any Vite-based host (Astro, SolidStart, plain Vite). |
1313
| [Nuxt Module](./nuxt) | `@devframes/nuxt` | Nuxt module that wires a Nuxt SPA as a devframe client and serves the dev-time RPC bridge. |
14+
| [Next Helper](./next) | `@devframes/next` | Route-handler host for mounting devframes inside a Next.js App Router app (experimental). |
1415
| [Open Helpers](./open-helpers) | `devframe/recipes/open-helpers` | Prebuilt RPC actions for "open in editor" and "reveal in Finder". |
1516
| [Interactive Auth](./interactive-auth) | `devframe/recipes/interactive-auth` | Ready-made OTP auth layer — handshake, resolver gate, connect-time trust, and the code/link banner. |
1617

docs/helpers/next.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Next Helper
6+
7+
> [!WARNING]
8+
> Experimental. `@devframes/next` ships as an in-repo spike ([plan 027](https://github.com/devframes/devframe/blob/main/plans/notes/devframes-next-proposal.md)) and isn't published to npm yet. The API described here is the proposed surface; expect changes before a stable release.
9+
10+
The `@devframes/next` package hosts one or more devframes from a Next.js App Router app. Next runs on webpack/Turbopack rather than Vite, so it hosts through a route handler instead of the [Vite Bridge](./vite-bridge): the package hands you a `DevframeHost` plus a single `fetch` handler your catch-all route delegates to.
11+
12+
It handles the two things a Next.js host needs:
13+
14+
1. **Static + connection serving.** `createDevframeNextHost()` returns a `DevframeHost` whose `mountStatic` / `mountConnectionMeta` calls accumulate into one WHATWG-`fetch` handler that serves every mounted SPA — reusing devframe's own [`serveStaticHandler`](/adapters/dev) for SPA fallback, content types, and path-traversal guarding — and answers each `<base>/__connection.json`.
15+
2. **Host-mode Next config.** `withDevframe()` applies the one Next setting a devframe host requires.
16+
17+
## Install
18+
19+
```ts [next.config.mjs]
20+
import { withDevframe } from '@devframes/next'
21+
22+
export default withDevframe({
23+
transpilePackages: ['@antfu/design'],
24+
})
25+
```
26+
27+
`withDevframe` sets `skipTrailingSlashRedirect: true` and preserves the rest of your config. Mounted SPAs are served at `/__<id>/` and reference their assets relatively (`./_next/…`); Next's default trailing-slash redirect (`/__git/``/__git`) would re-root those paths and 404 every asset, so a host serves the base verbatim.
28+
29+
## Hosting a devframe
30+
31+
Build the host once (a module-level singleton, since App Router invokes route handlers per request), then delegate both routes to its `fetch`:
32+
33+
```ts [devframe/host.ts]
34+
import { createHubContext, mountDevframe } from '@devframes/hub/node'
35+
import { createDevframeNextHost } from '@devframes/next'
36+
import { startHttpAndWs } from 'devframe/node'
37+
38+
const nextHost = createDevframeNextHost({
39+
resolveOrigin: () => 'http://localhost:3000',
40+
getStorageDir: scope => resolveStorageDir(scope),
41+
})
42+
43+
const context = await createHubContext({ host: nextHost.host, mode: 'dev' })
44+
await mountDevframe(context, myDevframe)
45+
nextHost.host.mountConnectionMeta('/__hub') // the hub's own connection base
46+
47+
const started = await startHttpAndWs({ context, port, auth: false })
48+
nextHost.setConnectionMeta({ backend: 'websocket', websocket: started.port })
49+
50+
export const hub = { fetch: nextHost.fetch }
51+
```
52+
53+
```ts [app/__[id]/[[...path]]/route.ts]
54+
export const runtime = 'nodejs'
55+
export const dynamic = 'force-dynamic'
56+
57+
export async function GET(request: Request): Promise<Response> {
58+
return hub.fetch(request) // serves every mounted SPA + connection meta
59+
}
60+
```
61+
62+
The same `fetch` answers the hub's own `__hub/__connection.json` — register `/__hub` with `mountConnectionMeta` and one route body covers both.
63+
64+
## API
65+
66+
### `createDevframeNextHost(options)`
67+
68+
| Option | Description |
69+
|--------|-------------|
70+
| `resolveOrigin` | Returns the public origin the app is reachable at, for docks needing an absolute iframe URL. |
71+
| `getStorageDir` | Resolves a directory for persisted state per `scope` (`workspace` / `project` / `global`). |
72+
| `connectionMeta` | Optional initial meta; usually published later via `setConnectionMeta`. |
73+
74+
Returns `{ host, fetch, setConnectionMeta }`:
75+
76+
- **`host`** — the [`DevframeHost`](/guide/hub) to pass to `createHubContext` / `createHostContext`.
77+
- **`fetch(request)`** — the WHATWG-`fetch` handler your route delegates to. Connection meta is matched before the static handler, so an SPA fallback never swallows a `<base>/__connection.json` discovery fetch.
78+
- **`setConnectionMeta(meta)`** — publish the live meta once the RPC/WS port is known. Until then, meta requests answer `503` so a racing client retries rather than caching a wrong endpoint.
79+
80+
### `withDevframe(nextConfig)`
81+
82+
Returns a Next config with `skipTrailingSlashRedirect: true` applied, preserving everything else.
83+
84+
## Runtime
85+
86+
Route handlers that call `fetch` pin `export const runtime = 'nodejs'`: the static handler streams built SPA files from disk, and the side-car RPC/WS server the hub starts is a Node process.
87+
88+
## See also
89+
90+
- [Vite Bridge](./vite-bridge) — the equivalent for Vite-based hosts
91+
- [Hub](/guide/hub)`createHubContext`, `mountDevframe`, and `DevframeHost`
92+
- [minimal-next-devframe-hub](/examples/minimal-next-devframe-hub) — a full working host

plans/027-spike-devframes-next.md

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)