vite was discovering effect/Match, effect/Predicate, and js-yaml during test runs instead of at boot, forcing a re-optimize and full program reload on both the client and SSR (workerd) environments. Each reload strands the previous worker program's heap inside workerd, and a handful of them exhausts its heap limit and kills the dev server mid-shard. Adding these to optimizeDeps.include (client) and environments.ssr.optimizeDeps.include (SSR) in apps/cloud and apps/host-selfhost's vite configs makes vite bundle them at boot instead, so no mid-run discovery happens. js-yaml is a transitive dependency via @executor-js/plugin-openapi that bun's isolated install doesn't hoist into either app's node_modules, so a bare "js-yaml" specifier silently fails to resolve in optimizeDeps.include. Used vite's "<pkg> > <dep>" nested resolution syntax to resolve it from the owning package instead.
Unified · Split
| @@ -65,6 +65,32 @@ export default defineConfig(({ command, mode }) => { · 64 unmodified lines | |||
| 65 | delete (publicEnv as Record<string, string | undefined>).VITE_PUBLIC_OTLP_TRACES_URL; | 65 | delete (publicEnv as Record<string, string | undefined>).VITE_PUBLIC_OTLP_TRACES_URL; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | // Deps vite only discovers once a lazy-loaded React chunk actually renders | ||
| 69 | // (e.g. opening the MCP/OpenAPI "add source" flow). Discovering them mid-run | ||
| 70 | // forces a re-optimize + full program reload; in workerd (apps/cloud's SSR | ||
| 71 | // worker) each reload stacks a new isolate's heap on top of the last one | ||
| 72 | // without freeing it, so a handful of reloads exhausts the worker's heap | ||
| 73 | // limit and crashes the dev server. Pre-bundling them at boot means vite | ||
| 74 | // never discovers them mid-run, so it never reloads. Keep this list scoped | ||
| 75 | // to deps actually imported by UI code (grep `from "effect/` under | ||
| 76 | // packages/react/src and packages/plugins/*/src/react, plus js-yaml pulled | ||
| 77 | // in via packages/plugins/openapi/src/sdk) rather than including the world. | ||
| 78 | // js-yaml is a transitive dep (via @executor-js/plugin-openapi), not hoisted | ||
| 79 | // into apps/cloud/node_modules under bun's isolated install, so a plain | ||
| 80 | // "js-yaml" specifier fails to resolve here and silently falls out of the | ||
| 81 | // pre-bundle. The "<pkg> > <dep>" syntax resolves it starting from that | ||
| 82 | // package's own node_modules instead. | ||
| 83 | const lateDiscoveredDeps = [ | ||
| 84 | "effect/Match", | ||
| 85 | "effect/Predicate", | ||
| 86 | "effect/Exit", | ||
| 87 | "effect/Option", | ||
| 88 | "effect/Cause", | ||
| 89 | "effect/Data", | ||
| 90 | "effect/Schema", | ||
| 91 | "@executor-js/plugin-openapi > js-yaml", | ||
| 92 | ]; | ||
| 93 | |||
| 68 | return { | 94 | return { |
| 69 | define: Object.fromEntries( | 95 | define: Object.fromEntries( |
| 70 | Object.entries(publicEnv) | 96 | Object.entries(publicEnv) |
| @@ -81,6 +107,22 @@ export default defineConfig(({ command, mode }) => { · 10 unmodified lines | |||
| 81 | }, | 107 | }, |
| 82 | }, | 108 | }, |
| 83 | resolve: { tsconfigPaths: true }, | 109 | resolve: { tsconfigPaths: true }, |
| 110 | // Client-side pre-bundle. See lateDiscoveredDeps comment above. | ||
| 111 | optimizeDeps: { | ||
| 112 | include: lateDiscoveredDeps, | ||
| 113 | }, | ||
| 114 | // SSR/worker-side pre-bundle for the same deps, keyed under the "ssr" | ||
| 115 | // environment name that `cloudflare({ viteEnvironment: { name: "ssr" } })` | ||
| 116 | // below uses. The cloudflare plugin sets its own environments.ssr.optimizeDeps | ||
| 117 | // (entries/exclude for worker externals); vite merges include arrays rather | ||
| 118 | // than replacing them, so this only adds to that, it doesn't fight it. | ||
| 119 | environments: { | ||
| 120 | ssr: { | ||
| 121 | optimizeDeps: { | ||
| 122 | include: lateDiscoveredDeps, | ||
| 123 | }, | ||
| 124 | }, | ||
| 125 | }, | ||
| 84 | plugins: [ | 126 | plugins: [ |
| 85 | devCrashGuard(), | 127 | devCrashGuard(), |
| 86 | tailwindcss(), | 128 | tailwindcss(), |
| @@ -190,6 +190,29 @@ export default defineConfig({ · 189 unmodified lines | |||
| 190 | alias: { "@executor-app": APP_ROOT }, | 190 | alias: { "@executor-app": APP_ROOT }, |
| 191 | dedupe: ["react", "react-dom"], | 191 | dedupe: ["react", "react-dom"], |
| 192 | }, | 192 | }, |
| 193 | // This SPA shares packages/app's shell and packages/react's lazy-loaded | ||
| 194 | // "add source" UI with apps/cloud, so the same deps only surface once a | ||
| 195 | // lazy chunk actually renders (e.g. opening the MCP/OpenAPI add-source | ||
| 196 | // flow). Pre-bundle them at boot so vite never discovers them mid-run and | ||
| 197 | // never has to re-optimize + reload. No SSR/worker environment here (this | ||
| 198 | // app has no cloudflare plugin, it's a plain SPA build), so only the | ||
| 199 | // client optimizeDeps applies. Keep in sync with apps/cloud/vite.config.ts. | ||
| 200 | // js-yaml is a transitive dep (via @executor-js/plugin-openapi) that isn't | ||
| 201 | // hoisted into this app's node_modules under bun's isolated install, so a | ||
| 202 | // plain "js-yaml" specifier fails to resolve; "<pkg> > <dep>" resolves it | ||
| 203 | // from that package's own node_modules instead. | ||
| 204 | optimizeDeps: { | ||
| 205 | include: [ | ||
| 206 | "effect/Match", | ||
| 207 | "effect/Predicate", | ||
| 208 | "effect/Exit", | ||
| 209 | "effect/Option", | ||
| 210 | "effect/Cause", | ||
| 211 | "effect/Data", | ||
| 212 | "effect/Schema", | ||
| 213 | "@executor-js/plugin-openapi > js-yaml", | ||
| 214 | ], | ||
| 215 | }, | ||
| 193 | define: { | 216 | define: { |
| 194 | "import.meta.env.VITE_APP_VERSION": JSON.stringify(EXECUTOR_VERSION ?? "0.0.0"), | 217 | "import.meta.env.VITE_APP_VERSION": JSON.stringify(EXECUTOR_VERSION ?? "0.0.0"), |
| 195 | // Self-host upgrades by pulling/rebuilding the image (or git + rebuild), not | 218 | // Self-host upgrades by pulling/rebuilding the image (or git + rebuild), not |