diff --git a/apps/web/src/components/input/chip-input.tsx b/apps/web/src/components/input/chip-input.tsx
index 196ba6428..45a59da70 100644
--- a/apps/web/src/components/input/chip-input.tsx
+++ b/apps/web/src/components/input/chip-input.tsx
@@ -59,8 +59,6 @@ function ChipDragOverlay({ activeChip }: ChipDragOverlayProps) {
{activeChip ? : null}
);
- if (typeof document === "undefined") return overlay;
-
return createPortal(overlay, document.body);
}
diff --git a/apps/web/src/components/input/rich-text-extensions.ts b/apps/web/src/components/input/rich-text-extensions.ts
index d85265f34..f4ce6fc09 100644
--- a/apps/web/src/components/input/rich-text-extensions.ts
+++ b/apps/web/src/components/input/rich-text-extensions.ts
@@ -359,7 +359,6 @@ const parsedTablesMatchSource = (sourceTables: readonly string[], tables: readon
};
export const hasUnsupportedTableMarkup = (html: string) => {
- if (typeof DOMParser === "undefined") return false;
const tableMarkers = tableMarkersFrom(html);
if (tableMarkers.length === 0) return false;
const document = new DOMParser().parseFromString(html, "text/html");
diff --git a/apps/web/src/features/auth/pages/login.tsx b/apps/web/src/features/auth/pages/login.tsx
index 78a60cebb..b7ce6bf07 100644
--- a/apps/web/src/features/auth/pages/login.tsx
+++ b/apps/web/src/features/auth/pages/login.tsx
@@ -95,7 +95,6 @@ export function LoginPage({ disableEmailAuth, disableSignups }: Props) {
useEffect(() => {
if (!("passkey" in providers)) return;
- if (typeof window === "undefined") return;
if (!("PublicKeyCredential" in window)) return;
if (!PublicKeyCredential.isConditionalMediationAvailable) return;
if (hasStartedConditionalPasskeyRef.current) return;
diff --git a/apps/web/src/features/resume/builder/draft.ts b/apps/web/src/features/resume/builder/draft.ts
index 32b3c9625..3d4e8dbd2 100644
--- a/apps/web/src/features/resume/builder/draft.ts
+++ b/apps/web/src/features/resume/builder/draft.ts
@@ -182,7 +182,6 @@ function getResumeQueryKey(id: string): QueryKey {
}
export function isEditableElementFocused(): boolean {
- if (typeof document === "undefined") return false;
const element = document.activeElement as HTMLElement | null;
if (!element) return false;
return (
@@ -211,7 +210,7 @@ function applyDeferredRemoteResume(id: string) {
const resume = runtime.deferredRemoteResume;
runtime.deferredRemoteResume = undefined;
- if (runtime.deferredFocusHandler && typeof document !== "undefined") {
+ if (runtime.deferredFocusHandler) {
document.removeEventListener("focusout", runtime.deferredFocusHandler, true);
runtime.deferredFocusHandler = undefined;
}
@@ -228,7 +227,7 @@ function deferRemoteResumeUntilBlur(id: string, resume: Resume) {
const runtime = getRuntime(id);
runtime.deferredRemoteResume = resume;
- if (runtime.deferredFocusHandler || typeof document === "undefined") return;
+ if (runtime.deferredFocusHandler) return;
const handler = () => {
// Let focus settle (e.g. tabbing between fields) before deciding editing has ended.
@@ -337,17 +336,15 @@ function createRuntime(): Runtime {
sessionId: crypto.randomUUID(),
};
- if (typeof window !== "undefined") {
- runtime.beforeUnloadHandler = () => runtime.syncResume.flush();
- window.addEventListener("beforeunload", runtime.beforeUnloadHandler);
- // Changes made offline are sent as soon as the connection comes back.
- runtime.onlineHandler = () => {
- const current = useResumeStore.getState().resume;
- if (!runtime.hasPendingLocalChanges || !current) return;
- queueResumeSave(current);
- };
- window.addEventListener("online", runtime.onlineHandler);
- }
+ runtime.beforeUnloadHandler = () => runtime.syncResume.flush();
+ window.addEventListener("beforeunload", runtime.beforeUnloadHandler);
+ // Changes made offline are sent as soon as the connection comes back.
+ runtime.onlineHandler = () => {
+ const current = useResumeStore.getState().resume;
+ if (!runtime.hasPendingLocalChanges || !current) return;
+ queueResumeSave(current);
+ };
+ window.addEventListener("online", runtime.onlineHandler);
return runtime;
}
@@ -376,15 +373,15 @@ function cleanupRuntime(id: string) {
runtime.syncResume.flush();
runtime.abortController.abort();
- if (runtime.beforeUnloadHandler && typeof window !== "undefined") {
+ if (runtime.beforeUnloadHandler) {
window.removeEventListener("beforeunload", runtime.beforeUnloadHandler);
}
- if (runtime.onlineHandler && typeof window !== "undefined") {
+ if (runtime.onlineHandler) {
window.removeEventListener("online", runtime.onlineHandler);
}
- if (runtime.deferredFocusHandler && typeof document !== "undefined") {
+ if (runtime.deferredFocusHandler) {
document.removeEventListener("focusout", runtime.deferredFocusHandler, true);
}
diff --git a/apps/web/src/features/resume/editor/write/picture-settings.tsx b/apps/web/src/features/resume/editor/write/picture-settings.tsx
index 236800382..5df9f4ea6 100644
--- a/apps/web/src/features/resume/editor/write/picture-settings.tsx
+++ b/apps/web/src/features/resume/editor/write/picture-settings.tsx
@@ -489,7 +489,7 @@ type CropState = {
/** Every photo option: upload with crop, address, show or hide, delete, fit, size, rotation, shape, border and shadow. */
export function PictureSettings() {
const fileInputRef = useRef(null);
- const appOrigin = typeof window === "undefined" ? "" : window.location.origin;
+ const appOrigin = window.location.origin;
const [cropState, setCropState] = useState(null);
const [crop, setCrop] = useState({ x: 0, y: 0 });
diff --git a/apps/web/src/features/resume/preview/preview.shared.utils.ts b/apps/web/src/features/resume/preview/preview.shared.utils.ts
index bf7df3686..ad7139e82 100644
--- a/apps/web/src/features/resume/preview/preview.shared.utils.ts
+++ b/apps/web/src/features/resume/preview/preview.shared.utils.ts
@@ -15,7 +15,7 @@ export const DEFAULT_PDF_PAGE_SIZE: PreviewPageSize = {
};
export const getPreviewCanvasScale = (width: number, height: number) => {
- const devicePixelRatio = typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
+ const devicePixelRatio = window.devicePixelRatio || 1;
const desiredScale = Math.max(PDF_PAGE_RENDER_SCALE, devicePixelRatio);
const desiredPixels = width * height * desiredScale * desiredScale;
diff --git a/apps/web/src/features/resume/preview/resume-accessible-text.tsx b/apps/web/src/features/resume/preview/resume-accessible-text.tsx
index 627d82caa..5ba71338f 100644
--- a/apps/web/src/features/resume/preview/resume-accessible-text.tsx
+++ b/apps/web/src/features/resume/preview/resume-accessible-text.tsx
@@ -122,7 +122,6 @@ function renderRichTextNode(node: ChildNode, key: string): ReactNode {
function RichText({ html }: { html: string }) {
if (!html.trim()) return null;
- if (typeof DOMParser === "undefined") return stripHtml(html);
const body = new DOMParser().parseFromString(html, "text/html").body;
return Array.from(body.childNodes).map((node, index) => renderRichTextNode(node, `rich-text-${index}`));
@@ -130,7 +129,6 @@ function RichText({ html }: { html: string }) {
function hasRenderableRichText(html: string): boolean {
if (!html.trim()) return false;
- if (typeof DOMParser === "undefined") return stripHtml(html).trim().length > 0;
const body = new DOMParser().parseFromString(html, "text/html").body;
const getText = (node: ChildNode): string => {
diff --git a/apps/web/src/features/settings/account/profile.tsx b/apps/web/src/features/settings/account/profile.tsx
index da9a04875..3461f3663 100644
--- a/apps/web/src/features/settings/account/profile.tsx
+++ b/apps/web/src/features/settings/account/profile.tsx
@@ -128,7 +128,7 @@ export function ProfileSection({ session }: ProfileSectionProps) {
const context = useRouteContext({ strict: false });
const smtpEnabled = context.flags?.smtpEnabled ?? false;
const user = session.user;
- const host = typeof window === "undefined" ? "" : window.location.host;
+ const host = window.location.host;
const updateUser = async (patch: Parameters[0]) => {
const { error } = await authClient.updateUser(patch);
diff --git a/apps/web/src/features/settings/ai/mcp.tsx b/apps/web/src/features/settings/ai/mcp.tsx
index 94067419c..bf61f3c42 100644
--- a/apps/web/src/features/settings/ai/mcp.tsx
+++ b/apps/web/src/features/settings/ai/mcp.tsx
@@ -5,7 +5,7 @@ import { Button } from "@reactive-resume/ui/components/button";
import { SettingsSection } from "../section";
export function McpSection() {
- const address = typeof window === "undefined" ? "/mcp" : `${window.location.origin}/mcp`;
+ const address = `${window.location.origin}/mcp`;
const [copied, setCopied] = useState(false);
const [, copy] = useCopyToClipboard();
diff --git a/apps/web/src/libs/auth/client.ts b/apps/web/src/libs/auth/client.ts
index eb8241bd0..87d954d75 100644
--- a/apps/web/src/libs/auth/client.ts
+++ b/apps/web/src/libs/auth/client.ts
@@ -18,16 +18,14 @@ export const authClient = createAuthClient({
twoFactorClient({
onTwoFactorRedirect() {
// Redirect to 2FA verification page
- if (typeof window !== "undefined") {
- const { callbackURL, reauthenticate } = authSearchSchema.parse({
- reauthenticate: new URLSearchParams(window.location.search).get("reauthenticate") === "true",
- callbackURL: new URLSearchParams(window.location.search).get("callbackURL"),
- });
- const search = callbackURL
- ? `?${new URLSearchParams({ callbackURL, ...(reauthenticate ? { reauthenticate: "true" } : {}) })}`
- : "";
- window.location.href = `/auth/verify-2fa${search}`;
- }
+ const { callbackURL, reauthenticate } = authSearchSchema.parse({
+ reauthenticate: new URLSearchParams(window.location.search).get("reauthenticate") === "true",
+ callbackURL: new URLSearchParams(window.location.search).get("callbackURL"),
+ });
+ const search = callbackURL
+ ? `?${new URLSearchParams({ callbackURL, ...(reauthenticate ? { reauthenticate: "true" } : {}) })}`
+ : "";
+ window.location.href = `/auth/verify-2fa${search}`;
},
}),
oauthProviderClient(),
diff --git a/apps/web/src/libs/orpc/client.ts b/apps/web/src/libs/orpc/client.ts
index 6354346e4..b4efb581b 100644
--- a/apps/web/src/libs/orpc/client.ts
+++ b/apps/web/src/libs/orpc/client.ts
@@ -6,14 +6,11 @@ import { BatchLinkPlugin } from "@orpc/client/plugins";
import { createTanstackQueryUtils } from "@orpc/tanstack-query";
import { rpcFetch } from "./fetch";
-const getRpcUrl = () => {
- if (typeof window === "undefined") return "http://localhost:3000/api/rpc";
- return `${window.location.origin}/api/rpc`;
-};
+const getRpcUrl = () => `${window.location.origin}/api/rpc`;
export const client: RouterClient = createORPCClient(
new RPCLink({
- url: getRpcUrl(),
+ url: getRpcUrl,
fetch: rpcFetch,
plugins: [
new BatchLinkPlugin({
@@ -32,7 +29,7 @@ export const client: RouterClient = createORPCClient(
export const streamClient: RouterClient = createORPCClient(
new RPCLink({
- url: getRpcUrl(),
+ url: getRpcUrl,
fetch: rpcFetch,
interceptors: [
onError((error) => {
diff --git a/apps/web/src/libs/seo.test.ts b/apps/web/src/libs/seo.test.ts
index 5e0fdb8c5..0f0d8defb 100644
--- a/apps/web/src/libs/seo.test.ts
+++ b/apps/web/src/libs/seo.test.ts
@@ -7,10 +7,6 @@ import {
} from "./seo";
describe("getCanonicalRootUrl", () => {
- it("uses the production root when no origin is available", () => {
- expect(getCanonicalRootUrl()).toBe("https://rxresu.me/");
- });
-
it("normalizes an app origin to the root URL", () => {
expect(getCanonicalRootUrl("http://localhost:3000")).toBe("http://localhost:3000/");
expect(getCanonicalRootUrl("https://rxresu.me/")).toBe("https://rxresu.me/");
diff --git a/apps/web/src/libs/seo.ts b/apps/web/src/libs/seo.ts
index c52f563ec..20e52cf69 100644
--- a/apps/web/src/libs/seo.ts
+++ b/apps/web/src/libs/seo.ts
@@ -1,19 +1,9 @@
-const productionRootUrl = "https://rxresu.me/";
const appName = "Reactive Resume";
const repositoryUrl = "https://github.com/reactive-resume/reactive-resume";
type JsonLd = Record;
-export const getCanonicalRootUrl = (origin?: string): string => {
- if (!origin) return productionRootUrl;
-
- const url = new URL(origin);
- url.pathname = "/";
- url.search = "";
- url.hash = "";
-
- return url.toString();
-};
+export const getCanonicalRootUrl = (origin: string): string => new URL("/", origin).href;
export const createNoindexFollowMeta = () => ({ name: "robots", content: "noindex, follow" });
diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx
index 1de1789cf..de9ad48d2 100644
--- a/apps/web/src/main.tsx
+++ b/apps/web/src/main.tsx
@@ -21,8 +21,4 @@ document.querySelectorAll(serverSeoSelectors.join(",")).forEach((element) => {
element.remove();
});
-if (!rootElement.innerHTML) {
- const root = ReactDOM.createRoot(rootElement);
-
- root.render();
-}
+ReactDOM.createRoot(rootElement).render();
diff --git a/apps/web/src/routes/$username/$slug.tsx b/apps/web/src/routes/$username/$slug.tsx
index c6af9ab68..f775e9dc0 100644
--- a/apps/web/src/routes/$username/$slug.tsx
+++ b/apps/web/src/routes/$username/$slug.tsx
@@ -9,7 +9,6 @@ import { createNoindexFollowMeta, createResumeSocialMeta, getCanonicalRootUrl }
type LoaderData = Omit & { data: ResumeData };
export const Route = createFileRoute("/$username/$slug")({
- ssr: "data-only",
component: lazyRouteComponent(() => import("@/features/resume/public/public-resume"), "PublicResumeRoute"),
notFoundComponent: lazyRouteComponent(
() => import("@/features/resume/public/public-resume"),
@@ -38,7 +37,7 @@ export const Route = createFileRoute("/$username/$slug")({
const social = getResumeSocialMeta(resume.data, resume.name || "Resume");
- const base = getCanonicalRootUrl(typeof window === "undefined" ? undefined : window.location.origin);
+ const base = getCanonicalRootUrl(window.location.origin);
const canonicalUrl = `${base}${params.username}/${params.slug}`;
const imageUrl = `${base}opengraph/banner.jpg`;
diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx
index 81b38e381..72829342c 100644
--- a/apps/web/src/routes/__root.tsx
+++ b/apps/web/src/routes/__root.tsx
@@ -48,7 +48,7 @@ const iconContextValue: IconProps = { size: 16, weight: "regular" };
export const Route = createRootRouteWithContext()({
component: RootComponent,
head: () => {
- const appUrl = typeof window !== "undefined" ? window.location.origin : "https://rxresu.me";
+ const appUrl = window.location.origin;
return {
links: [
diff --git a/apps/web/src/routes/_home/-index.test.ts b/apps/web/src/routes/_home/-index.test.ts
index 4b0b9abb9..c6b4815dc 100644
--- a/apps/web/src/routes/_home/-index.test.ts
+++ b/apps/web/src/routes/_home/-index.test.ts
@@ -1,3 +1,5 @@
+// @vitest-environment happy-dom
+
import { describe, expect, it, vi } from "vitest";
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
diff --git a/apps/web/src/routes/_home/ats-checker.tsx b/apps/web/src/routes/_home/ats-checker.tsx
index 5b3531f9e..4974a9402 100644
--- a/apps/web/src/routes/_home/ats-checker.tsx
+++ b/apps/web/src/routes/_home/ats-checker.tsx
@@ -17,7 +17,7 @@ export const Route = createFileRoute("/_home/ats-checker")({
// Back from signing up with a checked file to import.
validateSearch: z.object({ import: z.coerce.boolean().optional().catch(undefined) }),
head: () => {
- const origin = typeof window === "undefined" ? "https://rxresu.me" : window.location.origin;
+ const origin = window.location.origin;
const canonicalUrl = new URL("/ats-checker", origin).toString();
const imageUrl = new URL("/opengraph/ats-checker.png", origin).toString();
diff --git a/apps/web/src/routes/_home/index.tsx b/apps/web/src/routes/_home/index.tsx
index 08cafbbda..66fffac02 100644
--- a/apps/web/src/routes/_home/index.tsx
+++ b/apps/web/src/routes/_home/index.tsx
@@ -1,7 +1,6 @@
import { ORPCError } from "@orpc/client";
-import { ClientOnly, createFileRoute, lazyRouteComponent, redirect } from "@tanstack/react-router";
+import { createFileRoute, lazyRouteComponent, redirect } from "@tanstack/react-router";
import { getResumeSocialMeta } from "@reactive-resume/resume/social-meta";
-import { LoadingScreen } from "@/components/layout/loading-screen";
import { NotFoundScreen } from "@/components/layout/not-found-screen";
import { Homepage } from "@/features/homepage/page";
import { orpc } from "@/libs/orpc/client";
@@ -50,7 +49,7 @@ export const Route = createFileRoute("/_home/")({
links: [{ rel: "canonical", href: canonicalUrl }],
};
}
- const appUrl = typeof window !== "undefined" ? window.location.origin : "https://rxresu.me";
+ const appUrl = window.location.origin;
const canonicalUrl = getCanonicalRootUrl(appUrl);
return {
@@ -70,11 +69,7 @@ function RouteComponent() {
);
if (root.status === "public") {
- return (
- }>
-
-
- );
+ return ;
}
return ;
diff --git a/apps/web/src/routes/auth/consent.tsx b/apps/web/src/routes/auth/consent.tsx
index aa3f12723..774c953d7 100644
--- a/apps/web/src/routes/auth/consent.tsx
+++ b/apps/web/src/routes/auth/consent.tsx
@@ -2,13 +2,12 @@ import { createFileRoute, redirect } from "@tanstack/react-router";
import { OAuthConsentPage } from "@/features/auth/pages/consent";
export const Route = createFileRoute("/auth/consent")({
- ssr: false,
- beforeLoad: ({ context, location }) => {
+ beforeLoad: ({ context }) => {
if (!context.session) {
throw redirect({
to: "/auth/login",
search: {
- callbackURL: `/api/auth/oauth${typeof window === "undefined" ? location.searchStr : window.location.search}`,
+ callbackURL: `/api/auth/oauth${window.location.search}`,
},
});
}