diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9bfef28a0..e5043e960 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,7 +14,6 @@ "@radix-ui/react-dialog": "^1.1.6", "@tanstack/react-query": "^5.66.0", "@tanstack/react-router": "^1.102.5", - "@tanstack/router-zod-adapter": "^1.81.5", "@vector-im/compound-design-tokens": "3.0.1", "@vector-im/compound-web": "^7.6.2", "@zxcvbn-ts/core": "^3.0.4", @@ -26,8 +25,8 @@ "react-dom": "^19.0.0", "react-i18next": "^15.4.0", "swagger-ui-dist": "^5.18.3", - "vaul": "^1.1.2", - "zod": "^3.24.2" + "valibot": "^1.0.0-rc.0", + "vaul": "^1.1.2" }, "devDependencies": { "@biomejs/biome": "^1.9.4", @@ -5603,23 +5602,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tanstack/router-zod-adapter": { - "version": "1.81.5", - "resolved": "https://registry.npmjs.org/@tanstack/router-zod-adapter/-/router-zod-adapter-1.81.5.tgz", - "integrity": "sha512-oJp3QaCI5YwW7H46iuivC8pJLmYboXa1OztncRZNmfVBX69FZ7DodfxdrwNzceGpN3sXZT/f0t4sV05dKsneHg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@tanstack/react-router": ">=1.43.2", - "zod": ">=3" - } - }, "node_modules/@tanstack/store": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.0.tgz", @@ -14157,6 +14139,20 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/valibot": { + "version": "1.0.0-rc.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-rc.0.tgz", + "integrity": "sha512-9ZUrOXOejY/WaIn8p0Z469R1qBAwNJeqq8jzOIDsl1qR8gqtObHQmyHLFli0UCkcGiTco5kH6/KPLWsTWE9b2g==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/value-or-function": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", @@ -14933,6 +14929,7 @@ "version": "3.24.2", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/frontend/package.json b/frontend/package.json index f27dd0523..10e4bd974 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,7 +24,6 @@ "@radix-ui/react-dialog": "^1.1.6", "@tanstack/react-query": "^5.66.0", "@tanstack/react-router": "^1.102.5", - "@tanstack/router-zod-adapter": "^1.81.5", "@vector-im/compound-design-tokens": "3.0.1", "@vector-im/compound-web": "^7.6.2", "@zxcvbn-ts/core": "^3.0.4", @@ -36,8 +35,8 @@ "react-dom": "^19.0.0", "react-i18next": "^15.4.0", "swagger-ui-dist": "^5.18.3", - "vaul": "^1.1.2", - "zod": "^3.24.2" + "valibot": "^1.0.0-rc.0", + "vaul": "^1.1.2" }, "devDependencies": { "@biomejs/biome": "^1.9.4", diff --git a/frontend/src/pagination.ts b/frontend/src/pagination.ts index 14a323e81..9468d32e4 100644 --- a/frontend/src/pagination.ts +++ b/frontend/src/pagination.ts @@ -5,7 +5,7 @@ // Please see LICENSE in the repository root for full details. import { useState } from "react"; -import * as z from "zod"; +import * as v from "valibot"; // PageInfo we get on connections from the GraphQL API type PageInfo = { @@ -18,32 +18,32 @@ type PageInfo = { export const FIRST_PAGE = Symbol("FIRST_PAGE"); const LAST_PAGE = Symbol("LAST_PAGE"); -export const anyPaginationSchema = z.object({ - first: z.number().nullish(), - after: z.string().nullish(), - last: z.number().nullish(), - before: z.string().nullish(), +export const anyPaginationSchema = v.object({ + first: v.nullish(v.number()), + after: v.nullish(v.string()), + last: v.nullish(v.number()), + before: v.nullish(v.string()), }); -const forwardPaginationSchema = z.object({ - first: z.number(), - after: z.string().nullish(), +const forwardPaginationSchema = v.object({ + first: v.number(), + after: v.nullish(v.string()), }); -const backwardPaginationSchema = z.object({ - last: z.number(), - before: z.string().nullish(), +const backwardPaginationSchema = v.object({ + last: v.number(), + before: v.nullish(v.string()), }); -const paginationSchema = z.union([ +const paginationSchema = v.union([ forwardPaginationSchema, backwardPaginationSchema, ]); -type ForwardPagination = z.infer; -type BackwardPagination = z.infer; -export type Pagination = z.infer; -export type AnyPagination = z.infer; +type ForwardPagination = v.InferOutput; +type BackwardPagination = v.InferOutput; +export type Pagination = v.InferOutput; +export type AnyPagination = v.InferOutput; // Check if the pagination is a valid pagination const isValidPagination = ( diff --git a/frontend/src/routes/_account.index.tsx b/frontend/src/routes/_account.index.tsx index 9aac0815b..bbd0a41d5 100644 --- a/frontend/src/routes/_account.index.tsx +++ b/frontend/src/routes/_account.index.tsx @@ -6,8 +6,7 @@ import { queryOptions } from "@tanstack/react-query"; import { createFileRoute, redirect } from "@tanstack/react-router"; -import { zodSearchValidator } from "@tanstack/router-zod-adapter"; -import * as z from "zod"; +import * as v from "valibot"; import { query as userEmailListQuery } from "../components/UserProfile/UserEmailList"; import { graphql } from "../gql"; import { graphqlRequest } from "../graphql"; @@ -38,39 +37,39 @@ export const query = queryOptions({ queryFn: ({ signal }) => graphqlRequest({ query: QUERY, signal }), }); -const actionSchema = z - .discriminatedUnion("action", [ - z.object({ - action: z.enum(["profile", "org.matrix.profile"]), +const actionSchema = v.variant("action", [ + v.object({ + action: v.picklist(["profile", "org.matrix.profile"]), + }), + v.object({ + action: v.picklist(["sessions_list", "org.matrix.sessions_list"]), + }), + v.object({ + action: v.picklist(["session_view", "org.matrix.session_view"]), + device_id: v.optional(v.string()), + }), + v.object({ + action: v.picklist(["session_end", "org.matrix.session_end"]), + device_id: v.optional(v.string()), + }), + v.object({ + action: v.literal("org.matrix.cross_signing_reset"), + }), + v.partial( + v.looseObject({ + action: v.never(), }), - z.object({ - action: z.enum(["sessions_list", "org.matrix.sessions_list"]), - }), - z.object({ - action: z.enum(["session_view", "org.matrix.session_view"]), - device_id: z.string().optional(), - }), - z.object({ - action: z.enum(["session_end", "org.matrix.session_end"]), - device_id: z.string().optional(), - }), - z.object({ - action: z.literal("org.matrix.cross_signing_reset"), - }), - z.object({ - action: z.undefined(), - }), - ]) - .catch({ action: undefined }); + ), +]); export const Route = createFileRoute("/_account/")({ - validateSearch: zodSearchValidator(actionSchema), + validateSearch: actionSchema, beforeLoad({ search }) { switch (search.action) { case "profile": case "org.matrix.profile": - throw redirect({ to: "/" }); + throw redirect({ to: "/", search: {} }); case "sessions_list": case "org.matrix.sessions_list": diff --git a/frontend/src/routes/_account.sessions.browsers.tsx b/frontend/src/routes/_account.sessions.browsers.tsx index 23d99b1b8..77a8c8c0b 100644 --- a/frontend/src/routes/_account.sessions.browsers.tsx +++ b/frontend/src/routes/_account.sessions.browsers.tsx @@ -5,8 +5,7 @@ // Please see LICENSE in the repository root for full details. import { createFileRoute } from "@tanstack/react-router"; -import { zodSearchValidator } from "@tanstack/router-zod-adapter"; -import * as z from "zod"; +import * as v from "valibot"; import { queryOptions } from "@tanstack/react-query"; import { graphql } from "../gql"; @@ -81,14 +80,15 @@ export const query = (pagination: AnyPagination, inactive: true | undefined) => }), }); -const searchSchema = z - .object({ - inactive: z.literal(true).optional(), - }) - .and(anyPaginationSchema); +const searchSchema = v.intersect([ + v.object({ + inactive: v.optional(v.literal(true)), + }), + anyPaginationSchema, +]); export const Route = createFileRoute("/_account/sessions/browsers")({ - validateSearch: zodSearchValidator(searchSchema), + validateSearch: searchSchema, loaderDeps: ({ search: { inactive, ...pagination } }) => ({ inactive, diff --git a/frontend/src/routes/_account.sessions.index.tsx b/frontend/src/routes/_account.sessions.index.tsx index 191896467..355b50923 100644 --- a/frontend/src/routes/_account.sessions.index.tsx +++ b/frontend/src/routes/_account.sessions.index.tsx @@ -5,8 +5,7 @@ // Please see LICENSE in the repository root for full details. import { createFileRoute } from "@tanstack/react-router"; -import { zodSearchValidator } from "@tanstack/router-zod-adapter"; -import * as z from "zod"; +import * as v from "valibot"; import { queryOptions } from "@tanstack/react-query"; import { graphql } from "../gql"; @@ -98,14 +97,15 @@ export const listQuery = ( }), }); -const searchSchema = z - .object({ - inactive: z.literal(true).optional(), - }) - .and(anyPaginationSchema); +const searchSchema = v.intersect([ + v.object({ + inactive: v.optional(v.literal(true)), + }), + anyPaginationSchema, +]); export const Route = createFileRoute("/_account/sessions/")({ - validateSearch: zodSearchValidator(searchSchema), + validateSearch: searchSchema, loaderDeps: ({ search: { inactive, ...pagination } }) => ({ inactive, diff --git a/frontend/src/routes/emails.$id.in-use.tsx b/frontend/src/routes/emails.$id.in-use.tsx index c12120f5c..a4ae1c752 100644 --- a/frontend/src/routes/emails.$id.in-use.tsx +++ b/frontend/src/routes/emails.$id.in-use.tsx @@ -48,7 +48,7 @@ function EmailInUse(): React.ReactElement { })} /> - + {t("action.back")} diff --git a/frontend/src/routes/emails.$id.verify.lazy.tsx b/frontend/src/routes/emails.$id.verify.lazy.tsx index 71c6f3d6f..169798ca6 100644 --- a/frontend/src/routes/emails.$id.verify.lazy.tsx +++ b/frontend/src/routes/emails.$id.verify.lazy.tsx @@ -189,7 +189,7 @@ function EmailVerify(): React.ReactElement { {t("frontend.verify_email.resend_code")} - + {t("action.back")} diff --git a/frontend/src/routes/password.recovery.index.tsx b/frontend/src/routes/password.recovery.index.tsx index ba1a43df0..2efa5811f 100644 --- a/frontend/src/routes/password.recovery.index.tsx +++ b/frontend/src/routes/password.recovery.index.tsx @@ -6,8 +6,7 @@ import { queryOptions } from "@tanstack/react-query"; import { createFileRoute, notFound } from "@tanstack/react-router"; -import { zodSearchValidator } from "@tanstack/router-zod-adapter"; -import * as z from "zod"; +import * as v from "valibot"; import { graphql } from "../gql"; import { graphqlRequest } from "../graphql"; @@ -31,12 +30,12 @@ export const query = (ticket: string) => graphqlRequest({ query: QUERY, signal, variables: { ticket } }), }); -const schema = z.object({ - ticket: z.string(), +const schema = v.object({ + ticket: v.string(), }); export const Route = createFileRoute("/password/recovery/")({ - validateSearch: zodSearchValidator(schema), + validateSearch: schema, loaderDeps: ({ search: { ticket } }) => ({ ticket }), diff --git a/frontend/src/routes/reset-cross-signing.tsx b/frontend/src/routes/reset-cross-signing.tsx index 2af990b6b..e657d34ed 100644 --- a/frontend/src/routes/reset-cross-signing.tsx +++ b/frontend/src/routes/reset-cross-signing.tsx @@ -8,21 +8,21 @@ import { Outlet, createFileRoute, } from "@tanstack/react-router"; -import { zodSearchValidator } from "@tanstack/router-zod-adapter"; import IconError from "@vector-im/compound-design-tokens/assets/web/icons/error"; import { Button, Text } from "@vector-im/compound-web"; -import * as z from "zod"; +import * as v from "valibot"; import { useTranslation } from "react-i18next"; import BlockList from "../components/BlockList"; import Layout from "../components/Layout"; import PageHeading from "../components/PageHeading"; -const searchSchema = z.object({ - deepLink: z.boolean().optional(), +const searchSchema = v.object({ + deepLink: v.optional(v.boolean()), }); export const Route = createFileRoute("/reset-cross-signing")({ + validateSearch: searchSchema, component: () => ( @@ -31,7 +31,6 @@ export const Route = createFileRoute("/reset-cross-signing")({ ), errorComponent: ResetCrossSigningError, - validateSearch: zodSearchValidator(searchSchema), }); function ResetCrossSigningError({