mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
7653e6bfdb
## Summary
- Replace Next.js App Router with React Router v7.13.0 framework mode (Vite-based), eliminating the large `next` dependency from the web, CLI, and workflow metapackages
- Serve the web UI in-process from the CLI via Express instead of spawning `next start` as a child process
- Switch RPC transport from JSON to CBOR to preserve binary data types across the wire
- Replace `nuqs` URL state management with React Router's `useSearchParams`
- Replace Next.js server actions with an RPC resource route (`/api/rpc`) and a thin CBOR-based client
## Motivation
The `next` package is ~300MB installed and was the single largest dependency in the monorepo. It also required spawning a separate child process from the CLI to run the o11y web server, adding complexity around process lifecycle management, port readiness polling, and environment variable forwarding.
With React Router framework mode, the web package builds to a standard Express-compatible server bundle that the CLI can import and serve directly in its own process.
## What changed
**Framework swap (`@workflow/web`):**
- `next.config.ts` / `postcss.config.mjs` → `react-router.config.ts` / `vite.config.ts`
- `src/` directory → `app/` directory (React Router convention)
- `src/app/layout.tsx` + `layout-client.tsx` → `app/root.tsx`
- `src/app/page.tsx` → `app/routes/home.tsx`
- `src/app/run/[runId]/page.tsx` → `app/routes/run-detail.tsx`
- Path alias `@/` → `~/`
- Removed all `'use client'` / `'use server'` directives
**Data transport:**
- Server actions → RPC resource route at `/api/rpc` with CBOR encoding
- CBOR preserves `Uint8Array` and other binary types natively (no base64 overhead)
- Stream reading → dedicated `/api/stream/:streamId` resource route
**URL state:**
- `nuqs` (`useQueryState`) → `useSearchParams` from `react-router`
**Fonts:**
- `next/font/google` → Geist `.woff2` files referenced directly from `node_modules/geist` via `@font-face` in CSS
**CLI integration (`@workflow/cli`):**
- `import('@workflow/web/server').then(m => m.startServer(port))`
- No child process, no readiness polling, no cleanup handlers
**Radix UI compatibility:**
- `onSubmit` preventDefault on `AlertDialogContent` and `SheetContent` to prevent Radix's internal `<form method="dialog">` from triggering React Router route actions
- Catch-all action on root route for any stray POSTs
## Dependencies removed
- `next`, `swr`, `nuqs`, `@tailwindcss/postcss`
## Dependencies added
- `react-router` / `@react-router/dev` / `@react-router/node` / `@react-router/express` (all `7.13.0`)
- `express`, `vite`, `@tailwindcss/vite`, `cbor-x`, `isbot`, `cross-env`
- `geist` (devDep)
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
import { createContext, type ReactNode, useContext } from 'react';
|
|
import type { PublicServerConfig } from '~/lib/types';
|
|
|
|
// Re-export PublicServerConfig for convenience
|
|
export type { PublicServerConfig } from '~/lib/types';
|
|
|
|
/**
|
|
* Context value providing server configuration info to the UI.
|
|
*
|
|
* The web UI no longer supports dynamic world configuration via query params.
|
|
* Configuration is read from server-side environment variables, consistent
|
|
* with how the workflow runtime's createWorld() works.
|
|
*
|
|
* This context provides display-only information about the current configuration.
|
|
* Sensitive data like connection strings and auth tokens are never sent to the client.
|
|
*/
|
|
export interface ServerConfigContextValue {
|
|
/** Server configuration info (display-only, no sensitive data) */
|
|
serverConfig: PublicServerConfig;
|
|
}
|
|
|
|
const ServerConfigContext = createContext<ServerConfigContextValue | null>(
|
|
null
|
|
);
|
|
|
|
interface ServerConfigProviderProps {
|
|
children: ReactNode;
|
|
/** Server configuration fetched during SSR */
|
|
serverConfig: PublicServerConfig;
|
|
}
|
|
|
|
/**
|
|
* Provider component that makes server configuration available to child components.
|
|
*
|
|
* The serverConfig should be fetched during server-side rendering using
|
|
* getPublicServerConfig() from @/server/workflow-server-actions.
|
|
*/
|
|
export function ServerConfigProvider({
|
|
children,
|
|
serverConfig,
|
|
}: ServerConfigProviderProps) {
|
|
const value: ServerConfigContextValue = {
|
|
serverConfig,
|
|
};
|
|
|
|
return (
|
|
<ServerConfigContext.Provider value={value}>
|
|
{children}
|
|
</ServerConfigContext.Provider>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Hook to access the server configuration context.
|
|
*
|
|
* Returns display-only information about the current world configuration.
|
|
* This never includes sensitive data like connection strings or auth tokens.
|
|
*/
|
|
export function useServerConfig(): ServerConfigContextValue {
|
|
const context = useContext(ServerConfigContext);
|
|
if (!context) {
|
|
throw new Error(
|
|
'useServerConfig must be used within a ServerConfigProvider'
|
|
);
|
|
}
|
|
return context;
|
|
}
|