From dc74102551fe8861b18fe8a30116745e72a52370 Mon Sep 17 00:00:00 2001 From: Ian Boyes Date: Thu, 2 Jul 2026 14:00:44 -0700 Subject: [PATCH 1/3] chore: split browser and server TypeScript environments in apps/web The single apps/web tsconfig pulled @types/node into global scope for the whole program, so browser code type-checked against Node's ambient types instead of the DOM. Introduce two type-check projects: - tsconfig.server.json emits declarations for src/server (and the server entry files) with Node types. - tsconfig.app.json checks browser code with a DOM lib and no Node types, resolving @server/* to the emitted declarations so server source no longer enters the browser program. start.ts is reachable from the client via routeTree.gen.ts, so it must be browser-safe: switch its relative ./server/* imports to the @server alias (resolved to declarations in the browser project) and replace node:crypto with the Web Crypto API. The upload @ts-expect-error stays: superagent's .attach types are Node-flavored and its deps force @types/node globals into the browser program via `reference types="node"`. This split does not remove that; it goes away when superagent is dropped for server functions. --- .gitignore | 2 ++ apps/web/package.json | 2 +- apps/web/src/start.ts | 22 +++++++++---------- apps/web/src/uploads/uploader.ts | 9 +++++--- apps/web/tsconfig.app.json | 37 ++++++++++++++++++++++++++++++++ apps/web/tsconfig.server.json | 23 ++++++++++++++++++++ 6 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 apps/web/tsconfig.app.json create mode 100644 apps/web/tsconfig.server.json diff --git a/.gitignore b/.gitignore index 0076a3a3e..e5d3327c6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ __pycache__ /build node_modules dist +dist-types +*.tsbuildinfo .output .nitro doc diff --git a/apps/web/package.json b/apps/web/package.json index e9503ff91..d92648f68 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,7 +9,7 @@ "develop": "vite", "start": "node scripts/check-api.mjs && node .output/server/index.mjs", "test": "TZ=UTC vitest", - "typecheck": "tsc --noEmit" + "typecheck": "tsc -p tsconfig.server.json && tsc -p tsconfig.app.json" }, "dependencies": { "@hookform/resolvers": "^5.4.0", diff --git a/apps/web/src/start.ts b/apps/web/src/start.ts index f8d980bc9..0598e38cf 100644 --- a/apps/web/src/start.ts +++ b/apps/web/src/start.ts @@ -1,22 +1,20 @@ -import { randomBytes } from "node:crypto"; import { sentryGlobalFunctionMiddleware, sentryGlobalRequestMiddleware, } from "@sentry/tanstackstart-react"; -import { - createCsrfMiddleware, - createMiddleware, - createStart, -} from "@tanstack/react-start"; - import { createFirstUserFn, loginFn, logoutFn, resetPasswordFn, -} from "./server/auth/functions"; -import { createAuthenticationMiddleware } from "./server/auth/middleware"; -import { errorLoggingMiddleware } from "./server/error-logging"; +} from "@server/auth/functions"; +import { createAuthenticationMiddleware } from "@server/auth/middleware"; +import { errorLoggingMiddleware } from "@server/error-logging"; +import { + createCsrfMiddleware, + createMiddleware, + createStart, +} from "@tanstack/react-start"; // logoutFn must be exempt so stale or missing cookies can still be cleared. // createFirstUserFn runs before any user or session exists. @@ -83,7 +81,9 @@ const documentHeadersMiddleware = createMiddleware().server( } const html = await response.text(); - const nonce = randomBytes(16).toString("base64"); + const nonce = btoa( + String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))), + ); const body = html.replace(/])/g, `