From 8c38d04ac8bdca7a6deb6b1e4401eb16e42c86ca Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Fri, 13 Feb 2026 09:44:17 -0800 Subject: [PATCH 1/2] Fix "dev" script in `web` package --- .changeset/legal-poems-wash.md | 5 +++++ .changeset/shiny-pens-knock.md | 5 +++++ packages/web-shared/package.json | 1 + packages/web/app/lib/workflow-api-client.ts | 12 ++++++++---- packages/web/vite.config.ts | 21 ++++++++++++++------- 5 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 .changeset/legal-poems-wash.md create mode 100644 .changeset/shiny-pens-knock.md diff --git a/.changeset/legal-poems-wash.md b/.changeset/legal-poems-wash.md new file mode 100644 index 0000000000..0f1e60f835 --- /dev/null +++ b/.changeset/legal-poems-wash.md @@ -0,0 +1,5 @@ +--- +"@workflow/web-shared": patch +--- + +Set `"type": "module"` in package.json diff --git a/.changeset/shiny-pens-knock.md b/.changeset/shiny-pens-knock.md new file mode 100644 index 0000000000..821123c7a0 --- /dev/null +++ b/.changeset/shiny-pens-knock.md @@ -0,0 +1,5 @@ +--- +"@workflow/web": patch +--- + +Fix "dev" mode diff --git a/packages/web-shared/package.json b/packages/web-shared/package.json index 54861f77a1..1582ee5541 100644 --- a/packages/web-shared/package.json +++ b/packages/web-shared/package.json @@ -11,6 +11,7 @@ "access": "public" }, "license": "Apache-2.0", + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { diff --git a/packages/web/app/lib/workflow-api-client.ts b/packages/web/app/lib/workflow-api-client.ts index 029c9f5961..73c0277a96 100644 --- a/packages/web/app/lib/workflow-api-client.ts +++ b/packages/web/app/lib/workflow-api-client.ts @@ -189,10 +189,12 @@ export function useWorkflowRuns( ]); const [maxPagesVisited, setMaxPagesVisited] = useState(1); - // Store PageResult for each page + // Store PageResult for each page. + // Initial isLoading is false so SSR and client hydration agree; the + // useEffect that triggers the first fetch will set it to true immediately. const [allPageResults, setAllPageResults] = useState< Map> - >(new Map([[0, { data: null, isLoading: true, error: null }]])); + >(new Map([[0, { data: null, isLoading: false, error: null }]])); // Cache for fetched pages - key is cursor (or 'initial' for first page) const pageCache = useRef< @@ -412,10 +414,12 @@ export function useWorkflowHooks( ]); const [maxPagesVisited, setMaxPagesVisited] = useState(1); - // Store PageResult for each page + // Store PageResult for each page. + // Initial isLoading is false so SSR and client hydration agree; the + // useEffect that triggers the first fetch will set it to true immediately. const [allPageResults, setAllPageResults] = useState< Map> - >(new Map([[0, { data: null, isLoading: true, error: null }]])); + >(new Map([[0, { data: null, isLoading: false, error: null }]])); // Cache for fetched pages - key is cursor (or 'initial' for first page) const pageCache = useRef< diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts index 86da2d8448..ac125167ff 100644 --- a/packages/web/vite.config.ts +++ b/packages/web/vite.config.ts @@ -10,15 +10,22 @@ export default defineConfig(({ command, isSsrBuild }) => ({ // can be installed and run without needing any of the UI dependencies // (Radix, lucide-react, etc.) at runtime. Only Node.js built-ins and // express (needed by server.js) remain external. - ssr: - command === 'build' && isSsrBuild - ? { - noExternal: true, - external: ['express'], - } - : undefined, + // + // During dev (`react-router dev`), Vite's SSR module runner evaluates + // modules using its ESM evaluator which cannot handle CJS packages + // (they fail with "module/exports is not defined"). We disable + // noExternal for dev so dependencies are loaded natively by Node.js. + ssr: { + noExternal: command === 'build' ? true : undefined, + external: ['express'], + }, plugins: [tailwindcss(), reactRouter()], resolve: { + // Ensure all workspace packages resolve React from the same location + // to prevent duplicate React instances (which cause "Invalid hook call" + // errors). This is necessary during dev when noExternal is not set and + // linked workspace packages might resolve their own copy of React. + dedupe: ['react', 'react-dom'], alias: [{ find: '~', replacement: '/app' }], }, })); From 8216f3346bf79401b64110cf06b4d52afa952806 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Fri, 13 Feb 2026 14:24:14 -0800 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nathan Rajlich --- packages/web/app/lib/workflow-api-client.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/app/lib/workflow-api-client.ts b/packages/web/app/lib/workflow-api-client.ts index 73c0277a96..ce17c9eca4 100644 --- a/packages/web/app/lib/workflow-api-client.ts +++ b/packages/web/app/lib/workflow-api-client.ts @@ -190,8 +190,8 @@ export function useWorkflowRuns( const [maxPagesVisited, setMaxPagesVisited] = useState(1); // Store PageResult for each page. - // Initial isLoading is false so SSR and client hydration agree; the - // useEffect that triggers the first fetch will set it to true immediately. + // Initial isLoading is false so SSR and client hydration agree; after mount, + // the useEffect that triggers the first fetch will set it to true on the client. const [allPageResults, setAllPageResults] = useState< Map> >(new Map([[0, { data: null, isLoading: false, error: null }]])); @@ -415,8 +415,8 @@ export function useWorkflowHooks( const [maxPagesVisited, setMaxPagesVisited] = useState(1); // Store PageResult for each page. - // Initial isLoading is false so SSR and client hydration agree; the - // useEffect that triggers the first fetch will set it to true immediately. + // Initial isLoading is false so SSR and client hydration agree; after mount + // the useEffect that triggers the first fetch will set it to true on the client. const [allPageResults, setAllPageResults] = useState< Map> >(new Map([[0, { data: null, isLoading: false, error: null }]]));