| author | |
| committer | |
| log | 388b43a27cc37ae067087073825633d4fc5de3b7 |
| tree | b2a230e5172d87e38440c10d0485c4d7906c0e8a |
| parent | 23f0a1ae38cb1fc510a499ef2b3602f5ae98d0c9 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>9 files changed, 267 insertions(+), 3 deletions(-)
apps/web/package.json+2| ... | ... | @@ -52,11 +52,13 @@ |
| 52 | 52 | "@tailwindcss/vite": "^4.0.0", |
| 53 | 53 | "@tanstack/router-plugin": "^1.161.0", |
| 54 | 54 | "@types/babel__core": "^7.20.5", |
| 55 | "@types/compression": "^1.8.1", | |
| 55 | 56 | "@types/react": "~19.2.14", |
| 56 | 57 | "@types/react-dom": "~19.2.3", |
| 57 | 58 | "@vercel/config": "^0.3.0", |
| 58 | 59 | "@vitejs/plugin-react": "^6.0.0", |
| 59 | 60 | "babel-plugin-react-compiler": "1.0.0", |
| 61 | "compression": "^1.8.1", | |
| 60 | 62 | "msw": "2.12.11", |
| 61 | 63 | "tailwindcss": "^4.0.0", |
| 62 | 64 | "vite": "catalog:", |
apps/web/scripts/warm-dep-cache.ts created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | // @effect-diagnostics nodeBuiltinImport:off - setup-script bootstrap, runs before any Effect runtime exists. | |
| 2 | /** | |
| 3 | * Pre-warms Vite's dependency-optimizer cache (`node_modules/.vite/deps`) so | |
| 4 | * the first `vp run dev` in a fresh worktree doesn't stall the initial page | |
| 5 | * load on a full optimize pass. Run by the t3.json worktree setup script; | |
| 6 | * safe to re-run — a valid cache makes this a fast no-op. | |
| 7 | * | |
| 8 | * The cache cannot be shared between worktrees: Vite's config hash includes | |
| 9 | * the absolute project root, so each worktree must warm its own. | |
| 10 | */ | |
| 11 | import * as NodePath from "node:path"; | |
| 12 | import * as NodeURL from "node:url"; | |
| 13 | ||
| 14 | import { optimizeDeps, resolveConfig } from "vite"; | |
| 15 | ||
| 16 | const webRoot = NodePath.dirname(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url))); | |
| 17 | ||
| 18 | // logLevel "error" silences the "manually calling optimizeDeps is deprecated" | |
| 19 | // warning — deliberate here: warming ahead of the server is the whole point. | |
| 20 | const config = await resolveConfig({ root: webRoot, logLevel: "error" }, "serve"); | |
| 21 | await optimizeDeps(config); | |
| 22 | console.log("[warm-dep-cache] web dependency cache is warm"); |
apps/web/tsconfig.json+8-1| ... | ... | @@ -25,5 +25,12 @@ |
| 25 | 25 | } |
| 26 | 26 | ] |
| 27 | 27 | }, |
| 28 | "include": ["src", "vite.config.ts", "vercel.ts", "test", "../../scripts/lib/public-config.ts"] | |
| 28 | "include": [ | |
| 29 | "src", | |
| 30 | "vite.config.ts", | |
| 31 | "vercel.ts", | |
| 32 | "test", | |
| 33 | "scripts/warm-dep-cache.ts", | |
| 34 | "../../scripts/lib/public-config.ts" | |
| 35 | ] | |
| 29 | 36 | } |
apps/web/vite.config.ts+36-1| ... | ... | @@ -1,10 +1,13 @@ |
| 1 | import * as NodeZlib from "node:zlib"; | |
| 2 | ||
| 1 | 3 | import tailwindcss from "@tailwindcss/vite"; |
| 2 | 4 | import react, { reactCompilerPreset } from "@vitejs/plugin-react"; |
| 3 | 5 | import babel from "@rolldown/plugin-babel"; |
| 4 | 6 | import { tanstackRouter } from "@tanstack/router-plugin/vite"; |
| 7 | import compression from "compression"; | |
| 5 | 8 | import { defineProject, type TestProjectInlineConfiguration } from "vite-plus/test/config"; |
| 6 | 9 | import "vite-plus/test/config"; |
| 7 | import { defineConfig } from "vite-plus"; | |
| 10 | import { defineConfig, type Connect, type Plugin } from "vite-plus"; | |
| 8 | 11 | import pkg from "./package.json" with { type: "json" }; |
| 9 | 12 | |
| 10 | 13 | import { DEV_PROXIED_PATH_PREFIXES } from "@t3tools/shared/devProxy"; |
| ... | ... | @@ -55,6 +58,8 @@ const sourcemapEnv = process.env.T3CODE_WEB_SOURCEMAP?.trim().toLowerCase(); |
| 55 | 58 | // Vite 8.1's experimental bundled dev mode: serves rolldown-bundled chunks in |
| 56 | 59 | // dev for much faster startup/reload on large module graphs, with HMR served |
| 57 | 60 | // as hot patches. Opt-in while experimental: T3CODE_BUNDLED_DEV=1 pnpm dev:web |
| 61 | // The dev runner defaults this on for --share runs (remote browsers pay a | |
| 62 | // round trip per import level in unbundled dev); T3CODE_BUNDLED_DEV=0 opts out. | |
| 58 | 63 | const bundledDevEnv = process.env.T3CODE_BUNDLED_DEV?.trim().toLowerCase(); |
| 59 | 64 | const bundledDev = bundledDevEnv === "1" || bundledDevEnv === "true"; |
| 60 | 65 | |
| ... | ... | @@ -114,6 +119,28 @@ function resolveDevProxyTarget( |
| 114 | 119 | |
| 115 | 120 | const devProxyTarget = resolveDevProxyTarget(process.env.T3CODE_PORT, configuredWsUrl); |
| 116 | 121 | |
| 122 | // Vite's dev server sends JS uncompressed. On localhost that is free; over a | |
| 123 | // shared origin (tailnet, LAN) it is the whole cold-start: bundled dev serves | |
| 124 | // one ~25 MB chunk, and a typical uplink moves that in about a minute while | |
| 125 | // both machines sit idle. Compressing turns it into a few seconds of CPU. | |
| 126 | // Brotli quality 5 keeps encode time in the hundreds of ms; the default | |
| 127 | // (quality 11) would trade the transfer stall for an equally long encode stall. | |
| 128 | function devCompressionPlugin(): Plugin { | |
| 129 | return { | |
| 130 | name: "t3code:dev-compression", | |
| 131 | apply: "serve", | |
| 132 | configureServer(server) { | |
| 133 | // compression() is typed against Express's req/res, which extend the | |
| 134 | // node http objects Connect actually passes — safe to narrow. | |
| 135 | server.middlewares.use( | |
| 136 | compression({ | |
| 137 | brotli: { params: { [NodeZlib.constants.BROTLI_PARAM_QUALITY]: 5 } }, | |
| 138 | }) as unknown as Connect.NextHandleFunction, | |
| 139 | ); | |
| 140 | }, | |
| 141 | }; | |
| 142 | } | |
| 143 | ||
| 117 | 144 | // Vite rejects requests whose Host header isn't localhost, which blocks sharing |
| 118 | 145 | // a dev server over Tailscale/LAN. Tailnet names are safe to allow wholesale: |
| 119 | 146 | // the DNS is controlled by tailscale, so they can't be rebound by an attacker. |
| ... | ... | @@ -128,6 +155,7 @@ export default defineConfig(() => { |
| 128 | 155 | return { |
| 129 | 156 | assetsInclude: ["**/*.wasm"], |
| 130 | 157 | plugins: [ |
| 158 | devCompressionPlugin(), | |
| 131 | 159 | tanstackRouter(), |
| 132 | 160 | react(), |
| 133 | 161 | babel({ |
| ... | ... | @@ -187,6 +215,13 @@ export default defineConfig(() => { |
| 187 | 215 | port, |
| 188 | 216 | strictPort: true, |
| 189 | 217 | allowedHosts, |
| 218 | // Transform the whole module graph at server start instead of on the | |
| 219 | // first request. Without this, a cold worktree discovers and transforms | |
| 220 | // modules one import-level at a time while the browser waits — which | |
| 221 | // over a tailnet origin turns into minutes of waterfall. | |
| 222 | warmup: { | |
| 223 | clientFiles: ["./src/main.tsx"], | |
| 224 | }, | |
| 190 | 225 | ...(devProxyTarget |
| 191 | 226 | ? { |
| 192 | 227 | // One entry per shared prefix; the server's dev catch-all 404s the |
docs/internals/scripts.md+3| ... | ... | @@ -24,6 +24,9 @@ authenticated. |
| 24 | 24 | - `vp run dev`: Starts contracts, server, and web in watch mode. |
| 25 | 25 | - `vp run dev --share`: Also publishes the web port over HTTPS on this machine's tailnet. The |
| 26 | 26 | startup pairing URL is built against the shared origin, and the mapping is removed on exit. |
| 27 | Shared runs default to Vite's bundled dev mode (`T3CODE_BUNDLED_DEV=1`): a remote browser pays a | |
| 28 | network round trip per import level in unbundled dev, which turns a cold module graph into | |
| 29 | minutes of waterfall. Set `T3CODE_BUNDLED_DEV=0` to opt a shared run back out. | |
| 27 | 30 | - `vp run dev --browser`: Auto-opens a browser. Off by default. The dev runner writes |
| 28 | 31 | `T3CODE_NO_BROWSER` itself from this flag, so setting `T3CODE_NO_BROWSER=0` in your environment has |
| 29 | 32 | no effect; use `--browser`. |
pnpm-lock.yaml+78| ... | ... | @@ -623,6 +623,9 @@ importers: |
| 623 | 623 | '@types/babel__core': |
| 624 | 624 | specifier: ^7.20.5 |
| 625 | 625 | version: 7.20.5 |
| 626 | '@types/compression': | |
| 627 | specifier: ^1.8.1 | |
| 628 | version: 1.8.1 | |
| 626 | 629 | '@types/react': |
| 627 | 630 | specifier: ~19.2.14 |
| 628 | 631 | version: 19.2.16 |
| ... | ... | @@ -638,6 +641,9 @@ importers: |
| 638 | 641 | babel-plugin-react-compiler: |
| 639 | 642 | specifier: 1.0.0 |
| 640 | 643 | version: 1.0.0 |
| 644 | compression: | |
| 645 | specifier: ^1.8.1 | |
| 646 | version: 1.8.1 | |
| 641 | 647 | msw: |
| 642 | 648 | specifier: 2.12.11 |
| 643 | 649 | version: 2.12.11(@types/node@24.12.4)(typescript@6.0.3) |
| ... | ... | @@ -4777,6 +4783,9 @@ packages: |
| 4777 | 4783 | '@types/babel__traverse@7.28.0': |
| 4778 | 4784 | resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} |
| 4779 | 4785 | |
| 4786 | '@types/body-parser@1.19.6': | |
| 4787 | resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} | |
| 4788 | ||
| 4780 | 4789 | '@types/bun@1.3.14': |
| 4781 | 4790 | resolution: {integrity: sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==} |
| 4782 | 4791 | |
| ... | ... | @@ -4786,6 +4795,12 @@ packages: |
| 4786 | 4795 | '@types/chai@5.2.3': |
| 4787 | 4796 | resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} |
| 4788 | 4797 | |
| 4798 | '@types/compression@1.8.1': | |
| 4799 | resolution: {integrity: sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==} | |
| 4800 | ||
| 4801 | '@types/connect@3.4.38': | |
| 4802 | resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} | |
| 4803 | ||
| 4789 | 4804 | '@types/debug@4.1.13': |
| 4790 | 4805 | resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} |
| 4791 | 4806 | |
| ... | ... | @@ -4801,6 +4816,12 @@ packages: |
| 4801 | 4816 | '@types/estree@1.0.9': |
| 4802 | 4817 | resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} |
| 4803 | 4818 | |
| 4819 | '@types/express-serve-static-core@5.1.3': | |
| 4820 | resolution: {integrity: sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==} | |
| 4821 | ||
| 4822 | '@types/express@5.0.6': | |
| 4823 | resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} | |
| 4824 | ||
| 4804 | 4825 | '@types/fs-extra@9.0.13': |
| 4805 | 4826 | resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} |
| 4806 | 4827 | |
| ... | ... | @@ -4813,6 +4834,9 @@ packages: |
| 4813 | 4834 | '@types/http-cache-semantics@4.2.0': |
| 4814 | 4835 | resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} |
| 4815 | 4836 | |
| 4837 | '@types/http-errors@2.0.5': | |
| 4838 | resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} | |
| 4839 | ||
| 4816 | 4840 | '@types/istanbul-lib-coverage@2.0.6': |
| 4817 | 4841 | resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} |
| 4818 | 4842 | |
| ... | ... | @@ -4843,6 +4867,12 @@ packages: |
| 4843 | 4867 | '@types/pngjs@6.0.5': |
| 4844 | 4868 | resolution: {integrity: sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==} |
| 4845 | 4869 | |
| 4870 | '@types/qs@6.15.1': | |
| 4871 | resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} | |
| 4872 | ||
| 4873 | '@types/range-parser@1.2.7': | |
| 4874 | resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} | |
| 4875 | ||
| 4846 | 4876 | '@types/react-dom@19.2.3': |
| 4847 | 4877 | resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} |
| 4848 | 4878 | peerDependencies: |
| ... | ... | @@ -4857,6 +4887,12 @@ packages: |
| 4857 | 4887 | '@types/responselike@1.0.3': |
| 4858 | 4888 | resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} |
| 4859 | 4889 | |
| 4890 | '@types/send@1.2.1': | |
| 4891 | resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} | |
| 4892 | ||
| 4893 | '@types/serve-static@2.2.0': | |
| 4894 | resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} | |
| 4895 | ||
| 4860 | 4896 | '@types/statuses@2.0.6': |
| 4861 | 4897 | resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} |
| 4862 | 4898 | |
| ... | ... | @@ -14913,6 +14949,11 @@ snapshots: |
| 14913 | 14949 | dependencies: |
| 14914 | 14950 | '@babel/types': 7.29.7 |
| 14915 | 14951 | |
| 14952 | '@types/body-parser@1.19.6': | |
| 14953 | dependencies: | |
| 14954 | '@types/connect': 3.4.38 | |
| 14955 | '@types/node': 24.12.4 | |
| 14956 | ||
| 14916 | 14957 | '@types/bun@1.3.14': |
| 14917 | 14958 | dependencies: |
| 14918 | 14959 | bun-types: 1.3.14 |
| ... | ... | @@ -14929,6 +14970,15 @@ snapshots: |
| 14929 | 14970 | '@types/deep-eql': 4.0.2 |
| 14930 | 14971 | assertion-error: 2.0.1 |
| 14931 | 14972 | |
| 14973 | '@types/compression@1.8.1': | |
| 14974 | dependencies: | |
| 14975 | '@types/express': 5.0.6 | |
| 14976 | '@types/node': 24.12.4 | |
| 14977 | ||
| 14978 | '@types/connect@3.4.38': | |
| 14979 | dependencies: | |
| 14980 | '@types/node': 24.12.4 | |
| 14981 | ||
| 14932 | 14982 | '@types/debug@4.1.13': |
| 14933 | 14983 | dependencies: |
| 14934 | 14984 | '@types/ms': 2.1.0 |
| ... | ... | @@ -14943,6 +14993,19 @@ snapshots: |
| 14943 | 14993 | |
| 14944 | 14994 | '@types/estree@1.0.9': {} |
| 14945 | 14995 | |
| 14996 | '@types/express-serve-static-core@5.1.3': | |
| 14997 | dependencies: | |
| 14998 | '@types/node': 24.12.4 | |
| 14999 | '@types/qs': 6.15.1 | |
| 15000 | '@types/range-parser': 1.2.7 | |
| 15001 | '@types/send': 1.2.1 | |
| 15002 | ||
| 15003 | '@types/express@5.0.6': | |
| 15004 | dependencies: | |
| 15005 | '@types/body-parser': 1.19.6 | |
| 15006 | '@types/express-serve-static-core': 5.1.3 | |
| 15007 | '@types/serve-static': 2.2.0 | |
| 15008 | ||
| 14946 | 15009 | '@types/fs-extra@9.0.13': |
| 14947 | 15010 | dependencies: |
| 14948 | 15011 | '@types/node': 24.12.4 |
| ... | ... | @@ -14955,6 +15018,8 @@ snapshots: |
| 14955 | 15018 | |
| 14956 | 15019 | '@types/http-cache-semantics@4.2.0': {} |
| 14957 | 15020 | |
| 15021 | '@types/http-errors@2.0.5': {} | |
| 15022 | ||
| 14958 | 15023 | '@types/istanbul-lib-coverage@2.0.6': {} |
| 14959 | 15024 | |
| 14960 | 15025 | '@types/istanbul-lib-report@3.0.3': |
| ... | ... | @@ -14989,6 +15054,10 @@ snapshots: |
| 14989 | 15054 | dependencies: |
| 14990 | 15055 | '@types/node': 24.12.4 |
| 14991 | 15056 | |
| 15057 | '@types/qs@6.15.1': {} | |
| 15058 | ||
| 15059 | '@types/range-parser@1.2.7': {} | |
| 15060 | ||
| 14992 | 15061 | '@types/react-dom@19.2.3(@types/react@19.2.16)': |
| 14993 | 15062 | dependencies: |
| 14994 | 15063 | '@types/react': 19.2.16 |
| ... | ... | @@ -15005,6 +15074,15 @@ snapshots: |
| 15005 | 15074 | dependencies: |
| 15006 | 15075 | '@types/node': 24.12.4 |
| 15007 | 15076 | |
| 15077 | '@types/send@1.2.1': | |
| 15078 | dependencies: | |
| 15079 | '@types/node': 24.12.4 | |
| 15080 | ||
| 15081 | '@types/serve-static@2.2.0': | |
| 15082 | dependencies: | |
| 15083 | '@types/http-errors': 2.0.5 | |
| 15084 | '@types/node': 24.12.4 | |
| 15085 | ||
| 15008 | 15086 | '@types/statuses@2.0.6': {} |
| 15009 | 15087 | |
| 15010 | 15088 | '@types/unist@2.0.11': {} |
scripts/dev-runner.test.ts+109| ... | ... | @@ -986,6 +986,115 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { |
| 986 | 986 | }); |
| 987 | 987 | }); |
| 988 | 988 | |
| 989 | // A shared origin means a remote browser, where unbundled dev's | |
| 990 | // per-module waterfall pays a tailnet round trip per import level. The | |
| 991 | // runner defaults bundled dev on for the spawned stack, but only | |
| 992 | // defaults: an explicit T3CODE_BUNDLED_DEV (even "0") must pass through. | |
| 993 | describe("--share bundled dev default", () => { | |
| 994 | const shareSpawnedEnv = (input: { readonly ambientBundledDev: string | undefined }) => | |
| 995 | Effect.gen(function* () { | |
| 996 | let captured: Record<string, string | undefined> | undefined; | |
| 997 | const spawnerLayer = Layer.succeed( | |
| 998 | ChildProcessSpawner.ChildProcessSpawner, | |
| 999 | ChildProcessSpawner.make((command) => { | |
| 1000 | const spawned = command as unknown as { | |
| 1001 | readonly command: string; | |
| 1002 | readonly args: ReadonlyArray<string>; | |
| 1003 | readonly options?: { readonly env?: Record<string, string | undefined> }; | |
| 1004 | }; | |
| 1005 | if (spawned.command === "vp") { | |
| 1006 | captured = spawned.options?.env; | |
| 1007 | return Effect.succeed(mockProcess(0)); | |
| 1008 | } | |
| 1009 | // tailscale: answer `status --json` with a valid tailnet name, | |
| 1010 | // succeed the `serve`/`off` calls. | |
| 1011 | return Effect.succeed( | |
| 1012 | ChildProcessSpawner.makeHandle({ | |
| 1013 | pid: ChildProcessSpawner.ProcessId(2), | |
| 1014 | exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)), | |
| 1015 | isRunning: Effect.succeed(false), | |
| 1016 | kill: () => Effect.void, | |
| 1017 | unref: Effect.succeed(Effect.void), | |
| 1018 | stdin: Sink.drain, | |
| 1019 | stdout: spawned.args.includes("status") | |
| 1020 | ? Stream.make( | |
| 1021 | new TextEncoder().encode( | |
| 1022 | JSON.stringify({ Self: { DNSName: "host.example.ts.net." } }), | |
| 1023 | ), | |
| 1024 | ) | |
| 1025 | : Stream.empty, | |
| 1026 | stderr: Stream.empty, | |
| 1027 | all: Stream.empty, | |
| 1028 | getInputFd: () => Sink.drain, | |
| 1029 | getOutputFd: () => Stream.empty, | |
| 1030 | }), | |
| 1031 | ); | |
| 1032 | }), | |
| 1033 | ); | |
| 1034 | ||
| 1035 | yield* runDevRunnerWithInput({ | |
| 1036 | ...devServerInput, | |
| 1037 | mode: "dev", | |
| 1038 | port: undefined, | |
| 1039 | share: true, | |
| 1040 | }).pipe( | |
| 1041 | Effect.provide(Layer.mergeAll(emptyConfigLayer, netServiceLayer, spawnerLayer)), | |
| 1042 | Effect.provideService(HostProcessPlatform, "linux"), | |
| 1043 | Effect.provideService( | |
| 1044 | HostProcessEnvironment, | |
| 1045 | input.ambientBundledDev === undefined | |
| 1046 | ? {} | |
| 1047 | : { T3CODE_BUNDLED_DEV: input.ambientBundledDev }, | |
| 1048 | ), | |
| 1049 | ); | |
| 1050 | ||
| 1051 | return captured; | |
| 1052 | }); | |
| 1053 | ||
| 1054 | it.effect("defaults T3CODE_BUNDLED_DEV=1 for a shared run", () => | |
| 1055 | Effect.gen(function* () { | |
| 1056 | const env = yield* shareSpawnedEnv({ ambientBundledDev: undefined }); | |
| 1057 | assert.equal(env?.T3CODE_BUNDLED_DEV, "1"); | |
| 1058 | }), | |
| 1059 | ); | |
| 1060 | ||
| 1061 | it.effect("keeps an explicit T3CODE_BUNDLED_DEV=0 opt-out", () => | |
| 1062 | Effect.gen(function* () { | |
| 1063 | const env = yield* shareSpawnedEnv({ ambientBundledDev: "0" }); | |
| 1064 | assert.equal(env?.T3CODE_BUNDLED_DEV, "0"); | |
| 1065 | }), | |
| 1066 | ); | |
| 1067 | ||
| 1068 | it.effect("leaves T3CODE_BUNDLED_DEV unset without --share", () => | |
| 1069 | Effect.gen(function* () { | |
| 1070 | let captured: Record<string, string | undefined> | undefined; | |
| 1071 | const spawnerLayer = Layer.succeed( | |
| 1072 | ChildProcessSpawner.ChildProcessSpawner, | |
| 1073 | ChildProcessSpawner.make((command) => { | |
| 1074 | captured = ( | |
| 1075 | command as { | |
| 1076 | readonly options?: { readonly env?: Record<string, string | undefined> }; | |
| 1077 | } | |
| 1078 | ).options?.env; | |
| 1079 | return Effect.succeed(mockProcess(0)); | |
| 1080 | }), | |
| 1081 | ); | |
| 1082 | ||
| 1083 | yield* runDevRunnerWithInput({ | |
| 1084 | ...devServerInput, | |
| 1085 | mode: "dev", | |
| 1086 | port: undefined, | |
| 1087 | }).pipe( | |
| 1088 | Effect.provide(Layer.mergeAll(emptyConfigLayer, netServiceLayer, spawnerLayer)), | |
| 1089 | Effect.provideService(HostProcessPlatform, "linux"), | |
| 1090 | Effect.provideService(HostProcessEnvironment, {}), | |
| 1091 | ); | |
| 1092 | ||
| 1093 | assert.equal(captured?.T3CODE_BUNDLED_DEV, undefined); | |
| 1094 | }), | |
| 1095 | ); | |
| 1096 | }); | |
| 1097 | ||
| 989 | 1098 | it.effect("spawns nothing when --dry-run is combined with --share", () => { |
| 990 | 1099 | let spawnCount = 0; |
| 991 | 1100 | const spawnerLayer = Layer.succeed( |
scripts/dev-runner.ts+8| ... | ... | @@ -780,6 +780,14 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) { |
| 780 | 780 | if (input.devUrl === undefined) { |
| 781 | 781 | env.VITE_DEV_SERVER_URL = shared.url; |
| 782 | 782 | } |
| 783 | // A shared origin serves a remote browser, where unbundled dev's | |
| 784 | // per-module requests each pay a tailnet round trip — a cold module | |
| 785 | // graph takes minutes to first paint. Bundled dev collapses that to | |
| 786 | // a few chunk requests. Only defaulted, so T3CODE_BUNDLED_DEV=0 | |
| 787 | // still opts a --share run back out. | |
| 788 | if (env.T3CODE_BUNDLED_DEV === undefined) { | |
| 789 | env.T3CODE_BUNDLED_DEV = "1"; | |
| 790 | } | |
| 783 | 791 | yield* Effect.logInfo(`[dev-runner] shared on tailnet: ${shared.url}`); |
| 784 | 792 | } |
| 785 | 793 | } |
t3.json+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | "scripts": [ |
| 5 | 5 | { |
| 6 | 6 | "name": "Setup Worktree", |
| 7 | "command": "vp i && ln -sf $T3CODE_PROJECT_ROOT/.env .env && ln -sf $T3CODE_PROJECT_ROOT/infra/relay/.env infra/relay/.env", | |
| 7 | "command": "vp i && ln -sf $T3CODE_PROJECT_ROOT/.env .env && ln -sf $T3CODE_PROJECT_ROOT/infra/relay/.env infra/relay/.env && node apps/web/scripts/warm-dep-cache.ts", | |
| 8 | 8 | "icon": "configure", |
| 9 | 9 | "runOnWorktreeCreate": true |
| 10 | 10 | } |