fix(deps)!: update tanstack-query monorepo (#126)

* fix(deps): update tanstack-query monorepo to ^5.21.2

* fix(deps): update tanstack-query monorepo

* fix: type issue with transformer

* fix: issues with next-auth, updated to next canary

* chore: fix type issue in trpc route

* chore: fix formatting

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
This commit is contained in:
homarr-renovate[bot]
2024-02-17 12:52:25 +01:00
committed by GitHub
parent 3bdd117659
commit 71521c0768
21 changed files with 5335 additions and 2398 deletions

View File

@@ -29,9 +29,9 @@
"@mantine/modals": "^7.5.3", "@mantine/modals": "^7.5.3",
"@mantine/tiptap": "^7.5.3", "@mantine/tiptap": "^7.5.3",
"@t3-oss/env-nextjs": "^0.9.2", "@t3-oss/env-nextjs": "^0.9.2",
"@tanstack/react-query": "^5.20.5", "@tanstack/react-query": "^5.21.2",
"@tanstack/react-query-devtools": "^5.21.0", "@tanstack/react-query-devtools": "^5.21.3",
"@tanstack/react-query-next-experimental": "5.20.5", "@tanstack/react-query-next-experimental": "5.21.2",
"@tiptap/extension-link": "^2.2.3", "@tiptap/extension-link": "^2.2.3",
"@tiptap/react": "^2.2.3", "@tiptap/react": "^2.2.3",
"@tiptap/starter-kit": "^2.2.3", "@tiptap/starter-kit": "^2.2.3",
@@ -43,7 +43,7 @@
"@homarr/gridstack": "^1.0.0", "@homarr/gridstack": "^1.0.0",
"jotai": "^2.6.4", "jotai": "^2.6.4",
"mantine-modal-manager": "^7.5.2", "mantine-modal-manager": "^7.5.2",
"next": "^14.1.0", "next": "^14.1.1-canary.58",
"postcss-preset-mantine": "^1.13.0", "postcss-preset-mantine": "^1.13.0",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View File

@@ -14,7 +14,7 @@ export default async function EditIntegrationPage({
params, params,
}: EditIntegrationPageProps) { }: EditIntegrationPageProps) {
const t = await getScopedI18n("integration.page.edit"); const t = await getScopedI18n("integration.page.edit");
const integration = await api.integration.byId.query({ id: params.id }); const integration = await api.integration.byId({ id: params.id });
return ( return (
<Container> <Container>

View File

@@ -47,7 +47,7 @@ interface IntegrationsPageProps {
export default async function IntegrationsPage({ export default async function IntegrationsPage({
searchParams, searchParams,
}: IntegrationsPageProps) { }: IntegrationsPageProps) {
const integrations = await api.integration.all.query(); const integrations = await api.integration.all();
const t = await getScopedI18n("integration"); const t = await getScopedI18n("integration");
return ( return (

View File

@@ -1,5 +1,6 @@
"use client"; "use client";
import type { PropsWithChildren } from "react";
import { useState } from "react"; import { useState } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
@@ -9,19 +10,7 @@ import superjson from "superjson";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
import { env } from "~/env.mjs"; export function TRPCReactProvider(props: PropsWithChildren) {
const getBaseUrl = () => {
if (typeof window !== "undefined") return ""; // browser should use relative url
if (env.VERCEL_URL) return env.VERCEL_URL; // SSR should use vercel url
return `http://localhost:${env.PORT}`; // dev SSR should use localhost
};
export function TRPCReactProvider(props: {
children: React.ReactNode;
headers?: Headers;
}) {
const [queryClient] = useState( const [queryClient] = useState(
() => () =>
new QueryClient({ new QueryClient({
@@ -35,7 +24,6 @@ export function TRPCReactProvider(props: {
const [trpcClient] = useState(() => const [trpcClient] = useState(() =>
clientApi.createClient({ clientApi.createClient({
transformer: superjson,
links: [ links: [
loggerLink({ loggerLink({
enabled: (opts) => enabled: (opts) =>
@@ -43,11 +31,12 @@ export function TRPCReactProvider(props: {
(opts.direction === "down" && opts.result instanceof Error), (opts.direction === "down" && opts.result instanceof Error),
}), }),
unstable_httpBatchStreamLink({ unstable_httpBatchStreamLink({
url: `${getBaseUrl()}/api/trpc`, transformer: superjson,
url: getBaseUrl() + "/api/trpc",
headers() { headers() {
const headers = new Map(props.headers); const headers = new Headers();
headers.set("x-trpc-source", "nextjs-react"); headers.set("x-trpc-source", "nextjs-react");
return Object.fromEntries(headers); return headers;
}, },
}), }),
], ],
@@ -65,3 +54,9 @@ export function TRPCReactProvider(props: {
</clientApi.Provider> </clientApi.Provider>
); );
} }
function getBaseUrl() {
if (typeof window !== "undefined") return window.location.origin;
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
return `http://localhost:${process.env.PORT ?? 3000}`;
}

View File

@@ -3,6 +3,6 @@ import { createBoardPage } from "../_creator";
export default createBoardPage<{ locale: string }>({ export default createBoardPage<{ locale: string }>({
async getInitialBoard() { async getInitialBoard() {
return await api.board.default.query(); return await api.board.default();
}, },
}); });

View File

@@ -3,6 +3,6 @@ import { createBoardPage } from "../_creator";
export default createBoardPage<{ locale: string; name: string }>({ export default createBoardPage<{ locale: string; name: string }>({
async getInitialBoard({ name }) { async getInitialBoard({ name }) {
return await api.board.byName.query({ name }); return await api.board.byName({ name });
}, },
}); });

View File

@@ -28,7 +28,7 @@ interface Props {
} }
export default async function BoardSettingsPage({ params }: Props) { export default async function BoardSettingsPage({ params }: Props) {
const board = await api.board.byName.query({ name: params.name }); const board = await api.board.byName({ name: params.name });
const t = await getScopedI18n("board.setting"); const t = await getScopedI18n("board.setting");
return ( return (

View File

@@ -1,11 +1,9 @@
import type { Metadata } from "next"; import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "@homarr/ui/styles.css";
import "@homarr/notifications/styles.css"; import "@homarr/notifications/styles.css";
import "@homarr/spotlight/styles.css"; import "@homarr/spotlight/styles.css";
import "@homarr/ui/styles.css";
import { headers } from "next/headers";
import { Notifications } from "@homarr/notifications"; import { Notifications } from "@homarr/notifications";
import { import {
@@ -23,16 +21,28 @@ const fontSans = Inter({
variable: "--font-sans", variable: "--font-sans",
}); });
/**
* Since we're passing `headers()` to the `TRPCReactProvider` we need to
* make the entire app dynamic. You can move the `TRPCReactProvider` further
* down the tree (e.g. /dashboard and onwards) to make part of the app statically rendered.
*/
export const dynamic = "force-dynamic";
export const metadata: Metadata = { export const metadata: Metadata = {
metadataBase: new URL("http://localhost:3000"),
title: "Create T3 Turbo", title: "Create T3 Turbo",
description: "Simple monorepo with shared backend for web & mobile apps", description: "Simple monorepo with shared backend for web & mobile apps",
openGraph: {
title: "Create T3 Turbo",
description: "Simple monorepo with shared backend for web & mobile apps",
url: "https://create-t3-turbo.vercel.app",
siteName: "Create T3 Turbo",
},
twitter: {
card: "summary_large_image",
site: "@jullerino",
creator: "@jullerino",
},
};
export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
}; };
export default function Layout(props: { export default function Layout(props: {
@@ -42,12 +52,12 @@ export default function Layout(props: {
const colorScheme = "dark"; const colorScheme = "dark";
return ( return (
<html lang="en"> <html lang="en" suppressHydrationWarning>
<head> <head>
<ColorSchemeScript defaultColorScheme={colorScheme} /> <ColorSchemeScript defaultColorScheme={colorScheme} />
</head> </head>
<body className={["font-sans", fontSans.variable].join(" ")}> <body className={["font-sans", fontSans.variable].join(" ")}>
<TRPCReactProvider headers={headers()}> <TRPCReactProvider>
<NextInternationalProvider locale={props.params.locale}> <NextInternationalProvider locale={props.params.locale}>
<MantineProvider <MantineProvider
defaultColorScheme={colorScheme} defaultColorScheme={colorScheme}

View File

@@ -10,7 +10,7 @@ import { DeleteBoardButton } from "./_components/delete-board-button";
export default async function ManageBoardsPage() { export default async function ManageBoardsPage() {
const t = await getScopedI18n("management.page.board"); const t = await getScopedI18n("management.page.board");
const boards = await api.board.getAll.query(); const boards = await api.board.getAll();
return ( return (
<> <>

View File

@@ -28,7 +28,7 @@ const handler = auth(async (req) => {
router: appRouter, router: appRouter,
req, req,
createContext: () => createContext: () =>
createTRPCContext({ auth: req.auth, headers: req.headers }), createTRPCContext({ session: req.auth, headers: req.headers }),
onError({ error, path }) { onError({ error, path }) {
console.error(`>>> tRPC Error on '${path}'`, error); console.error(`>>> tRPC Error on '${path}'`, error);
}, },

View File

@@ -1,12 +1,7 @@
import { cache } from "react"; import { cache } from "react";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { createTRPCClient, loggerLink, TRPCClientError } from "@trpc/client";
import { callProcedure } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import type { TRPCErrorResponse } from "@trpc/server/rpc";
import SuperJSON from "superjson";
import { appRouter, createTRPCContext } from "@homarr/api"; import { createCaller, createTRPCContext } from "@homarr/api";
import { auth } from "@homarr/auth"; import { auth } from "@homarr/auth";
/** /**
@@ -18,44 +13,9 @@ const createContext = cache(async () => {
heads.set("x-trpc-source", "rsc"); heads.set("x-trpc-source", "rsc");
return createTRPCContext({ return createTRPCContext({
auth: await auth(), session: await auth(),
headers: heads, headers: heads,
}); });
}); });
export const api = createTRPCClient<typeof appRouter>({ export const api = createCaller(createContext);
transformer: SuperJSON,
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
/**
* Custom RSC link that invokes procedures directly in the server component Don't be too afraid
* about the complexity here, it's just wrapping `callProcedure` with an observable to make it a
* valid ending link for tRPC.
*/
() =>
({ op }) =>
observable((observer) => {
createContext()
.then((ctx) => {
return callProcedure({
procedures: appRouter._def.procedures,
path: op.path,
getRawInput: () => Promise.resolve(op.input),
ctx,
type: op.type,
});
})
.then((data) => {
observer.next({ result: { data } });
observer.complete();
})
.catch((cause: TRPCErrorResponse) => {
observer.error(TRPCClientError.from(cause));
});
}),
],
});

View File

@@ -1,17 +0,0 @@
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
import type { AppRouter } from "./src/root";
export { appRouter, type AppRouter } from "./src/root";
export { createTRPCContext } from "./src/trpc";
/**
* Inference helpers for input types
* @example type HelloInput = RouterInputs['example']['hello']
**/
export type RouterInputs = inferRouterInputs<AppRouter>;
/**
* Inference helpers for output types
* @example type HelloOutput = RouterOutputs['example']['hello']
**/
export type RouterOutputs = inferRouterOutputs<AppRouter>;

View File

@@ -2,7 +2,7 @@
"name": "@homarr/api", "name": "@homarr/api",
"version": "0.1.0", "version": "0.1.0",
"exports": { "exports": {
".": "./index.ts", ".": "./src/index.ts",
"./client": "./src/client.ts" "./client": "./src/client.ts"
}, },
"private": true, "private": true,

View File

@@ -1,5 +1,5 @@
import { createTRPCReact } from "@trpc/react-query"; import { createTRPCReact } from "@trpc/react-query";
import type { AppRouter } from ".."; import type { AppRouter } from ".";
export const clientApi = createTRPCReact<AppRouter>(); export const clientApi = createTRPCReact<AppRouter>();

33
packages/api/src/index.ts Normal file
View File

@@ -0,0 +1,33 @@
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
import type { AppRouter } from "./root";
import { appRouter } from "./root";
import { createCallerFactory, createTRPCContext } from "./trpc";
/**
* Create a server-side caller for the tRPC API
* @example
* const trpc = createCaller(createContext);
* const res = await trpc.post.all();
* ^? Post[]
*/
const createCaller = createCallerFactory(appRouter);
/**
* Inference helpers for input types
* @example
* type PostByIdInput = RouterInputs['post']['byId']
* ^? { id: number }
**/
type RouterInputs = inferRouterInputs<AppRouter>;
/**
* Inference helpers for output types
* @example
* type AllPostsOutput = RouterOutputs['post']['all']
* ^? Post[]
**/
type RouterOutputs = inferRouterOutputs<AppRouter>;
export { createTRPCContext, appRouter, createCaller };
export type { AppRouter, RouterInputs, RouterOutputs };

View File

@@ -13,7 +13,7 @@ import {
} from "@homarr/db/schema/sqlite"; } from "@homarr/db/schema/sqlite";
import { createDb } from "@homarr/db/test"; import { createDb } from "@homarr/db/test";
import type { RouterOutputs } from "../../.."; import type { RouterOutputs } from "../..";
import { boardRouter } from "../board"; import { boardRouter } from "../board";
// Mock the auth module to return an empty session // Mock the auth module to return an empty session

View File

@@ -5,7 +5,7 @@ import { createId } from "@homarr/db";
import { integrations, integrationSecrets } from "@homarr/db/schema/sqlite"; import { integrations, integrationSecrets } from "@homarr/db/schema/sqlite";
import { createDb } from "@homarr/db/test"; import { createDb } from "@homarr/db/test";
import type { RouterInputs } from "../../.."; import type { RouterInputs } from "../..";
import { encryptSecret, integrationRouter } from "../integration"; import { encryptSecret, integrationRouter } from "../integration";
import { expectToBeDefined } from "./board.spec"; import { expectToBeDefined } from "./board.spec";

View File

@@ -17,49 +17,28 @@ import { ZodError } from "@homarr/validation";
/** /**
* 1. CONTEXT * 1. CONTEXT
* *
* This section defines the "contexts" that are available in the backend API * This section defines the "contexts" that are available in the backend API.
* *
* These allow you to access things like the database, the session, etc, when * These allow you to access things when processing a request, like the database, the session, etc.
* processing a request
* *
*/ * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each
interface CreateContextOptions { * wrap this and provides the required context.
session: Session | null;
}
/**
* This helper generates the "internals" for a tRPC context. If you need to use
* it, you can export it from here
* *
* Examples of things you may need it for: * @see https://trpc.io/docs/server/context
* - testing, so we dont have to mock Next.js' req/res
* - trpc's `createSSGHelpers` where we don't have req/res
* @see https://create.t3.gg/en/usage/trpc#-servertrpccontextts
*/
const createInnerTRPCContext = (opts: CreateContextOptions) => {
return {
session: opts.session,
db,
};
};
/**
* This is the actual context you'll use in your router. It will be used to
* process every request that goes through your tRPC endpoint
* @link https://trpc.io/docs/context
*/ */
export const createTRPCContext = async (opts: { export const createTRPCContext = async (opts: {
headers?: Headers; headers: Headers;
auth: Session | null; session: Session | null;
}) => { }) => {
const session = opts.auth ?? (await auth()); const session = opts.session ?? (await auth());
const source = opts.headers?.get("x-trpc-source") ?? "unknown"; const source = opts.headers.get("x-trpc-source") ?? "unknown";
console.log(">>> tRPC Request from", source, "by", session?.user); console.log(">>> tRPC Request from", source, "by", session?.user);
return createInnerTRPCContext({ return {
session, session,
}); db,
};
}; };
/** /**
@@ -70,18 +49,21 @@ export const createTRPCContext = async (opts: {
*/ */
const t = initTRPC.context<typeof createTRPCContext>().create({ const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson, transformer: superjson,
errorFormatter({ shape, error }) { errorFormatter: ({ shape, error }) => ({
return { ...shape,
...shape, data: {
data: { ...shape.data,
...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
zodError: },
error.cause instanceof ZodError ? error.cause.flatten() : null, }),
},
};
},
}); });
/**
* Create a server-side caller
* @see https://trpc.io/docs/server/server-side-calls
*/
export const createCallerFactory = t.createCallerFactory;
/** /**
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
* *

View File

@@ -7,18 +7,10 @@ export const env = createEnv({
process.env.NODE_ENV === "production" process.env.NODE_ENV === "production"
? z.string().min(1) ? z.string().min(1)
: z.string().min(1).optional(), : z.string().min(1).optional(),
AUTH_URL: z.preprocess(
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
// Since NextAuth.js automatically uses the VERCEL_URL if present.
(str) => process.env.VERCEL_URL ?? str,
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
process.env.VERCEL ? z.string() : z.string().url(),
),
}, },
client: {}, client: {},
runtimeEnv: { runtimeEnv: {
AUTH_SECRET: process.env.AUTH_SECRET, AUTH_SECRET: process.env.AUTH_SECRET,
AUTH_URL: process.env.AUTH_URL,
}, },
skipValidation: skipValidation:
Boolean(process.env.CI) || Boolean(process.env.SKIP_ENV_VALIDATION), Boolean(process.env.CI) || Boolean(process.env.SKIP_ENV_VALIDATION),

View File

@@ -24,7 +24,7 @@
"@t3-oss/env-nextjs": "^0.9.2", "@t3-oss/env-nextjs": "^0.9.2",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"cookies": "^0.9.1", "cookies": "^0.9.1",
"next": "^14.1.0", "next": "^14.1.1-canary.58",
"next-auth": "5.0.0-beta.11", "next-auth": "5.0.0-beta.11",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"

7464
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff