Repository: IgnisDa/ryot (language: rust)

Issue:
Time summary for video games
Hey,

I noticed that games don't have a time summary in the dashboard, and I think this would be nice to display alongside the movie time.

Thanks!


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 1a8029d590..487f7a7d3e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -909,6 +909,7 @@ dependencies = [
  "apalis",
  "chrono",
  "chrono-tz",
+ "database-models",
  "enums",
  "fitness-models",
  "media-models",
@@ -1708,7 +1709,6 @@ dependencies = [
 name = "database-utils"
 version = "0.1.0"
 dependencies = [
- "apalis",
  "application-utils",
  "async-graphql",
  "background",
@@ -1757,7 +1757,6 @@ name = "dependent-utils"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "apalis",
  "application-utils",
  "async-graphql",
  "background",
@@ -2259,7 +2258,6 @@ dependencies = [
 name = "exporter-service"
 version = "0.1.0"
 dependencies = [
- "apalis",
  "async-graphql",
  "background",
  "chrono",
@@ -2281,6 +2279,19 @@ dependencies = [
  "tokio-util",
 ]
 
+[[package]]
+name = "external-utils"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "common-utils",
+ "reqwest 0.12.8",
+ "sea-orm",
+ "serde",
+ "serde_json",
+ "tracing",
+]
+
 [[package]]
 name = "fast_chemail"
 version = "0.9.6"
@@ -2380,7 +2391,6 @@ dependencies = [
 name = "fitness-service"
 version = "0.1.0"
 dependencies = [
- "apalis",
  "application-utils",
  "async-graphql",
  "background",
@@ -3338,7 +3348,6 @@ name = "importer-service"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "apalis",
  "application-utils",
  "async-graphql",
  "background",
@@ -3355,6 +3364,7 @@ dependencies = [
  "enum_meta",
  "enums",
  "env-utils",
+ "external-utils",
  "fitness-models",
  "flate2",
  "importer-models",
@@ -3431,6 +3441,7 @@ dependencies = [
  "dependent-utils",
  "enums",
  "eventsource-stream",
+ "external-utils",
  "itertools 0.13.0",
  "media-models",
  "providers",
@@ -3843,7 +3854,6 @@ dependencies = [
 name = "miscellaneous-service"
 version = "0.1.0"
 dependencies = [
- "apalis",
  "application-utils",
  "async-graphql",
  "background",
@@ -6319,6 +6329,7 @@ name = "supporting-service"
 version = "0.1.0"
 dependencies = [
  "apalis",
+ "async-graphql",
  "background",
  "cache-service",
  "chrono-tz",
diff --git a/Cargo.toml b/Cargo.toml
index aa4d15ba9d..15aa37cba9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,7 @@ members = [
     "crates/utils/common",
     "crates/utils/database",
     "crates/utils/dependent",
+    "crates/utils/external",
     "crates/utils/env",
 ]
 resolver = "2"
diff --git a/apps/backend/src/job.rs b/apps/backend/src/job.rs
index d93dcba35c..194f2bedf1 100644
--- a/apps/backend/src/job.rs
+++ b/apps/backend/src/job.rs
@@ -96,6 +96,10 @@ pub async fn perform_application_job(
             .update_person_and_notify_users(person_id)
             .await
             .is_ok(),
+        ApplicationJob::HandleAfterMediaSeenTasks(seen) => misc_service
+            .handle_after_media_seen_tasks(seen)
+            .await
+            .is_ok(),
         ApplicationJob::UpdateMetadataGroup(metadata_group_id) => misc_service
             .update_metadata_group(&metadata_group_id)
             .await
@@ -135,6 +139,10 @@ pub async fn perform_application_job(
                 .await
                 .is_ok()
         }
+        ApplicationJob::HandleOnSeenComplete(id) => integration_service
+            .handle_on_seen_complete(id)
+            .await
+            .is_ok(),
     };
     ryot_log!(
         trace,
diff --git a/apps/frontend/app/lib/utilities.server.ts b/apps/frontend/app/lib/utilities.server.ts
index 136309c4ab..6b44bf03ab 100644
--- a/apps/frontend/app/lib/utilities.server.ts
+++ b/apps/frontend/app/lib/utilities.server.ts
@@ -17,7 +17,7 @@ import {
 } from "@ryot/generated/graphql/backend/graphql";
 import { UserDetailsDocument } from "@ryot/generated/graphql/backend/graphql";
 import { isEmpty } from "@ryot/ts-utils";
-import { type CookieSerializeOptions, parse, serialize } from "cookie";
+import { type SerializeOptions, parse, serialize } from "cookie";
 import {
 	ClientError,
 	GraphQLClient,
@@ -176,7 +176,7 @@ export const getCachedCoreDetails = async () => {
 export const getCachedUserDetails = async (request: Request) => {
 	const token = getAuthorizationCookie(request);
 	return await queryClient.ensureQueryData({
-		queryKey: queryFactory.users.details(token).queryKey,
+		queryKey: queryFactory.users.details(token ?? "").queryKey,
 		queryFn: () =>
 			serverGqlService.authenticatedRequest(
 				request,
@@ -366,7 +366,7 @@ export const getCookiesForApplication = async (
 	const [{ coreDetails }] = await Promise.all([getCachedCoreDetails()]);
 	const maxAge =
 		(tokenValidForDays || coreDetails.tokenValidForDays) * 24 * 60 * 60;
-	const options = { maxAge, path: "/" } satisfies CookieSerializeOptions;
+	const options = { maxAge, path: "/" } satisfies SerializeOptions;
 	return combineHeaders({
 		"set-cookie": serialize(AUTH_COOKIE_NAME, token, options),
 	});
@@ -391,9 +391,12 @@ export const getWorkoutCookieValue = (request: Request) => {
 };
 
 export const isWorkoutActive = (request: Request) => {
-	const inProgress = [FitnessAction.LogWorkout, FitnessAction.UpdateWorkout]
-		.map(String)
-		.includes(getWorkoutCookieValue(request));
+	const cookieValue = getWorkoutCookieValue(request);
+	const inProgress =
+		cookieValue &&
+		[FitnessAction.LogWorkout, FitnessAction.UpdateWorkout]
+			.map(String)
+			.includes(cookieValue);
 	return inProgress;
 };
 
diff --git a/apps/frontend/app/root.tsx b/apps/frontend/app/root.tsx
index 72f7ed912b..88b8965aed 100644
--- a/apps/frontend/app/root.tsx
+++ b/apps/frontend/app/root.tsx
@@ -12,9 +12,9 @@ import "@mantine/core/styles.css";
 import "@mantine/notifications/styles.css";
 import {
 	type LinksFunction,
+	type LoaderFunctionArgs,
 	type MetaFunction,
-	unstable_data,
-	unstable_defineLoader,
+	data,
 } from "@remix-run/node";
 import {
 	Links,
@@ -93,7 +93,7 @@ export const links: LinksFunction = () => {
 	];
 };
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const { toast, headers: toastHeaders } = await getToast(request);
 	const colorScheme = await colorSchemeCookie.parse(
 		request.headers.get("cookie") || "",
@@ -114,8 +114,8 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		}
 	}
 
-	return unstable_data({ toast, defaultColorScheme, isIOS18 }, { headers });
-});
+	return data({ toast, defaultColorScheme, isIOS18 }, { headers });
+};
 
 const DefaultHeadTags = () => {
 	const loaderData = useLoaderData<typeof loader>();
diff --git a/apps/frontend/app/routes/_dashboard._index.tsx b/apps/frontend/app/routes/_dashboard._index.tsx
index a505f966c4..bbaba20db5 100644
--- a/apps/frontend/app/routes/_dashboard._index.tsx
+++ b/apps/frontend/app/routes/_dashboard._index.tsx
@@ -17,8 +17,7 @@ import {
 	useMantineTheme,
 } from "@mantine/core";
 import { useInViewport } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { Link, useLoaderData } from "@remix-run/react";
 import {
 	type CalendarEventPartFragment,
@@ -89,7 +88,7 @@ const getTake = (preferences: UserPreferences, el: DashboardElementLot) => {
 	return t;
 };
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const preferences = await getCachedUserPreferences(request);
 	const takeUpcoming = getTake(preferences, DashboardElementLot.Upcoming);
 	const takeInProgress = getTake(preferences, DashboardElementLot.InProgress);
@@ -143,9 +142,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		inProgressCollectionContents,
 		userRecommendations,
 	};
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Home | Ryot" }];
 };
 
@@ -282,6 +281,12 @@ export default function Page() {
 												value: latestUserSummary.videoGameCount,
 												type: "number",
 											},
+											{
+												label: "Runtime",
+												value: latestUserSummary.totalVideoGameDuration,
+												type: "duration",
+												hideIfZero: true,
+											},
 										]}
 									/>
 									<DisplayStatForMediaType
diff --git a/apps/frontend/app/routes/_dashboard.calendar.tsx b/apps/frontend/app/routes/_dashboard.calendar.tsx
index 13c28bf3a9..14d45e25e0 100644
--- a/apps/frontend/app/routes/_dashboard.calendar.tsx
+++ b/apps/frontend/app/routes/_dashboard.calendar.tsx
@@ -11,12 +11,8 @@ import {
 	Title,
 	Tooltip,
 } from "@mantine/core";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	Link,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { Link, useLoaderData } from "@remix-run/react";
 import {
 	UserCalendarEventsDocument,
 	type UserCalendarEventsQuery,
@@ -40,7 +36,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const cookieName = await getEnhancedCookieName("calendar", request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
@@ -51,9 +47,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		}),
 	]);
 	return { query, userCalendarEvents, cookieName };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Calendar | Ryot" }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx b/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
index 258388564b..ea69460ba5 100644
--- a/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
@@ -15,12 +15,8 @@ import {
 	Title,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-	useNavigate,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { useLoaderData, useNavigate } from "@remix-run/react";
 import {
 	CollectionContentsDocument,
 	CollectionContentsSortBy,
@@ -81,7 +77,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { id: collectionId } = zx.parseParams(params, { id: z.string() });
 	const cookieName = await getEnhancedCookieName(
 		`collections.details.${collectionId}`,
@@ -108,9 +104,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		query[pageQueryParam] || 1,
 	);
 	return { collectionId, query, collectionContents, cookieName, totalPages };
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.collectionContents.details.name} | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.collections.list.tsx b/apps/frontend/app/routes/_dashboard.collections.list.tsx
index 5d8a086e6e..326c32ed50 100644
--- a/apps/frontend/app/routes/_dashboard.collections.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.collections.list.tsx
@@ -23,11 +23,14 @@ import {
 } from "@mantine/core";
 import { useDidUpdate, useHover, useListState } from "@mantine/hooks";
 import { notifications } from "@mantine/notifications";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
 import {
 	Form,
 	Link,
-	type MetaArgs_SingleFetch,
 	useLoaderData,
 	useNavigation,
 	useSearchParams,
@@ -46,6 +49,7 @@ import {
 } from "@ryot/generated/graphql/backend/graphql";
 import {
 	changeCase,
+	getActionIntent,
 	isString,
 	processSubmission,
 	truncate,
@@ -61,7 +65,7 @@ import { ClientError } from "graphql-request";
 import { useEffect, useState } from "react";
 import { Virtuoso } from "react-virtuoso";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
+import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
 import { zx } from "zodix";
@@ -91,24 +95,25 @@ import {
 	serverGqlService,
 } from "~/lib/utilities.server";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const cookieName = await getEnhancedCookieName("collections.list", request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const [{ usersList }] = await Promise.all([
 		serverGqlService.authenticatedRequest(request, UsersListDocument, {}),
 	]);
 	return { usersList, cookieName };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Your collections | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
 	await removeCachedUserCollectionsList(request);
-	return namedAction(request, {
-		createOrUpdate: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("createOrUpdate", async () => {
 			const submission = processSubmission(formData, createOrUpdateSchema);
 			try {
 				await serverGqlService.authenticatedRequest(
@@ -141,8 +146,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					},
 				);
 			}
-		},
-		delete: async () => {
+		})
+		.with("delete", async () => {
 			const submission = processSubmission(
 				formData,
 				z.object({ collectionName: z.string() }),
@@ -168,9 +173,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 					}),
 				},
 			);
-		},
-	});
-});
+		})
+		.run();
+};
 
 const createOrUpdateSchema = z.object({
 	name: z.string(),
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
index 84d812e8c3..4cf92f4248 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
@@ -42,15 +42,13 @@ import {
 	useToggle,
 } from "@mantine/hooks";
 import { notifications } from "@mantine/notifications";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import {
 	Link,
-	useFetcher,
 	useLoaderData,
 	useNavigate,
 	useRevalidator,
 } from "@remix-run/react";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
 import {
 	CreateOrUpdateUserWorkoutDocument,
 	CreateOrUpdateUserWorkoutTemplateDocument,
@@ -98,10 +96,8 @@ import { useEffect, useMemo, useRef, useState } from "react";
 import Webcam from "react-webcam";
 import { $path } from "remix-routes";
 import { ClientOnly } from "remix-utils/client-only";
-import { namedAction } from "remix-utils/named-action";
 import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
-import { withQuery } from "ufo";
 import { useInterval, useOnClickOutside } from "usehooks-ts";
 import { v4 as randomUUID } from "uuid";
 import { z } from "zod";
@@ -117,6 +113,7 @@ import {
 	FitnessAction,
 	FitnessEntity,
 	PRO_REQUIRED_MESSAGE,
+	clientGqlService,
 	dayjsLib,
 	getSetColor,
 	getSurroundingElements,
@@ -149,15 +146,11 @@ import {
 	useMeasurementsDrawerOpen,
 	useTimerAtom,
 } from "~/lib/state/fitness";
-import {
-	isWorkoutActive,
-	redirectWithToast,
-	serverGqlService,
-} from "~/lib/utilities.server";
+import { isWorkoutActive, redirectWithToast } from "~/lib/utilities.server";
 
 const workoutCookieName = CurrentWorkoutKey;
 
-export const loader = unstable_defineLoader(async ({ params, request }) => {
+export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, {
 		action: z.nativeEnum(FitnessAction),
 	});
@@ -177,49 +170,12 @@ export const loader = unstable_defineLoader(async ({ params, request }) => {
 		isUpdatingWorkout: action === FitnessAction.UpdateWorkout,
 		isCreatingTemplate: action === FitnessAction.CreateTemplate,
 	};
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${changeCase(data?.action || "")} | Ryot` }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
-	const formData = await request.clone().formData();
-	const workout = JSON.parse(formData.get("workout") as string);
-	return namedAction(request, {
-		createWorkout: async () => {
-			const { createOrUpdateUserWorkout } =
-				await serverGqlService.authenticatedRequest(
-					request,
-					CreateOrUpdateUserWorkoutDocument,
-					workout,
-				);
-			return redirectWithToast(
-				$path("/fitness/:entity/:id", {
-					entity: "workouts",
-					id: createOrUpdateUserWorkout,
-				}),
-				{ message: "Workout completed successfully", type: "success" },
-			);
-		},
-		createTemplate: async () => {
-			const { createOrUpdateUserWorkoutTemplate } =
-				await serverGqlService.authenticatedRequest(
-					request,
-					CreateOrUpdateUserWorkoutTemplateDocument,
-					workout,
-				);
-			return redirectWithToast(
-				$path("/fitness/:entity/:id", {
-					entity: "templates",
-					id: createOrUpdateUserWorkoutTemplate,
-				}),
-				{ message: "Template created successfully", type: "success" },
-			);
-		},
-	});
-});
-
 const deleteUploadedAsset = (key: string) => {
 	const formData = new FormData();
 	formData.append("key", key);
@@ -319,8 +275,6 @@ export default function Page() {
 		setCurrentTimer(RESET);
 	};
 
-	const createUserWorkoutFetcher = useFetcher<typeof action>();
-
 	return (
 		<Container size="sm">
 			{currentWorkout ? (
@@ -492,21 +446,45 @@ export default function Page() {
 															});
 														}
 														stopTimer();
-														if (!loaderData.isCreatingTemplate) {
+														const [entityId, fitnessEntity] = await match(
+															loaderData.isCreatingTemplate,
+														)
+															.with(true, () =>
+																clientGqlService
+																	.request(
+																		CreateOrUpdateUserWorkoutTemplateDocument,
+																		input,
+																	)
+																	.then((c) => [
+																		c.createOrUpdateUserWorkoutTemplate,
+																		FitnessEntity.Templates,
+																	]),
+															)
+															.with(false, () =>
+																clientGqlService
+																	.request(
+																		CreateOrUpdateUserWorkoutDocument,
+																		input,
+																	)
+																	.then((c) => [
+																		c.createOrUpdateUserWorkout,
+																		FitnessEntity.Workouts,
+																	]),
+															)
+															.exhaustive();
+														notifications.show({
+															color: "green",
+															message: "Saved successfully",
+														});
+														Cookies.remove(workoutCookieName);
+														revalidator.revalidate();
+														if (loaderData.action === FitnessAction.LogWorkout)
 															events.createWorkout();
-															Cookies.remove(workoutCookieName);
-														}
-														createUserWorkoutFetcher.submit(
-															{ workout: JSON.stringify(input) },
-															{
-																method: "post",
-																action: withQuery(".", {
-																	intent: loaderData.isCreatingTemplate
-																		? "createTemplate"
-																		: "createWorkout",
-																}),
-																encType: "multipart/form-data",
-															},
+														navigate(
+															$path("/fitness/:entity/:id", {
+																entity: fitnessEntity,
+																id: entityId,
+															}),
 														);
 													}
 												}}
@@ -1171,7 +1149,7 @@ const ExerciseDisplay = (props: {
 								leftSection={<IconLayersIntersect size={14} />}
 								onClick={() => props.openSupersetModal(exercise.identifier)}
 							>
-								Superset
+								{partOfSuperset ? "Edit" : "Create"} superset
 							</Menu.Item>
 							<Menu.Item
 								leftSection={<IconPhoto size={14} />}
@@ -1239,6 +1217,19 @@ const ExerciseDisplay = (props: {
 										for (const asset of assets) deleteUploadedAsset(asset.key);
 										setCurrentWorkout(
 											produce(currentWorkout, (draft) => {
+												const idx = draft.supersets.findIndex((s) =>
+													s.exercises.includes(exercise.identifier),
+												);
+												if (idx !== -1) {
+													if (draft.supersets[idx].exercises.length === 2)
+														draft.supersets.splice(idx, 1);
+													else
+														draft.supersets[idx].exercises = draft.supersets[
+															idx
+														].exercises.filter(
+															(e) => e !== exercise.identifier,
+														);
+												}
 												draft.exercises.splice(props.exerciseIdx, 1);
 											}),
 										);
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx b/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx
index e7494c1f32..a28c634655 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx
@@ -17,8 +17,11 @@ import { $path } from "remix-routes";
 import "@mantine/dates/styles.css";
 import { useDisclosure } from "@mantine/hooks";
 import { notifications } from "@mantine/notifications";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
 import { Form, Link, useLoaderData } from "@remix-run/react";
 import {
 	DeleteUserWorkoutDocument,
@@ -30,6 +33,7 @@ import {
 } from "@ryot/generated/graphql/backend/graphql";
 import {
 	changeCase,
+	getActionIntent,
 	humanizeDuration,
 	processSubmission,
 } from "@ryot/ts-utils";
@@ -50,7 +54,6 @@ import {
 	IconZzz,
 } from "@tabler/icons-react";
 import { type ReactNode, useState } from "react";
-import { namedAction } from "remix-utils/named-action";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
@@ -82,7 +85,7 @@ import {
 	serverGqlService,
 } from "~/lib/utilities.server";
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { id: entityId, entity } = zx.parseParams(params, {
 		id: z.string(),
 		entity: z.nativeEnum(FitnessEntity),
@@ -156,16 +159,17 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		})
 		.exhaustive();
 	return { entityId, entity, ...resp };
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.entityName} | Ryot` }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return namedAction(request, {
-		edit: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("edit", async () => {
 			const submission = processSubmission(formData, editWorkoutSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -178,8 +182,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Workout edited successfully",
 				}),
 			});
-		},
-		delete: async () => {
+		})
+		.with("delete", async () => {
 			const submission = processSubmission(formData, deleteSchema);
 			if (submission.workoutId)
 				await serverGqlService.authenticatedRequest(
@@ -198,9 +202,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 				type: "success",
 				message: `${changeCase(entity)} deleted successfully`,
 			});
-		},
-	});
-});
+		})
+		.run();
+};
 
 const deleteSchema = z.object({
 	workoutId: z.string().optional(),
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
index e5f3ab0af1..68177e1b24 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
@@ -13,12 +13,8 @@ import {
 	Title,
 } from "@mantine/core";
 import { notifications } from "@mantine/notifications";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	Link,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { Link, useLoaderData } from "@remix-run/react";
 import {
 	UserWorkoutTemplatesListDocument,
 	UserWorkoutsListDocument,
@@ -72,7 +68,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ params, request }) => {
+export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 	const { entity } = zx.parseParams(params, {
 		entity: z.nativeEnum(FitnessEntity),
 	});
@@ -128,9 +124,9 @@ export const loader = unstable_defineLoader(async ({ params, request }) => {
 		query[pageQueryParam],
 	);
 	return { query, entity, itemList, cookieName, totalPages };
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${changeCase(data?.entity || "")} | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx
index 38c48335fb..c46982874c 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx
@@ -11,16 +11,14 @@ import {
 	Title,
 } from "@mantine/core";
 import {
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
+	type MetaArgs,
+	data,
 	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
 	unstable_parseMultipartFormData,
 } from "@remix-run/node";
-import {
-	Form,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import { Form, useLoaderData } from "@remix-run/react";
 import {
 	CreateCustomExerciseDocument,
 	ExerciseDetailsDocument,
@@ -32,11 +30,15 @@ import {
 	ExerciseMuscle,
 	UpdateCustomExerciseDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import { cloneDeep, processSubmission, startCase } from "@ryot/ts-utils";
+import {
+	cloneDeep,
+	getActionIntent,
+	processSubmission,
+	startCase,
+} from "@ryot/ts-utils";
 import { IconPhoto } from "@tabler/icons-react";
 import { ClientError } from "graphql-request";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
 import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
@@ -58,7 +60,7 @@ enum Action {
 	Update = "update",
 }
 
-export const loader = unstable_defineLoader(async ({ params, request }) => {
+export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, { action: z.nativeEnum(Action) });
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const details = await match(action)
@@ -73,21 +75,18 @@ export const loader = unstable_defineLoader(async ({ params, request }) => {
 			return exerciseDetails;
 		})
 		.exhaustive();
-	return {
-		action,
-		details,
-	};
-});
+	return { action, details };
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Create Exercise | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
-	const uploaders = s3FileUploader("exercises");
+export const action = async ({ request }: ActionFunctionArgs) => {
+	const uploader = s3FileUploader("exercises");
 	const formData = await unstable_parseMultipartFormData(
 		request.clone(),
-		uploaders,
+		uploader,
 	);
 	const submission = processSubmission(formData, schema);
 	const muscles = submission.muscles
@@ -107,8 +106,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 		},
 	};
 	try {
-		return await namedAction(request, {
-			[Action.Create]: async () => {
+		const intent = getActionIntent(request);
+		return await match(intent)
+			.with(Action.Create, async () => {
 				const { createCustomExercise } =
 					await serverGqlService.authenticatedRequest(
 						request,
@@ -120,8 +120,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 						id: encodeURIComponent(createCustomExercise),
 					}),
 				);
-			},
-			[Action.Update]: async () => {
+			})
+			.with(Action.Update, async () => {
 				invariant(submission.oldName);
 				await serverGqlService.authenticatedRequest(
 					request,
@@ -129,12 +129,12 @@ export const action = unstable_defineAction(async ({ request }) => {
 					{ input: { ...input, oldName: submission.oldName } },
 				);
 				return redirect($path("/fitness/exercises/list"));
-			},
-		});
+			})
+			.run();
 	} catch (e) {
 		if (e instanceof ClientError && e.response.errors) {
 			const message = e.response.errors[0].message;
-			return Response.json(
+			return data(
 				{ error: e.message },
 				{
 					status: 400,
@@ -144,7 +144,7 @@ export const action = unstable_defineAction(async ({ request }) => {
 		}
 		throw e;
 	}
-});
+};
 
 const optionalString = z.string().optional();
 const optionalStringArray = z.array(z.string()).optional();
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
index ea8c204844..d409c45935 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
@@ -24,8 +24,11 @@ import {
 	rem,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
 import { Form, Link, useLoaderData, useNavigate } from "@remix-run/react";
 import {
 	EntityLot,
@@ -104,7 +107,7 @@ export type SearchParams = z.infer<typeof searchParamsSchema>;
 
 const paramsSchema = { id: z.string() };
 
-export const loader = unstable_defineLoader(async ({ params, request }) => {
+export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 	const { id: exerciseId } = zx.parseParams(params, paramsSchema);
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const workoutInProgress = !!getWorkoutCookieValue(request);
@@ -126,13 +129,13 @@ export const loader = unstable_defineLoader(async ({ params, request }) => {
 		exerciseParameters,
 		userExerciseDetails,
 	};
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.exerciseDetails.id} | Ryot` }];
 };
 
-export const action = unstable_defineAction(async ({ params, request }) => {
+export const action = async ({ params, request }: ActionFunctionArgs) => {
 	const { id: exerciseId } = zx.parseParams(params, paramsSchema);
 	const entries = Object.entries(Object.fromEntries(await request.formData()));
 	const submission = [];
@@ -156,7 +159,7 @@ export const action = unstable_defineAction(async ({ params, request }) => {
 			message: "Preferences updated",
 		}),
 	});
-});
+};
 
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
index 2b19203954..2005456694 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
@@ -21,13 +21,8 @@ import {
 	rem,
 } from "@mantine/core";
 import { useDisclosure, useListState } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	Link,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-	useNavigate,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { Link, useLoaderData, useNavigate } from "@remix-run/react";
 import {
 	ExerciseEquipment,
 	ExerciseForce,
@@ -92,7 +87,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const cookieName = await getEnhancedCookieName("exercises.list", request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
@@ -134,9 +129,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		workoutInProgress,
 		exerciseParameters,
 	};
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Exercises | Ryot" }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
index 88bf336438..61b2360f7d 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
@@ -12,17 +12,17 @@ import {
 	Text,
 	Title,
 } from "@mantine/core";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import {
-	Form,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
+import { Form, useLoaderData } from "@remix-run/react";
 import {
 	DeleteUserMeasurementDocument,
 	UserMeasurementsListDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import { processSubmission, startCase } from "@ryot/ts-utils";
+import { getActionIntent, processSubmission, startCase } from "@ryot/ts-utils";
 import {
 	IconChartArea,
 	IconPlus,
@@ -30,7 +30,8 @@ import {
 	IconTrash,
 } from "@tabler/icons-react";
 import { DataTable } from "mantine-datatable";
-import { namedAction } from "remix-utils/named-action";
+import { match } from "ts-pattern";
+import { withQuery } from "ufo";
 import { useLocalStorage } from "usehooks-ts";
 import { z } from "zod";
 import { zx } from "zodix";
@@ -63,7 +64,7 @@ export type SearchParams = z.infer<typeof searchParamsSchema>;
 
 const defaultTimeSpan = TimeSpan.Last30Days;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const cookieName = await getEnhancedCookieName("measurements.list", request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
@@ -82,16 +83,17 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		),
 	]);
 	return { query, userMeasurementsList, cookieName };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Measurements | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return namedAction(request, {
-		delete: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("delete", async () => {
 			const submission = processSubmission(formData, deleteSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -104,9 +106,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Measurement deleted successfully",
 				}),
 			});
-		},
-	});
-});
+		})
+		.run();
+};
 
 const deleteSchema = z.object({ timestamp: z.string() });
 
@@ -220,8 +222,8 @@ export default function Page() {
 							records={loaderData.userMeasurementsList}
 							columns={[
 								{
-									accessor: "timestamp",
 									width: 200,
+									accessor: "timestamp",
 									render: ({ timestamp }) => dayjsLib(timestamp).format("lll"),
 								},
 								...([
@@ -247,16 +249,14 @@ export default function Page() {
 									// biome-ignore lint/suspicious/noExplicitAny: required here
 								})) as any),
 								{
-									accessor: "Delete",
 									width: 80,
+									accessor: "Delete",
 									textAlign: "center",
 									render: ({ timestamp }) => (
-										<Form method="POST" replace>
-											<input
-												type="hidden"
-												name="intent"
-												defaultValue="delete"
-											/>
+										<Form
+											method="POST"
+											action={withQuery(".", { intent: "delete" })}
+										>
 											<input
 												type="hidden"
 												name="timestamp"
diff --git a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
index 7e75023908..ad351d47ce 100644
--- a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
@@ -16,8 +16,7 @@ import {
 	Text,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import {
 	Link,
 	useLoaderData,
@@ -119,7 +118,7 @@ const metadataMapping = {
 	[MediaLot.VisualNovel]: [MediaSource.Vndb],
 };
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { action, lot } = zx.parseParams(params, {
 		action: z.nativeEnum(Action),
 		lot: z.string().transform((v) => getLot(v) as MediaLot),
@@ -218,9 +217,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		url: withoutHost(url.href),
 		[pageQueryParam]: Number(query[pageQueryParam]),
 	};
-});
+};
 
-export const meta = ({ params }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ params }: MetaArgs<typeof loader>) => {
 	return [
 		{
 			title: `${changeCase(params.action || "")} ${changeCase(
diff --git a/apps/frontend/app/routes/_dashboard.media.create.tsx b/apps/frontend/app/routes/_dashboard.media.create.tsx
index 16083cf9d9..0a55245a9f 100644
--- a/apps/frontend/app/routes/_dashboard.media.create.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.create.tsx
@@ -15,12 +15,12 @@ import {
 	Title,
 } from "@mantine/core";
 import {
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
+	type MetaArgs,
 	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
 	unstable_parseMultipartFormData,
 } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
 import { Form, useLoaderData } from "@remix-run/react";
 import {
 	CreateCustomMetadataDocument,
@@ -40,16 +40,16 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchParamsSchema);
 	return { query };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Create Media | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const uploaders = s3FileUploader("metadata");
 	const formData = await unstable_parseMultipartFormData(request, uploaders);
 	const submission = processSubmission(formData, schema);
@@ -69,7 +69,7 @@ export const action = unstable_defineAction(async ({ request }) => {
 		{ input },
 	);
 	return redirect($path("/media/item/:id", { id: createCustomMetadata.id }));
-});
+};
 
 const optionalString = z.string().optional();
 const optionalStringArray = z.array(z.string()).optional();
diff --git a/apps/frontend/app/routes/_dashboard.media.genre.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.genre.$id._index.tsx
index 7a66f3ccc7..975d400f10 100644
--- a/apps/frontend/app/routes/_dashboard.media.genre.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.genre.$id._index.tsx
@@ -7,8 +7,7 @@ import {
 	Text,
 	Title,
 } from "@mantine/core";
-import { unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { useLoaderData } from "@remix-run/react";
 import { GenreDetailsDocument } from "@ryot/generated/graphql/backend/graphql";
 import { z } from "zod";
@@ -30,7 +29,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { id: genreId } = zx.parseParams(params, { id: z.string() });
 	const cookieName = await getEnhancedCookieName(`genre.${genreId}`, request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
@@ -46,9 +45,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		query[pageQueryParam],
 	);
 	return { query, genreDetails, cookieName, totalPages };
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.genreDetails.details.name} | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.media.genre.list.tsx b/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
index 86c8253079..6f76c6d722 100644
--- a/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
@@ -12,12 +12,8 @@ import {
 	Text,
 	Title,
 } from "@mantine/core";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	Link,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { Link, useLoaderData } from "@remix-run/react";
 import {
 	GenreDetailsDocument,
 	GenresListDocument,
@@ -61,7 +57,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const cookieName = await getEnhancedCookieName("genre.list", request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
@@ -76,9 +72,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		query[pageQueryParam],
 	);
 	return { query, genresList, cookieName, totalPages };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Genres | Ryot" }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx b/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx
index 516acdb78a..485210de16 100644
--- a/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx
@@ -15,12 +15,8 @@ import {
 	Title,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-	useNavigate,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { useLoaderData, useNavigate } from "@remix-run/react";
 import {
 	EntityLot,
 	GraphqlSortOrder,
@@ -84,7 +80,7 @@ const SEARCH_SOURCES_ALLOWED: Partial<Record<MediaSource, MediaLot>> = {
 	[MediaSource.Igdb]: MediaLot.VideoGame,
 };
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, { action: z.nativeEnum(Action) });
 	const cookieName = await getEnhancedCookieName(`groups.${action}`, request);
 	const query = zx.parseQuery(request, {
@@ -159,9 +155,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		cookieName,
 		[pageQueryParam]: query[pageQueryParam],
 	};
-});
+};
 
-export const meta = ({ params }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ params }: MetaArgs<typeof loader>) => {
 	return [{ title: `${changeCase(params.action || "")} Groups | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx
index bf0e409270..f3f1b146a2 100644
--- a/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx
@@ -10,8 +10,7 @@ import {
 	Text,
 	Title,
 } from "@mantine/core";
-import { unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { useLoaderData } from "@remix-run/react";
 import {
 	EntityLot,
@@ -45,7 +44,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { id: metadataGroupId } = zx.parseParams(params, { id: z.string() });
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const [{ metadataGroupDetails }, { userMetadataGroupDetails }] =
@@ -65,9 +64,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		metadataGroupDetails,
 		userMetadataGroupDetails,
 	};
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.metadataGroupDetails.details.title} | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
index 337ecb9b96..152f6d13b6 100644
--- a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
@@ -32,13 +32,12 @@ import {
 import { DateInput } from "@mantine/dates";
 import { useDidUpdate, useDisclosure, useInViewport } from "@mantine/hooks";
 import { notifications } from "@mantine/notifications";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import {
-	Form,
-	Link,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
+import { Form, Link, useLoaderData } from "@remix-run/react";
 import {
 	DeleteSeenItemDocument,
 	DisassociateMetadataDocument,
@@ -59,6 +58,7 @@ import {
 import {
 	changeCase,
 	formatDateToNaiveDate,
+	getActionIntent,
 	humanizeDuration,
 	isInteger,
 	isNumber,
@@ -91,7 +91,6 @@ import {
 } from "react";
 import { Virtuoso, VirtuosoGrid, type VirtuosoHandle } from "react-virtuoso";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
@@ -147,7 +146,7 @@ const searchParamsSchema = z
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { id: metadataId } = zx.parseParams(params, { id: z.string() });
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const [{ metadataDetails }, { userMetadataDetails }] = await Promise.all([
@@ -159,16 +158,17 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		),
 	]);
 	return { query, metadataId, metadataDetails, userMetadataDetails };
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.metadataDetails.title} | Ryot` }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return namedAction(request, {
-		deleteSeenItem: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("deleteSeenItem", async () => {
 			const submission = processSubmission(formData, seenIdSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -181,8 +181,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Record deleted successfully",
 				}),
 			});
-		},
-		mergeMetadata: async () => {
+		})
+		.with("mergeMetadata", async () => {
 			const submission = processSubmission(formData, mergeMetadataSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -193,8 +193,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 				$path("/media/item/:id", { id: submission.mergeInto }),
 				{ type: "success", message: "Metadata merged successfully" },
 			);
-		},
-		editSeenItem: async () => {
+		})
+		.with("editSeenItem", async () => {
 			const submission = processSubmission(formData, editSeenItem);
 			submission.reviewId = submission.reviewId || "";
 			await serverGqlService.authenticatedRequest(
@@ -208,8 +208,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Edited history item successfully",
 				}),
 			});
-		},
-		removeItem: async () => {
+		})
+		.with("removeItem", async () => {
 			const submission = processSubmission(formData, MetadataIdSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -220,9 +220,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 				type: "success",
 				message: "Removed item successfully",
 			});
-		},
-	});
-});
+		})
+		.run();
+};
 
 const seenIdSchema = z.object({ seenId: z.string() });
 
@@ -718,42 +718,38 @@ export default function Page() {
 										nextEntry ? (
 											<>
 												<Menu.Label>Anime</Menu.Label>
-												<>
-													<Menu.Item
-														onClick={() => {
-															setMetadataToUpdate({
-																metadataId: loaderData.metadataId,
-																animeEpisodeNumber: nextEntry.episode,
-															});
-														}}
-													>
-														Mark EP-
-														{nextEntry.episode} as listened
-													</Menu.Item>
-												</>
+												<Menu.Item
+													onClick={() => {
+														setMetadataToUpdate({
+															metadataId: loaderData.metadataId,
+															animeEpisodeNumber: nextEntry.episode,
+														});
+													}}
+												>
+													Mark EP-
+													{nextEntry.episode} as listened
+												</Menu.Item>
 											</>
 										) : null}
 										{loaderData.metadataDetails.lot === MediaLot.Manga &&
 										nextEntry ? (
 											<>
 												<Menu.Label>Manga</Menu.Label>
-												<>
-													<Menu.Item
-														onClick={() => {
-															setMetadataToUpdate({
-																metadataId: loaderData.metadataId,
-																mangaChapterNumber: nextEntry.chapter,
-																mangaVolumeNumber: nextEntry.volume,
-															});
-														}}
-													>
-														Mark{" "}
-														{nextEntry.chapter
-															? `CH-${nextEntry.chapter}`
-															: `VOL-${nextEntry.volume}`}{" "}
-														as read
-													</Menu.Item>
-												</>
+												<Menu.Item
+													onClick={() => {
+														setMetadataToUpdate({
+															metadataId: loaderData.metadataId,
+															mangaChapterNumber: nextEntry.chapter,
+															mangaVolumeNumber: nextEntry.volume,
+														});
+													}}
+												>
+													Mark{" "}
+													{nextEntry.chapter
+														? `CH-${nextEntry.chapter}`
+														: `VOL-${nextEntry.volume}`}{" "}
+													as read
+												</Menu.Item>
 											</>
 										) : null}
 										{loaderData.metadataDetails.lot === MediaLot.Podcast ? (
@@ -1801,25 +1797,23 @@ const DisplayShowSeason = (props: {
 				.map((e) => e.runtime || 0)
 				.reduce((i, a) => i + a, 0)}
 		>
-			<>
-				{props.season.episodes.length > 0 ? (
-					<Button
-						variant={isSeen ? "default" : "outline"}
-						size="xs"
-						color="blue"
-						onClick={() => {
-							setMetadataToUpdate({
-								metadataId: loaderData.metadataId,
-								showSeasonNumber: props.season.seasonNumber,
-								showEpisodeNumber: props.season.episodes.at(-1)?.episodeNumber,
-								showAllEpisodesBefore: true,
-							});
-						}}
-					>
-						{isSeen ? "Watch again" : "Mark as seen"}
-					</Button>
-				) : null}
-			</>
+			{props.season.episodes.length > 0 ? (
+				<Button
+					variant={isSeen ? "default" : "outline"}
+					size="xs"
+					color="blue"
+					onClick={() => {
+						setMetadataToUpdate({
+							metadataId: loaderData.metadataId,
+							showSeasonNumber: props.season.seasonNumber,
+							showEpisodeNumber: props.season.episodes.at(-1)?.episodeNumber,
+							showAllEpisodesBefore: true,
+						});
+					}}
+				>
+					{isSeen ? "Watch again" : "Mark as seen"}
+				</Button>
+			) : null}
 		</DisplaySeasonOrEpisodeDetails>
 	);
 };
diff --git a/apps/frontend/app/routes/_dashboard.media.people.$action.tsx b/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
index ab248f265c..3262a86e8f 100644
--- a/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
@@ -15,12 +15,8 @@ import {
 	Title,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
-import {
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-	useNavigate,
-} from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { useLoaderData, useNavigate } from "@remix-run/react";
 import {
 	EntityLot,
 	GraphqlSortOrder,
@@ -85,7 +81,7 @@ const SEARCH_SOURCES_ALLOWED = [
 	MediaSource.Igdb,
 ] as const;
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, { action: z.nativeEnum(Action) });
 	const cookieName = await getEnhancedCookieName(`people.${action}`, request);
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
@@ -162,9 +158,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		peopleSearch,
 		[pageQueryParam]: query[pageQueryParam],
 	};
-});
+};
 
-export const meta = ({ params }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ params }: MetaArgs<typeof loader>) => {
 	return [{ title: `${changeCase(params.action || "")} People | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
index 7c228b7be6..a0b434f757 100644
--- a/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
@@ -11,8 +11,8 @@ import {
 	Text,
 	Title,
 } from "@mantine/core";
-import { unstable_defineLoader } from "@remix-run/node";
-import { type MetaArgs_SingleFetch, useLoaderData } from "@remix-run/react";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { useLoaderData } from "@remix-run/react";
 import {
 	EntityLot,
 	PersonDetailsDocument,
@@ -48,7 +48,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { id: personId } = zx.parseParams(params, { id: z.string() });
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const [{ personDetails }, { userPersonDetails }] = await Promise.all([
@@ -58,9 +58,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		}),
 	]);
 	return { query, personId, userPersonDetails, personDetails };
-});
+};
 
-export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = ({ data }: MetaArgs<typeof loader>) => {
 	return [{ title: `${data?.personDetails.details.name} | Ryot` }];
 };
 
diff --git a/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx b/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx
index f8a0f3620d..e0412f4828 100644
--- a/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx
@@ -22,15 +22,12 @@ import {
 	Tooltip,
 } from "@mantine/core";
 import {
-	unstable_defineAction,
-	unstable_defineLoader,
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
+	type MetaArgs,
 	unstable_parseMultipartFormData,
 } from "@remix-run/node";
-import {
-	Form,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import { Form, useLoaderData } from "@remix-run/react";
 import {
 	DeployExportJobDocument,
 	DeployImportJobDocument,
@@ -38,11 +35,10 @@ import {
 	ImportSource,
 	UserExportsDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import { changeCase, processSubmission } from "@ryot/ts-utils";
+import { changeCase, getActionIntent, processSubmission } from "@ryot/ts-utils";
 import { IconDownload } from "@tabler/icons-react";
 import { filesize } from "filesize";
-import { type ReactNode, useState } from "react";
-import { namedAction } from "remix-utils/named-action";
+import { useState } from "react";
 import { match } from "ts-pattern";
 import { withFragment, withQuery } from "ufo";
 import { z } from "zod";
@@ -57,25 +53,26 @@ import {
 import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";
 import { temporaryFileUploadHandler } from "~/lib/utilities.server";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const [{ importReports }, { userExports }] = await Promise.all([
 		serverGqlService.authenticatedRequest(request, ImportReportsDocument, {}),
 		serverGqlService.authenticatedRequest(request, UserExportsDocument, {}),
 	]);
 	return { importReports, userExports };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Imports and Exports | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await unstable_parseMultipartFormData(
 		request.clone(),
 		temporaryFileUploadHandler,
 	);
-	return namedAction(request, {
-		deployImport: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("deployImport", async () => {
 			const source = formData.get("source") as ImportSource;
 			formData.delete("source");
 			const values = await match(source)
@@ -133,8 +130,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					}),
 				},
 			);
-		},
-		deployExport: async () => {
+		})
+		.with("deployExport", async () => {
 			await serverGqlService.authenticatedRequest(
 				request,
 				DeployExportJobDocument,
@@ -149,9 +146,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 					}),
 				},
 			);
-		},
-	});
-});
+		})
+		.run();
+};
 
 const usernameImportFormSchema = z.object({ username: z.string() });
 
@@ -194,6 +191,7 @@ const malImportFormSchema = z.object({
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
 	const coreDetails = useCoreDetails();
+	const submit = useConfirmSubmit();
 	const fileUploadNotAllowed = !coreDetails.fileStorageEnabled;
 	const userCollections = useUserCollections();
 	const events = useApplicationEvents();
@@ -262,7 +260,7 @@ export default function Page() {
 									}}
 								/>
 								{deployImportSource ? (
-									<ImportSourceElement>
+									<>
 										{match(deployImportSource)
 											.with(
 												ImportSource.Audiobookshelf,
@@ -283,7 +281,6 @@ export default function Page() {
 													</>
 												),
 											)
-
 											.with(
 												ImportSource.OpenScale,
 												ImportSource.Goodreads,
@@ -423,7 +420,26 @@ export default function Page() {
 												</>
 											))
 											.exhaustive()}
-									</ImportSourceElement>
+										<Button
+											mt="md"
+											fullWidth
+											radius="md"
+											color="blue"
+											type="submit"
+											variant="light"
+											onClick={async (e) => {
+												const form = e.currentTarget.form;
+												e.preventDefault();
+												const conf = await confirmWrapper({
+													confirmation:
+														"Are you sure you want to deploy an import job? This action is irreversible.",
+												});
+												if (conf && form) submit(form);
+											}}
+										>
+											Import
+										</Button>
+									</>
 								) : null}
 								<Divider />
 								<Title order={3}>Import history</Title>
@@ -503,15 +519,19 @@ export default function Page() {
 									</Anchor>
 								</Group>
 							</Flex>
-							<Form method="POST" encType="multipart/form-data">
+							<Form
+								method="POST"
+								encType="multipart/form-data"
+								action={withQuery(".", { intent: "deployExport" })}
+							>
 								<input
-									type="hidden"
-									name="intent"
-									defaultValue="deployExport"
+									hidden
+									name="dummy"
+									defaultValue="this is required because of the encType"
 								/>
 								<Tooltip
 									label="Please enable file storage to use this feature"
-									disabled={fileUploadNotAllowed}
+									disabled={!fileUploadNotAllowed}
 								>
 									<Button
 										mt="xs"
@@ -560,34 +580,3 @@ export default function Page() {
 		</Container>
 	);
 }
-
-const ImportSourceElement = (props: {
-	children: ReactNode | Array<ReactNode>;
-}) => {
-	const submit = useConfirmSubmit();
-
-	return (
-		<>
-			{props.children}
-			<Button
-				type="submit"
-				variant="light"
-				color="blue"
-				fullWidth
-				mt="md"
-				radius="md"
-				onClick={async (e) => {
-					const form = e.currentTarget.form;
-					e.preventDefault();
-					const conf = await confirmWrapper({
-						confirmation:
-							"Are you sure you want to deploy an import job? This action is irreversible.",
-					});
-					if (conf && form) submit(form);
-				}}
-			>
-				Import
-			</Button>
-		</>
-	);
-};
diff --git a/apps/frontend/app/routes/_dashboard.settings.integrations.tsx b/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
index 0bf11adb20..d83a138aac 100644
--- a/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
@@ -21,8 +21,11 @@ import {
 	Tooltip,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
 import { Form, useActionData, useLoaderData } from "@remix-run/react";
 import {
 	CreateUserIntegrationDocument,
@@ -34,7 +37,7 @@ import {
 	UserIntegrationsDocument,
 	type UserIntegrationsQuery,
 } from "@ryot/generated/graphql/backend/graphql";
-import { changeCase, processSubmission } from "@ryot/ts-utils";
+import { changeCase, getActionIntent, processSubmission } from "@ryot/ts-utils";
 import {
 	IconCheck,
 	IconCopy,
@@ -44,15 +47,14 @@ import {
 	IconTrash,
 } from "@tabler/icons-react";
 import { useState } from "react";
-import { namedAction } from "remix-utils/named-action";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
 import { zx } from "zodix";
 import { confirmWrapper } from "~/components/confirmation";
 import {
+	PRO_REQUIRED_MESSAGE,
 	applicationBaseUrl,
-	commaDelimitedString,
 	dayjsLib,
 } from "~/lib/generals";
 import {
@@ -62,6 +64,7 @@ import {
 } from "~/lib/hooks";
 import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";
 
+const PRO_INTEGRATIONS = [IntegrationProvider.JellyfinPush];
 const YANK_INTEGRATIONS = [
 	IntegrationProvider.Audiobookshelf,
 	IntegrationProvider.Komga,
@@ -69,10 +72,11 @@ const YANK_INTEGRATIONS = [
 const PUSH_INTEGRATIONS = [
 	IntegrationProvider.Radarr,
 	IntegrationProvider.Sonarr,
+	IntegrationProvider.JellyfinPush,
 ];
 const NO_SHOW_URL = [...YANK_INTEGRATIONS, ...PUSH_INTEGRATIONS];
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const [{ userIntegrations }] = await Promise.all([
 		serverGqlService.authenticatedRequest(
 			request,
@@ -81,16 +85,17 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		),
 	]);
 	return { userIntegrations };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Integration Settings | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return namedAction(request, {
-		delete: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("delete", async () => {
 			const submission = processSubmission(formData, deleteSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -106,8 +111,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					}),
 				},
 			);
-		},
-		create: async () => {
+		})
+		.with("create", async () => {
 			const submission = processSubmission(formData, createSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -123,8 +128,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					}),
 				},
 			);
-		},
-		update: async () => {
+		})
+		.with("update", async () => {
 			const submission = processSubmission(formData, updateSchema);
 			// DEV: Reason for this: https://stackoverflow.com/a/11424089/11667450
 			submission.isDisabled = submission.isDisabled === true;
@@ -142,47 +147,27 @@ export const action = unstable_defineAction(async ({ request }) => {
 					}),
 				},
 			);
-		},
-		generateAuthToken: async () => {
+		})
+		.with("generateAuthToken", async () => {
 			const { generateAuthToken } = await serverGqlService.authenticatedRequest(
 				request,
 				GenerateAuthTokenDocument,
 				{},
 			);
 			return Response.json({ status: "success", generateAuthToken } as const);
-		},
-	});
-});
+		})
+		.run();
+};
 
 const MINIMUM_PROGRESS = "2";
 const MAXIMUM_PROGRESS = "95";
 
 const createSchema = z.object({
-	provider: z.nativeEnum(IntegrationProvider),
+	providerSpecifics: z.any().optional(),
 	minimumProgress: z.string().optional(),
 	maximumProgress: z.string().optional(),
+	provider: z.nativeEnum(IntegrationProvider),
 	syncToOwnedCollection: zx.CheckboxAsString.optional(),
-	providerSpecifics: z
-		.object({
-			plexUsername: z.string().optional(),
-			audiobookshelfBaseUrl: z.string().optional(),
-			audiobookshelfToken: z.string().optional(),
-			komgaBaseUrl: z.string().optional(),
-			komgaUsername: z.string().optional(),
-			komgaPassword: z.string().optional(),
-			komgaProvider: z.nativeEnum(MediaSource).optional(),
-			radarrBaseUrl: z.string().optional(),
-			radarrApiKey: z.string().optional(),
-			radarrProfileId: z.number().optional(),
-			radarrRootFolderPath: z.string().optional(),
-			radarrSyncCollectionIds: commaDelimitedString,
-			sonarrBaseUrl: z.string().optional(),
-			sonarrApiKey: z.string().optional(),
-			sonarrProfileId: z.number().optional(),
-			sonarrRootFolderPath: z.string().optional(),
-			sonarrSyncCollectionIds: commaDelimitedString,
-		})
-		.optional(),
 });
 
 const deleteSchema = z.object({
@@ -392,7 +377,9 @@ const CreateIntegrationModal = (props: {
 	closeIntegrationModal: () => void;
 }) => {
 	const coreDetails = useCoreDetails();
-	const [provider, setProvider] = useState<IntegrationProvider | null>(null);
+	const [provider, setProvider] = useState<IntegrationProvider>();
+	const disableCreationButton =
+		!coreDetails.isPro && provider && PRO_INTEGRATIONS.includes(provider);
 
 	return (
 		<Modal
@@ -493,6 +480,25 @@ const CreateIntegrationModal = (props: {
 								/>
 							</>
 						))
+						.with(IntegrationProvider.JellyfinPush, () => (
+							<>
+								<TextInput
+									required
+									label="Base URL"
+									name="providerSpecifics.jellyfinPushBaseUrl"
+								/>
+								<TextInput
+									required
+									label="Username"
+									name="providerSpecifics.jellyfinPushUsername"
+								/>
+								<TextInput
+									required
+									label="Password"
+									name="providerSpecifics.jellyfinPushPassword"
+								/>
+							</>
+						))
 						.with(IntegrationProvider.Radarr, () => <ArrInputs name="radarr" />)
 						.with(IntegrationProvider.Sonarr, () => <ArrInputs name="sonarr" />)
 						.otherwise(() => undefined)}
@@ -510,7 +516,14 @@ const CreateIntegrationModal = (props: {
 							/>
 						</Tooltip>
 					) : undefined}
-					<Button type="submit">Submit</Button>
+					<Tooltip
+						label={PRO_REQUIRED_MESSAGE}
+						disabled={!disableCreationButton}
+					>
+						<Button type="submit" disabled={disableCreationButton}>
+							Submit
+						</Button>
+					</Tooltip>
 				</Stack>
 			</Form>
 		</Modal>
diff --git a/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx b/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx
index 8b567d64fc..ec6b124237 100644
--- a/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx
@@ -7,8 +7,8 @@ import {
 	Text,
 	Title,
 } from "@mantine/core";
-import { unstable_defineAction } from "@remix-run/node";
-import { Form, type MetaArgs_SingleFetch } from "@remix-run/react";
+import type { ActionFunctionArgs, MetaArgs } from "@remix-run/node";
+import { Form, data } from "@remix-run/react";
 import {
 	BackgroundJob,
 	DeployBackgroundJobDocument,
@@ -19,11 +19,11 @@ import { z } from "zod";
 import { useDashboardLayoutData, useUserDetails } from "~/lib/hooks";
 import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";
 
-export const meta = (_args: MetaArgs_SingleFetch) => {
+export const meta = (_args: MetaArgs) => {
 	return [{ title: "Miscellaneous settings | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
 	const submission = processSubmission(formData, jobSchema);
 	await serverGqlService.authenticatedRequest(
@@ -31,13 +31,13 @@ export const action = unstable_defineAction(async ({ request }) => {
 		DeployBackgroundJobDocument,
 		submission,
 	);
-	return Response.json({ status: "success" } as const, {
+	return data({} as const, {
 		headers: await createToastHeaders({
 			type: "success",
 			message: "Job has been deployed",
 		}),
 	});
-});
+};
 
 const jobSchema = z.object({
 	jobName: z.nativeEnum(BackgroundJob),
diff --git a/apps/frontend/app/routes/_dashboard.settings.notifications.tsx b/apps/frontend/app/routes/_dashboard.settings.notifications.tsx
index cf6151d353..36f9b00f50 100644
--- a/apps/frontend/app/routes/_dashboard.settings.notifications.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.notifications.tsx
@@ -17,12 +17,12 @@ import {
 	Tooltip,
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import {
-	Form,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
+import { Form, useLoaderData } from "@remix-run/react";
 import {
 	CreateUserNotificationPlatformDocument,
 	DeleteUserNotificationPlatformDocument,
@@ -32,14 +32,13 @@ import {
 	UserNotificationPlatformsDocument,
 	type UserNotificationPlatformsQuery,
 } from "@ryot/generated/graphql/backend/graphql";
-import { changeCase, processSubmission } from "@ryot/ts-utils";
+import { changeCase, getActionIntent, processSubmission } from "@ryot/ts-utils";
 import {
 	IconPlayerPause,
 	IconPlayerPlay,
 	IconTrash,
 } from "@tabler/icons-react";
 import { useState } from "react";
-import { namedAction } from "remix-utils/named-action";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
@@ -49,7 +48,7 @@ import { dayjsLib } from "~/lib/generals";
 import { useConfirmSubmit, useCoreDetails } from "~/lib/hooks";
 import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const [{ userNotificationPlatforms }] = await Promise.all([
 		serverGqlService.authenticatedRequest(
 			request,
@@ -58,16 +57,17 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		),
 	]);
 	return { userNotificationPlatforms };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Notification Settings | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return namedAction(request, {
-		create: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("create", async () => {
 			const submission = processSubmission(formData, createSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -75,8 +75,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 				{ input: submission },
 			);
 			return Response.json({ status: "success", submission } as const);
-		},
-		delete: async () => {
+		})
+		.with("delete", async () => {
 			const submission = processSubmission(formData, deleteSchema);
 			await serverGqlService.authenticatedRequest(
 				request,
@@ -89,8 +89,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Notification platform deleted successfully",
 				}),
 			});
-		},
-		test: async () => {
+		})
+		.with("test", async () => {
 			const { testUserNotificationPlatforms } =
 				await serverGqlService.authenticatedRequest(
 					request,
@@ -105,8 +105,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 						: "Something went wrong",
 				}),
 			});
-		},
-		update: async () => {
+		})
+		.with("update", async () => {
 			const submission = processSubmission(formData, updateSchema);
 			submission.isDisabled = submission.isDisabled === true;
 			await serverGqlService.authenticatedRequest(
@@ -120,9 +120,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Notification updated successfully",
 				}),
 			});
-		},
-	});
-});
+		})
+		.run();
+};
 
 const deleteSchema = z.object({ notificationId: z.string() });
 
diff --git a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
index 6a203782f2..e8a5cbf43a 100644
--- a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
@@ -25,9 +25,12 @@ import {
 	rem,
 } from "@mantine/core";
 import { notifications } from "@mantine/notifications";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
-import { Form, useLoaderData } from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
+import { Form, data, useLoaderData } from "@remix-run/react";
 import {
 	type DashboardElementLot,
 	GridPacking,
@@ -76,13 +79,13 @@ const searchSchema = z.object({
 	defaultTab: z.string().default("dashboard").optional(),
 });
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchSchema);
 	const workoutInProgress = isWorkoutActive(request);
 	return { query, workoutInProgress };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Preference | Ryot" }];
 };
 
@@ -93,7 +96,7 @@ const notificationContent = {
 		"Changing preferences is disabled for demo users. Please create an account to save your preferences.",
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const userDetails = await redirectIfNotAuthenticatedOrUpdated(request);
 	const entries = Object.entries(Object.fromEntries(await request.formData()));
 	const submission = [];
@@ -121,8 +124,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 		message: "Preferences updated",
 		type: "success",
 	});
-	return Response.json({}, { headers: toastHeaders });
-});
+	return data({}, { headers: toastHeaders });
+};
 
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
diff --git a/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx b/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx
index 3fe089131b..35d6e970c9 100644
--- a/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx
@@ -22,12 +22,12 @@ import {
 } from "@mantine/core";
 import { DateTimePicker } from "@mantine/dates";
 import { useDisclosure } from "@mantine/hooks";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
-import {
-	Form,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-} from "@remix-run/react";
+import type {
+	ActionFunctionArgs,
+	LoaderFunctionArgs,
+	MetaArgs,
+} from "@remix-run/node";
+import { Form, useLoaderData } from "@remix-run/react";
 import {
 	CreateAccessLinkDocument,
 	RevokeAccessLinkDocument,
@@ -35,7 +35,12 @@ import {
 	UserAccessLinksDocument,
 	type UserAccessLinksQuery,
 } from "@ryot/generated/graphql/backend/graphql";
-import { isNumber, isString, processSubmission } from "@ryot/ts-utils";
+import {
+	getActionIntent,
+	isNumber,
+	isString,
+	processSubmission,
+} from "@ryot/ts-utils";
 import {
 	IconEye,
 	IconEyeClosed,
@@ -43,7 +48,7 @@ import {
 	IconLockAccess,
 } from "@tabler/icons-react";
 import { ClientOnly } from "remix-utils/client-only";
-import { namedAction } from "remix-utils/named-action";
+import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
 import { zx } from "zodix";
@@ -66,28 +71,29 @@ import {
 	serverGqlService,
 } from "~/lib/utilities.server";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const [{ userAccessLinks }] = await Promise.all([
 		serverGqlService.authenticatedRequest(request, UserAccessLinksDocument, {}),
 	]);
 	return { userAccessLinks };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch) => {
+export const meta = (_args: MetaArgs) => {
 	return [{ title: "Profile and Sharing | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.formData();
-	return namedAction(request, {
-		updateProfile: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("updateProfile", async () => {
 			const token = getAuthorizationCookie(request);
 			const submission = processSubmission(formData, updateProfileFormSchema);
 			await serverGqlService.authenticatedRequest(request, UpdateUserDocument, {
 				input: submission,
 			});
 			queryClient.removeQueries({
-				queryKey: queryFactory.users.details(token).queryKey,
+				queryKey: queryFactory.users.details(token ?? "").queryKey,
 			});
 			return Response.json({ status: "success", submission } as const, {
 				headers: await createToastHeaders({
@@ -95,8 +101,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Profile updated successfully",
 				}),
 			});
-		},
-		revokeAccessLink: async () => {
+		})
+		.with("revokeAccessLink", async () => {
 			const submission = processSubmission(
 				formData,
 				revokeAccessLinkFormSchema,
@@ -112,8 +118,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Access link revoked successfully",
 				}),
 			});
-		},
-		createAccessLink: async () => {
+		})
+		.with("createAccessLink", async () => {
 			const submission = processSubmission(
 				formData,
 				createAccessLinkFormSchema,
@@ -130,8 +136,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Access link created successfully",
 				}),
 			});
-		},
-		createDefaultAccessLink: async () => {
+		})
+		.with("createDefaultAccessLink", async () => {
 			await serverGqlService.authenticatedRequest(
 				request,
 				CreateAccessLinkDocument,
@@ -143,9 +149,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "Account default access link created successfully",
 				}),
 			});
-		},
-	});
-});
+		})
+		.run();
+};
 
 const updateProfileFormSchema = z.object({
 	userId: z.string(),
@@ -285,8 +291,9 @@ export default function Page() {
 												<Button
 													w="30%"
 													type="submit"
-													color={hasDefaultAccountLink ? "blue" : "green"}
 													variant="light"
+													disabled={isEditDisabled}
+													color={hasDefaultAccountLink ? "blue" : "green"}
 												>
 													{hasDefaultAccountLink ? "Disable" : "Enable"}
 												</Button>
diff --git a/apps/frontend/app/routes/_dashboard.settings.users.tsx b/apps/frontend/app/routes/_dashboard.settings.users.tsx
index 74a75ce0b1..8b6b8bbf7b 100644
--- a/apps/frontend/app/routes/_dashboard.settings.users.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.users.tsx
@@ -18,11 +18,11 @@ import {
 } from "@mantine/core";
 import { useDisclosure } from "@mantine/hooks";
 import {
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
+	type MetaArgs,
 	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
 } from "@remix-run/node";
-import type { MetaArgs_SingleFetch } from "@remix-run/react";
 import { Form, useLoaderData } from "@remix-run/react";
 import {
 	DeleteUserDocument,
@@ -35,6 +35,7 @@ import {
 } from "@ryot/generated/graphql/backend/graphql";
 import {
 	changeCase,
+	getActionIntent,
 	processSubmission,
 	randomString,
 	truncate,
@@ -48,7 +49,6 @@ import {
 import { forwardRef, useState } from "react";
 import { VirtuosoGrid } from "react-virtuoso";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
@@ -70,7 +70,7 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const userDetails = await redirectIfNotAuthenticatedOrUpdated(request);
 	if (userDetails.lot !== UserLot.Admin) throw redirect($path("/"));
 	const cookieName = await getEnhancedCookieName("settings.users", request);
@@ -82,16 +82,17 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		}),
 	]);
 	return { usersList, query, cookieName };
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
+export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "User Settings | Ryot" }];
 };
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return namedAction(request, {
-		delete: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("delete", async () => {
 			const submission = processSubmission(formData, deleteSchema);
 			const { deleteUser } = await serverGqlService.authenticatedRequest(
 				request,
@@ -106,8 +107,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 						: "User can not be deleted",
 				}),
 			});
-		},
-		registerNew: async () => {
+		})
+		.with("registerNew", async () => {
 			const submission = processSubmission(formData, registerFormSchema);
 			const { registerUser } = await serverGqlService.authenticatedRequest(
 				request,
@@ -142,8 +143,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 								.exhaustive(),
 				}),
 			});
-		},
-		update: async () => {
+		})
+		.with("update", async () => {
 			const submission = processSubmission(formData, updateUserSchema);
 			submission.isDisabled = submission.isDisabled === true;
 			await serverGqlService.authenticatedRequest(request, UpdateUserDocument, {
@@ -155,9 +156,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 					message: "User updated successfully",
 				}),
 			});
-		},
-	});
-});
+		})
+		.run();
+};
 
 const registerFormSchema = z.object({
 	username: z.string(),
@@ -205,8 +206,12 @@ export default function Page() {
 					withCloseButton={false}
 					centered
 				>
-					<Form replace method="POST" onSubmit={closeRegisterUserModal}>
-						<input hidden name="intent" defaultValue="registerNew" />
+					<Form
+						replace
+						method="POST"
+						onSubmit={closeRegisterUserModal}
+						action={withQuery(".", { intent: "registerNew" })}
+					>
 						<Stack>
 							<Title order={3}>Create User</Title>
 							<TextInput label="Name" required name="username" />
diff --git a/apps/frontend/app/routes/_dashboard.tsx b/apps/frontend/app/routes/_dashboard.tsx
index 6e5b43a088..f686c5bbd2 100644
--- a/apps/frontend/app/routes/_dashboard.tsx
+++ b/apps/frontend/app/routes/_dashboard.tsx
@@ -47,7 +47,6 @@ import {
 	useDisclosure,
 	useLocalStorage,
 } from "@mantine/hooks";
-import { unstable_defineLoader } from "@remix-run/node";
 import {
 	Form,
 	Link,
@@ -145,12 +144,13 @@ import {
 } from "~/lib/utilities.server";
 import { colorSchemeCookie } from "~/lib/utilities.server";
 import "@mantine/dates/styles.css";
+import type { LoaderFunctionArgs } from "@remix-run/node";
 import { useBulkEditCollection } from "~/lib/state/collection";
 import classes from "~/styles/dashboard.module.css";
 
 const discordLink = "https://discord.gg/D9XTg2a7R8";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const userDetails = await redirectIfNotAuthenticatedOrUpdated(request);
 	const [userPreferences, userCollections, { coreDetails }] = await Promise.all(
 		[
@@ -245,7 +245,7 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 
 	const decodedCookie = jwtDecode<{
 		access_link?: { id: string; is_demo?: boolean };
-	}>(getAuthorizationCookie(request));
+	}>(getAuthorizationCookie(request) ?? "");
 	const isAccessLinkSession = Boolean(decodedCookie?.access_link);
 	const isDemo = Boolean(decodedCookie?.access_link?.is_demo);
 
@@ -272,7 +272,7 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		currentColorScheme,
 		isAccessLinkSession,
 	};
-});
+};
 
 export function ErrorBoundary() {
 	const error = useRouteError() as Error;
diff --git a/apps/frontend/app/routes/actions.tsx b/apps/frontend/app/routes/actions.tsx
index cfbbeacb17..87c5586aee 100644
--- a/apps/frontend/app/routes/actions.tsx
+++ b/apps/frontend/app/routes/actions.tsx
@@ -1,6 +1,6 @@
 import {
+	type ActionFunctionArgs,
 	redirect,
-	unstable_defineAction,
 	unstable_parseMultipartFormData,
 } from "@remix-run/node";
 import {
@@ -24,6 +24,7 @@ import {
 	Visibility,
 } from "@ryot/generated/graphql/backend/graphql";
 import {
+	getActionIntent,
 	isEmpty,
 	isNumber,
 	omitBy,
@@ -54,11 +55,9 @@ const sleepForHalfSecond = async (request: Request) =>
 
 export const loader = async () => redirect($path("/"));
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	const { searchParams } = new URL(request.url);
-	const intent = searchParams.get("intent") as string;
-	invariant(intent);
+	const intent = getActionIntent(request);
 	const redirectToForm = formData.get(redirectToQueryParam);
 	let redirectTo = redirectToForm ? redirectToForm.toString() : undefined;
 	let returnData = {};
@@ -462,7 +461,7 @@ export const action = unstable_defineAction(async ({ request }) => {
 		status = 302;
 	}
 	return Response.json(returnData, { headers, status });
-});
+};
 
 const commitMediaSchema = z.object({
 	identifier: z.string(),
diff --git a/apps/frontend/app/routes/api.auth.tsx b/apps/frontend/app/routes/api.auth.tsx
index 2bc81b7352..6887c11f83 100644
--- a/apps/frontend/app/routes/api.auth.tsx
+++ b/apps/frontend/app/routes/api.auth.tsx
@@ -1,4 +1,4 @@
-import { redirect, unstable_defineLoader } from "@remix-run/node";
+import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
 import {
 	GetOidcTokenDocument,
 	LoginUserDocument,
@@ -20,7 +20,7 @@ const searchParamsSchema = z.object({ code: z.string() });
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const input = zx.parseQuery(request, searchParamsSchema);
 	const { getOidcToken } = await serverGqlService.request(
 		GetOidcTokenDocument,
@@ -59,4 +59,4 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 	}
 	console.error("Login failed:", loginUser);
 	return Response.json({ input });
-});
+};
diff --git a/apps/frontend/app/routes/api.sharing.$accessLinkId.tsx b/apps/frontend/app/routes/api.sharing.$accessLinkId.tsx
index 0e00ae33f4..0fb65b6289 100644
--- a/apps/frontend/app/routes/api.sharing.$accessLinkId.tsx
+++ b/apps/frontend/app/routes/api.sharing.$accessLinkId.tsx
@@ -1,4 +1,4 @@
-import { redirect, unstable_defineLoader } from "@remix-run/node";
+import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
 import {
 	ProcessAccessLinkDocument,
 	type ProcessAccessLinkInput,
@@ -21,7 +21,7 @@ const searchParamsSchema = z.object({
 	[redirectToQueryParam]: z.string().optional(),
 });
 
-export const loader = unstable_defineLoader(async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const routeParams = zx.parseParams(params, paramsSchema);
 	const input: ProcessAccessLinkInput = {};
@@ -53,4 +53,4 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 			message: `Encountered: ${processAccessLink.error}`,
 		}),
 	});
-});
+};
diff --git a/apps/frontend/app/routes/auth.tsx b/apps/frontend/app/routes/auth.tsx
index 3e1033a3d3..d97b49d77b 100644
--- a/apps/frontend/app/routes/auth.tsx
+++ b/apps/frontend/app/routes/auth.tsx
@@ -11,17 +11,12 @@ import {
 	TextInput,
 } from "@mantine/core";
 import {
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
+	type MetaArgs,
 	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
 } from "@remix-run/node";
-import {
-	Form,
-	Link,
-	type MetaArgs_SingleFetch,
-	useLoaderData,
-	useSearchParams,
-} from "@remix-run/react";
+import { Form, Link, useLoaderData, useSearchParams } from "@remix-run/react";
 import {
 	GetOidcRedirectUrlDocument,
 	LatestUserSummaryDocument,
@@ -31,10 +26,9 @@ import {
 	RegisterErrorVariant,
 	RegisterUserDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import { processSubmission, startCase } from "@ryot/ts-utils";
+import { getActionIntent, processSubmission, startCase } from "@ryot/ts-utils";
 import { IconAt } from "@tabler/icons-react";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
 import { safeRedirect } from "remix-utils/safe-redirect";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
@@ -59,7 +53,7 @@ const searchParamsSchema = z.object({
 export type SearchParams = z.infer<typeof searchParamsSchema> &
 	Record<string, string>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const isAuthenticated = !!getAuthorizationCookie(request);
 	if (isAuthenticated) {
@@ -94,16 +88,17 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		tokenValidForDays: coreDetails.tokenValidForDays,
 		signupAllowed: coreDetails.signupAllowed,
 	};
-});
+};
 
-export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => [
+export const meta = (_args: MetaArgs<typeof loader>) => [
 	{ title: "Authentication | Ryot" },
 ];
 
-export const action = unstable_defineAction(async ({ request }) => {
-	const formData = await request.formData();
-	return namedAction(request, {
-		register: async () => {
+export const action = async ({ request }: ActionFunctionArgs) => {
+	const formData = await request.clone().formData();
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("register", async () => {
 			const submission = parseWithZod(formData, {
 				schema: registerSchema,
 			});
@@ -148,8 +143,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 				type: "success",
 				message: "Please login with your new credentials",
 			});
-		},
-		login: async () => {
+		})
+		.with("login", async () => {
 			const submission = processSubmission(formData, loginSchema);
 			const { loginUser } = await serverGqlService.request(LoginUserDocument, {
 				input: {
@@ -185,15 +180,15 @@ export const action = unstable_defineAction(async ({ request }) => {
 			return Response.json({} as const, {
 				headers: await createToastHeaders({ message, type: "error" }),
 			});
-		},
-		getOidcRedirectUrl: async () => {
+		})
+		.with("getOidcRedirectUrl", async () => {
 			const { getOidcRedirectUrl } = await serverGqlService.request(
 				GetOidcRedirectUrlDocument,
 			);
 			return redirect(getOidcRedirectUrl);
-		},
-	});
-});
+		})
+		.run();
+};
 
 const registerSchema = z
 	.object({
diff --git a/apps/frontend/app/styles/preferences.module.css b/apps/frontend/app/styles/preferences.module.css
index dbc8384921..293324c578 100644
--- a/apps/frontend/app/styles/preferences.module.css
+++ b/apps/frontend/app/styles/preferences.module.css
@@ -1,3 +1,3 @@
 .itemDragging {
-  box-shadow: var(--mantine-shadow-sm);
+	box-shadow: var(--mantine-shadow-sm);
 }
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 0dd0e7c555..5f5be3dab2 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -7,56 +7,56 @@
 		"hoistingLimits": "workspaces"
 	},
 	"dependencies": {
-		"@conform-to/react": "1.1.5",
-		"@conform-to/zod": "1.1.5",
+		"@conform-to/react": "1.2.2",
+		"@conform-to/zod": "1.2.2",
 		"@formkit/auto-animate": "0.8.2",
-		"@hello-pangea/dnd": "16.6.0",
+		"@hello-pangea/dnd": "17.0.0",
 		"@lukemorales/query-key-factory": "1.3.4",
-		"@mantine/carousel": "7.12.1",
-		"@mantine/charts": "7.12.1",
-		"@mantine/core": "7.12.1",
-		"@mantine/dates": "7.12.1",
-		"@mantine/form": "7.12.1",
-		"@mantine/hooks": "7.12.1",
-		"@mantine/notifications": "7.12.1",
+		"@mantine/carousel": "7.13.3",
+		"@mantine/charts": "7.13.3",
+		"@mantine/core": "7.13.3",
+		"@mantine/dates": "7.13.3",
+		"@mantine/form": "7.13.3",
+		"@mantine/hooks": "7.13.3",
+		"@mantine/notifications": "7.13.3",
 		"@remix-pwa/sw": "3.0.10",
 		"@remix-pwa/worker-runtime": "2.1.4",
-		"@remix-run/node": "2.11.2",
-		"@remix-run/react": "2.11.2",
-		"@remix-run/serve": "2.11.2",
+		"@remix-run/node": "2.13.1",
+		"@remix-run/react": "2.13.1",
+		"@remix-run/serve": "2.13.1",
 		"@ryot/generated": "workspace:*",
 		"@ryot/graphql": "workspace:*",
 		"@ryot/ts-utils": "workspace:*",
-		"@tabler/icons-react": "3.12.0",
-		"@tanstack/react-query": "5.52.0",
-		"@tanstack/react-query-devtools": "5.52.0",
+		"@tabler/icons-react": "3.19.0",
+		"@tanstack/react-query": "5.59.15",
+		"@tanstack/react-query-devtools": "5.59.15",
 		"buffer": "6.0.3",
 		"clsx": "2.1.1",
-		"cookie": "0.6.0",
+		"cookie": "1.0.1",
 		"dayjs": "1.11.13",
 		"dotenv": "16.4.5",
 		"embla-carousel-react": "7.1.0",
-		"filesize": "10.1.4",
+		"filesize": "10.1.6",
 		"graphql": "16.9.0",
 		"graphql-request": "7.1.0",
 		"howler": "2.2.4",
 		"humanize-duration-ts": "2.1.1",
 		"immer": "10.1.1",
 		"isbot": "5.1.17",
-		"jotai": "2.9.3",
+		"jotai": "2.10.1",
 		"js-cookie": "3.0.5",
 		"jwt-decode": "4.0.0",
-		"mantine-datatable": "7.11.3",
+		"mantine-datatable": "7.12.4",
 		"react": "18.3.1",
 		"react-confirm": "0.3.0-7",
 		"react-dom": "18.3.1",
-		"react-virtuoso": "4.10.1",
+		"react-virtuoso": "4.12.0",
 		"react-webcam": "7.2.0",
-		"recharts": "2.12.7",
+		"recharts": "2.13.0",
 		"remix-routes": "1.7.7",
-		"remix-utils": "7.6.0",
+		"remix-utils": "7.7.0",
 		"tiny-invariant": "1.3.3",
-		"ts-pattern": "5.3.1",
+		"ts-pattern": "5.5.0",
 		"ufo": "1.5.4",
 		"usehooks-ts": "3.1.0",
 		"uuid": "10.0.0",
@@ -66,23 +66,23 @@
 	"devDependencies": {
 		"@bitprojects/umami-logger-typescript": "1.0.10",
 		"@remix-pwa/dev": "3.1.0",
-		"@remix-run/dev": "2.11.2",
+		"@remix-run/dev": "2.13.1",
 		"@types/cookie": "0.6.0",
 		"@types/crypto-js": "4.2.2",
-		"@types/howler": "2.2.11",
+		"@types/howler": "2.2.12",
 		"@types/js-cookie": "3.0.6",
-		"@types/react": "18.3.4",
-		"@types/react-dom": "18.3.0",
+		"@types/react": "18.3.11",
+		"@types/react-dom": "18.3.1",
 		"@types/uuid": "10.0.0",
-		"postcss": "8.4.41",
+		"postcss": "8.4.47",
 		"postcss-preset-mantine": "1.17.0",
 		"postcss-simple-vars": "7.0.1",
-		"remix-development-tools": "4.4.1",
+		"remix-development-tools": "4.7.3",
 		"ts-essentials": "10.0.2",
-		"typescript": "5.5.4",
+		"typescript": "5.6.3",
 		"typescript-plugin-css-modules": "5.1.0",
 		"typescript-remix-routes-plugin": "1.0.1",
-		"vite": "5.4.2",
+		"vite": "5.4.9",
 		"vite-tsconfig-paths": "5.0.1"
 	},
 	"engines": {
diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json
index 78c6e97e08..72dd2478d2 100644
--- a/apps/frontend/tsconfig.json
+++ b/apps/frontend/tsconfig.json
@@ -31,8 +31,7 @@
 			{
 				"name": "typescript-remix-routes-plugin"
 			}
-		],
-		"types": ["@remix-run/react/future/single-fetch.d.ts"]
+		]
 	},
 	"references": [
 		{
diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts
index 9417ecf65a..004c9aa9f8 100644
--- a/apps/frontend/vite.config.ts
+++ b/apps/frontend/vite.config.ts
@@ -5,6 +5,12 @@ import { remixRoutes } from "remix-routes/vite";
 import { defineConfig } from "vite";
 import tsconfigPaths from "vite-tsconfig-paths";
 
+declare module "@remix-run/server-runtime" {
+	interface Future {
+		v3_singleFetch: true;
+	}
+}
+
 export default defineConfig({
 	server: {
 		port: process.env.FRONTEND_PORT
@@ -15,7 +21,7 @@ export default defineConfig({
 	plugins: [
 		remixDevTools(),
 		remix({
-			future: { unstable_singleFetch: true },
+			future: { v3_singleFetch: true },
 		}),
 		remixRoutes(),
 		tsconfigPaths(),
diff --git a/apps/website/app/lib/components/ui/carousel.tsx b/apps/website/app/lib/components/ui/carousel.tsx
index dffc3cb36e..60c760410e 100644
--- a/apps/website/app/lib/components/ui/carousel.tsx
+++ b/apps/website/app/lib/components/ui/carousel.tsx
@@ -134,10 +134,9 @@ const Carousel = React.forwardRef<
 			>
 				<div
 					ref={ref}
+					aria-roledescription="carousel"
 					onKeyDownCapture={handleKeyDown}
 					className={cn("relative", className)}
-					role="region"
-					aria-roledescription="carousel"
 					{...props}
 				>
 					{children}
diff --git a/apps/website/app/lib/components/ui/input-otp.tsx b/apps/website/app/lib/components/ui/input-otp.tsx
index 9ab935b92c..2778f55fa5 100644
--- a/apps/website/app/lib/components/ui/input-otp.tsx
+++ b/apps/website/app/lib/components/ui/input-otp.tsx
@@ -62,7 +62,6 @@ const InputOTPSeparator = React.forwardRef<
 >(({ ...props }, ref) => (
 	<div
 		ref={ref}
-		role="separator"
 		aria-valuemax={0}
 		aria-valuemin={0}
 		aria-valuenow={0}
diff --git a/apps/website/app/lib/config.server.ts b/apps/website/app/lib/config.server.ts
index bfd09686ac..63e670ef84 100644
--- a/apps/website/app/lib/config.server.ts
+++ b/apps/website/app/lib/config.server.ts
@@ -103,21 +103,21 @@ export const sendEmail = async (
 	element: JSX.Element,
 ) => {
 	const client = createTransport({
+		secure: true,
 		host: serverVariables.SERVER_SMTP_SERVER,
 		auth: {
 			user: serverVariables.SERVER_SMTP_USER,
 			pass: serverVariables.SERVER_SMTP_PASSWORD,
 		},
-		secure: true,
 	});
-	const html = render(element, { pretty: true });
-	const text = render(element, { plainText: true });
+	const html = await render(element, { pretty: true });
+	const text = await render(element, { plainText: true });
 	const resp = await client.sendMail({
-		from: '"Ryot" <no-reply@ryot.io>',
-		to: recipient,
-		subject,
 		text,
 		html,
+		subject,
+		to: recipient,
+		from: '"Ryot" <no-reply@ryot.io>',
 	});
 	return resp.messageId;
 };
diff --git a/apps/website/app/root.tsx b/apps/website/app/root.tsx
index ecdac9ce54..97444c2bb0 100644
--- a/apps/website/app/root.tsx
+++ b/apps/website/app/root.tsx
@@ -9,11 +9,10 @@ import {
 } from "@remix-run/react";
 import { HoneypotProvider } from "remix-utils/honeypot/react";
 import "./tailwind.css";
-import {
-	type LinksFunction,
-	type MetaFunction,
-	json,
-	unstable_defineLoader,
+import type {
+	LinksFunction,
+	LoaderFunctionArgs,
+	MetaFunction,
 } from "@remix-run/node";
 import { $path } from "remix-routes";
 import { withFragment } from "ufo";
@@ -41,13 +40,13 @@ export const links: LinksFunction = () => {
 	];
 };
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const userId = await getUserIdFromCookie(request);
-	return json({
+	return {
 		isLoggedIn: !!userId,
 		honeypotInputProps: honeypot.getInputProps(),
-	});
-});
+	};
+};
 
 export const meta: MetaFunction = () => {
 	return [{ title: "Ryot" }];
diff --git a/apps/website/app/routes/_index.tsx b/apps/website/app/routes/_index.tsx
index dbcbd04acc..7e8df9cb38 100644
--- a/apps/website/app/routes/_index.tsx
+++ b/apps/website/app/routes/_index.tsx
@@ -1,12 +1,16 @@
 import TTLCache from "@isaacs/ttlcache";
 import {
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
 	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
 } from "@remix-run/node";
 import { Form, Link, useLoaderData } from "@remix-run/react";
 import LoginCodeEmail from "@ryot/transactional/emails/LoginCode";
-import { processSubmission, randomString } from "@ryot/ts-utils";
+import {
+	getActionIntent,
+	processSubmission,
+	randomString,
+} from "@ryot/ts-utils";
 import {
 	IconBrandDiscord,
 	IconBrandGithub,
@@ -20,7 +24,7 @@ import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
 import { $path } from "remix-routes";
 import { HoneypotInputs } from "remix-utils/honeypot/react";
 import { SpamError } from "remix-utils/honeypot/server";
-import { namedAction } from "remix-utils/named-action";
+import { match } from "ts-pattern";
 import { withFragment, withQuery } from "ufo";
 import { z } from "zod";
 import { zx } from "zodix";
@@ -61,21 +65,22 @@ const searchParamsSchema = z.object({
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const userId = await getUserIdFromCookie(request);
 	return { prices, query, isLoggedIn: !!userId };
-});
+};
 
 const otpCodesCache = new TTLCache<string, string>({
 	ttl: dayjs.duration(5, "minutes").asMilliseconds(),
 	max: 1000,
 });
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return await namedAction(request, {
-		sendLoginCode: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("sendLoginCode", async () => {
 			const { email } = processSubmission(formData, emailSchema);
 			const otpCode = randomString(6);
 			otpCodesCache.set(email, otpCode);
@@ -86,8 +91,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 				LoginCodeEmail({ code: otpCode }),
 			);
 			return redirect(withQuery(withFragment(".", "start-here"), { email }));
-		},
-		registerWithEmail: async () => {
+		})
+		.with("registerWithEmail", async () => {
 			const submission = processSubmission(formData, registerSchema);
 			const otpCode = otpCodesCache.get(submission.email);
 			if (otpCode !== submission.otpCode) throw new Error("Invalid OTP code.");
@@ -105,13 +110,13 @@ export const action = unstable_defineAction(async ({ request }) => {
 			return redirect($path("/me"), {
 				headers: { "set-cookie": await authCookie.serialize(customerId) },
 			});
-		},
-		registerWithOidc: async () => {
+		})
+		.with("registerWithOidc", async () => {
 			const client = await oauthClient();
 			const redirectUrl = client.authorizationUrl({ scope: "openid email" });
 			return redirect(redirectUrl);
-		},
-		contactSubmission: async () => {
+		})
+		.with("contactSubmission", async () => {
 			let isSpam = false;
 			try {
 				honeypot.check(formData);
@@ -130,9 +135,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 			return redirect(
 				withQuery(withFragment(".", "contact"), { contactSubmission: true }),
 			);
-		},
-	});
-});
+		})
+		.run();
+};
 
 const intentSchema = z.object({ intent: z.string() });
 
@@ -294,17 +299,15 @@ export default function Page() {
 							</Link>
 						) : (
 							<>
-								<Form method="POST" className="flex gap-2 flex-none">
-									<input
-										readOnly
-										type="hidden"
-										name="intent"
-										value={
-											loaderData.query.email
-												? "registerWithEmail"
-												: "sendLoginCode"
-										}
-									/>
+								<Form
+									method="POST"
+									className="flex gap-2 flex-none"
+									action={withQuery(".", {
+										intent: loaderData.query.email
+											? "registerWithEmail"
+											: "sendLoginCode",
+									})}
+								>
 									{loaderData.query.email ? (
 										<>
 											<input
@@ -343,12 +346,10 @@ export default function Page() {
 									</Button>
 								</Form>
 								<p className="text-xs">OR</p>
-								<Form method="POST">
-									<input
-										type="hidden"
-										name="intent"
-										defaultValue="registerWithOidc"
-									/>
+								<Form
+									method="POST"
+									action={withQuery(".", { intent: "registerWithOidc" })}
+								>
 									<Button
 										variant="outline"
 										className="inline-flex h-10 items-center justify-center rounded-md px-8 text-sm font-medium"
@@ -439,13 +440,9 @@ export default function Page() {
 					) : (
 						<Form
 							method="POST"
+							action={withQuery(".", { intent: "contactSubmission" })}
 							className="flex flex-col items-center justify-center pt-12 gap-y-4 gap-x-4"
 						>
-							<input
-								type="hidden"
-								name="intent"
-								defaultValue="contactSubmission"
-							/>
 							<HoneypotInputs />
 							<Input
 								type="email"
diff --git a/apps/website/app/routes/auth.tsx b/apps/website/app/routes/auth.tsx
index 37b5642bcf..2d72085db7 100644
--- a/apps/website/app/routes/auth.tsx
+++ b/apps/website/app/routes/auth.tsx
@@ -1,17 +1,21 @@
 import TTLCache from "@isaacs/ttlcache";
 import {
+	type ActionFunctionArgs,
+	type LoaderFunctionArgs,
 	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
 } from "@remix-run/node";
 import { Form, useLoaderData } from "@remix-run/react";
 import LoginCodeEmail from "@ryot/transactional/emails/LoginCode";
-import { processSubmission, randomString } from "@ryot/ts-utils";
+import {
+	getActionIntent,
+	processSubmission,
+	randomString,
+} from "@ryot/ts-utils";
 import dayjs from "dayjs";
 import duration from "dayjs/plugin/duration";
 import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
+import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { z } from "zod";
 import { zx } from "zodix";
@@ -39,15 +43,16 @@ const otpCodesCache = new TTLCache<string, string>({
 	max: 1000,
 });
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchParamsSchema);
 	return { query };
-});
+};
 
-export const action = unstable_defineAction(async ({ request }) => {
+export const action = async ({ request }: ActionFunctionArgs) => {
 	const formData = await request.clone().formData();
-	return await namedAction(request, {
-		sendLoginCode: async () => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("sendLoginCode", async () => {
 			const { email } = processSubmission(formData, emailSchema);
 			const otpCode = randomString(6);
 			otpCodesCache.set(email, otpCode);
@@ -57,8 +62,8 @@ export const action = unstable_defineAction(async ({ request }) => {
 				LoginCodeEmail({ code: otpCode }),
 			);
 			return redirect(withQuery(".", { email }));
-		},
-		registerWithEmail: async () => {
+		})
+		.with("registerWithEmail", async () => {
 			const submission = processSubmission(formData, registerSchema);
 			const otpCode = otpCodesCache.get(submission.email);
 			if (otpCode !== submission.otpCode) throw new Error("Invalid OTP code.");
@@ -76,14 +81,14 @@ export const action = unstable_defineAction(async ({ request }) => {
 			return redirect($path("/"), {
 				headers: { "set-cookie": await authCookie.serialize(customerId) },
 			});
-		},
-		registerWithOidc: async () => {
+		})
+		.with("registerWithOidc", async () => {
 			const client = await oauthClient();
 			const redirectUrl = client.authorizationUrl({ scope: "openid email" });
 			return redirect(redirectUrl);
-		},
-	});
-});
+		})
+		.run();
+};
 
 const emailSchema = z.object({
 	email: z.string().email(),
@@ -144,8 +149,8 @@ export default function Index() {
 			</Form>
 			<Form
 				method="POST"
-				action={withQuery("?index", { intent: "registerWithOidc" })}
 				className="rounded-2xl border space-y-4 m-4 p-4"
+				action={withQuery(".", { intent: "registerWithOidc" })}
 			>
 				<Button type="submit">Sign up with Google</Button>
 			</Form>
diff --git a/apps/website/app/routes/callback.tsx b/apps/website/app/routes/callback.tsx
index 6298bf5b0b..6b00293353 100644
--- a/apps/website/app/routes/callback.tsx
+++ b/apps/website/app/routes/callback.tsx
@@ -1,4 +1,4 @@
-import { redirect, unstable_defineLoader } from "@remix-run/node";
+import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
 import { eq } from "drizzle-orm";
 import { $path } from "remix-routes";
 import { match } from "ts-pattern";
@@ -10,7 +10,7 @@ import {
 	oauthClient,
 } from "~/lib/config.server";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const client = await oauthClient();
 	const params = client.callbackParams(request.url);
 	const tokenSet = await client.callback(OAUTH_CALLBACK_URL, params, {
@@ -39,4 +39,4 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 	return redirect($path("/me"), {
 		headers: { "set-cookie": await authCookie.serialize(customerId) },
 	});
-});
+};
diff --git a/apps/website/app/routes/me.tsx b/apps/website/app/routes/me.tsx
index c5196241c8..864161dfeb 100644
--- a/apps/website/app/routes/me.tsx
+++ b/apps/website/app/routes/me.tsx
@@ -3,17 +3,16 @@ import {
 	type Paddle,
 	initializePaddle,
 } from "@paddle/paddle-js";
-import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
+import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
 import { Form, redirect, useLoaderData, useSubmit } from "@remix-run/react";
 import { RegisterUserDocument } from "@ryot/generated/graphql/backend/graphql";
 import PurchaseCompleteEmail from "@ryot/transactional/emails/PurchaseComplete";
-import { changeCase, randomString } from "@ryot/ts-utils";
+import { changeCase, getActionIntent, randomString } from "@ryot/ts-utils";
 import { Unkey } from "@unkey/api";
 import dayjs from "dayjs";
 import { eq } from "drizzle-orm";
 import { useEffect, useState } from "react";
 import { $path } from "remix-routes";
-import { namedAction } from "remix-utils/named-action";
 import { P, match } from "ts-pattern";
 import { withFragment, withQuery } from "ufo";
 import { customers } from "~/drizzle/schema.server";
@@ -34,7 +33,7 @@ import {
 	serverVariables,
 } from "~/lib/config.server";
 
-export const loader = unstable_defineLoader(async ({ request }) => {
+export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const userId = await getUserIdFromCookie(request);
 	const isLoggedIn = !!userId;
 	if (!isLoggedIn) return redirect(withFragment($path("/"), "start-here"));
@@ -61,15 +60,16 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		isSandbox: !!serverVariables.PADDLE_SANDBOX,
 		clientToken: serverVariables.PADDLE_CLIENT_TOKEN,
 	};
-});
+};
 
-export const action = unstable_defineAction(async ({ request }) => {
-	return await namedAction(request.clone(), {
-		logout: async () => {
+export const action = async ({ request }: ActionFunctionArgs) => {
+	const intent = getActionIntent(request);
+	return await match(intent)
+		.with("logout", async () => {
 			const cookies = await authCookie.serialize("", { expires: new Date(0) });
 			return Response.json({}, { headers: { "set-cookie": cookies } });
-		},
-		processPurchase: async () => {
+		})
+		.with("processPurchase", async () => {
 			const userId = await getUserIdFromCookie(request);
 			if (!userId)
 				throw new Error("You must be logged in to buy a subscription");
@@ -161,9 +161,9 @@ export const action = unstable_defineAction(async ({ request }) => {
 				})
 				.where(eq(customers.id, userId));
 			return Response.json({});
-		},
-	});
-});
+		})
+		.run();
+};
 
 export default function Index() {
 	const loaderData = useLoaderData<typeof loader>();
@@ -183,7 +183,7 @@ export default function Index() {
 						{
 							method: "POST",
 							encType: "application/json",
-							action: withQuery("?index", { intent: "processPurchase" }),
+							action: withQuery(".", { intent: "processPurchase" }),
 						},
 					);
 				}
@@ -256,9 +256,9 @@ export default function Index() {
 			)}
 			<Form
 				method="POST"
+				action={withQuery(".", { intent: "logout" })}
 				className="flex w-full items-end justify-end px-4 md:px-10 pb-6"
 			>
-				<input type="hidden" name="intent" defaultValue="logout" />
 				<Button type="submit">Sign out</Button>
 			</Form>
 		</>
diff --git a/apps/website/app/tailwind.css b/apps/website/app/tailwind.css
index d6356b63a6..cbd092db62 100644
--- a/apps/website/app/tailwind.css
+++ b/apps/website/app/tailwind.css
@@ -3,70 +3,75 @@
 @tailwind utilities;
 
 @layer base {
-  :root {
-    --background: 0 0% 100%;
-    --foreground: 240 10% 3.9%;
-    --card: 0 0% 100%;
-    --card-foreground: 240 10% 3.9%;
-    --popover: 0 0% 100%;
-    --popover-foreground: 240 10% 3.9%;
-    --primary: 240 5.9% 10%;
-    --primary-foreground: 0 0% 98%;
-    --secondary: 240 4.8% 95.9%;
-    --secondary-foreground: 240 5.9% 10%;
-    --muted: 240 4.8% 95.9%;
-    --muted-foreground: 240 3.8% 46.1%;
-    --accent: 240 4.8% 95.9%;
-    --accent-foreground: 240 5.9% 10%;
-    --destructive: 0 84.2% 60.2%;
-    --destructive-foreground: 0 0% 98%;
-    --border: 240 5.9% 90%;
-    --input: 240 5.9% 90%;
-    --ring: 240 10% 3.9%;
-    --radius: 0.5rem;
-    --chart-1: 12 76% 61%;
-    --chart-2: 173 58% 39%;
-    --chart-3: 197 37% 24%;
-    --chart-4: 43 74% 66%;
-    --chart-5: 27 87% 67%;
-  }
+	:root {
+		--background: 0 0% 100%;
+		--foreground: 240 10% 3.9%;
+		--card: 0 0% 100%;
+		--card-foreground: 240 10% 3.9%;
+		--popover: 0 0% 100%;
+		--popover-foreground: 240 10% 3.9%;
+		--primary: 240 5.9% 10%;
+		--primary-foreground: 0 0% 98%;
+		--secondary: 240 4.8% 95.9%;
+		--secondary-foreground: 240 5.9% 10%;
+		--muted: 240 4.8% 95.9%;
+		--muted-foreground: 240 3.8% 46.1%;
+		--accent: 240 4.8% 95.9%;
+		--accent-foreground: 240 5.9% 10%;
+		--destructive: 0 84.2% 60.2%;
+		--destructive-foreground: 0 0% 98%;
+		--border: 240 5.9% 90%;
+		--input: 240 5.9% 90%;
+		--ring: 240 10% 3.9%;
+		--radius: 0.5rem;
+		--chart-1: 12 76% 61%;
+		--chart-2: 173 58% 39%;
+		--chart-3: 197 37% 24%;
+		--chart-4: 43 74% 66%;
+		--chart-5: 27 87% 67%;
+	}
 
-  .dark {
-    --background: 240 10% 3.9%;
-    --foreground: 0 0% 98%;
-    --card: 240 10% 3.9%;
-    --card-foreground: 0 0% 98%;
-    --popover: 240 10% 3.9%;
-    --popover-foreground: 0 0% 98%;
-    --primary: 0 0% 98%;
-    --primary-foreground: 240 5.9% 10%;
-    --secondary: 240 3.7% 15.9%;
-    --secondary-foreground: 0 0% 98%;
-    --muted: 240 3.7% 15.9%;
-    --muted-foreground: 240 5% 64.9%;
-    --accent: 240 3.7% 15.9%;
-    --accent-foreground: 0 0% 98%;
-    --destructive: 0 62.8% 30.6%;
-    --destructive-foreground: 0 0% 98%;
-    --border: 240 3.7% 15.9%;
-    --input: 240 3.7% 15.9%;
-    --ring: 240 4.9% 83.9%;
-    --chart-1: 220 70% 50%;
-    --chart-2: 160 60% 45%;
-    --chart-3: 30 80% 55%;
-    --chart-4: 280 65% 60%;
-    --chart-5: 340 75% 55%;
-  }
+	.dark {
+		--background: 240 10% 3.9%;
+		--foreground: 0 0% 98%;
+		--card: 240 10% 3.9%;
+		--card-foreground: 0 0% 98%;
+		--popover: 240 10% 3.9%;
+		--popover-foreground: 0 0% 98%;
+		--primary: 0 0% 98%;
+		--primary-foreground: 240 5.9% 10%;
+		--secondary: 240 3.7% 15.9%;
+		--secondary-foreground: 0 0% 98%;
+		--muted: 240 3.7% 15.9%;
+		--muted-foreground: 240 5% 64.9%;
+		--accent: 240 3.7% 15.9%;
+		--accent-foreground: 0 0% 98%;
+		--destructive: 0 62.8% 30.6%;
+		--destructive-foreground: 0 0% 98%;
+		--border: 240 3.7% 15.9%;
+		--input: 240 3.7% 15.9%;
+		--ring: 240 4.9% 83.9%;
+		--chart-1: 220 70% 50%;
+		--chart-2: 160 60% 45%;
+		--chart-3: 30 80% 55%;
+		--chart-4: 280 65% 60%;
+		--chart-5: 340 75% 55%;
+	}
 }
 
 @layer base {
-  * {
-    @apply border-border;
-  }
-  body {
-    @apply bg-background text-foreground font-body;
-  }
-  h1, h2, h3, h4, h5, h6 {
-    @apply font-heading;
-  }
+	* {
+		@apply border-border;
+	}
+	body {
+		@apply bg-background text-foreground font-body;
+	}
+	h1,
+	h2,
+	h3,
+	h4,
+	h5,
+	h6 {
+		@apply font-heading;
+	}
 }
diff --git a/apps/website/package.json b/apps/website/package.json
index 8c96636c1d..b62f0aa14c 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -7,69 +7,69 @@
 		"hoistingLimits": "workspaces"
 	},
 	"dependencies": {
-		"@conform-to/zod": "1.1.5",
+		"@conform-to/zod": "1.2.2",
 		"@isaacs/ttlcache": "1.4.1",
-		"@paddle/paddle-js": "1.2.0",
-		"@paddle/paddle-node-sdk": "1.4.1",
+		"@paddle/paddle-js": "1.2.3",
+		"@paddle/paddle-node-sdk": "1.9.1",
 		"@radix-ui/react-label": "2.1.0",
 		"@radix-ui/react-slot": "1.1.0",
-		"@radix-ui/react-tooltip": "1.1.2",
-		"@react-email/components": "0.0.22",
-		"@remix-run/node": "2.10.3",
-		"@remix-run/react": "2.10.3",
-		"@remix-run/serve": "2.10.3",
+		"@radix-ui/react-tooltip": "1.1.3",
+		"@react-email/components": "0.0.25",
+		"@remix-run/node": "2.13.1",
+		"@remix-run/react": "2.13.1",
+		"@remix-run/serve": "2.13.1",
 		"@ryot/generated": "workspace:*",
 		"@ryot/graphql": "workspace:*",
 		"@ryot/transactional": "workspace:*",
 		"@ryot/ts-utils": "workspace:*",
-		"@tabler/icons-react": "3.11.0",
-		"@unkey/api": "0.23.0",
+		"@tabler/icons-react": "3.19.0",
+		"@unkey/api": "0.26.2",
 		"class-variance-authority": "0.7.0",
 		"clsx": "2.1.1",
 		"crypto-js": "4.2.0",
-		"dayjs": "1.11.12",
-		"drizzle-orm": "0.32.1",
-		"embla-carousel-autoplay": "8.1.7",
-		"embla-carousel-react": "8.1.7",
+		"dayjs": "1.11.13",
+		"drizzle-orm": "0.35.3",
+		"embla-carousel-autoplay": "8.3.0",
+		"embla-carousel-react": "8.3.0",
 		"graphql": "16.9.0",
 		"graphql-request": "7.1.0",
 		"humanize-duration-ts": "2.1.1",
 		"input-otp": "1.2.4",
-		"isbot": "4.1.0",
+		"isbot": "5.1.17",
 		"lodash": "4.17.21",
-		"lucide-react": "0.414.0",
+		"lucide-react": "0.453.0",
 		"next-themes": "0.3.0",
-		"nodemailer": "6.9.14",
+		"nodemailer": "6.9.15",
 		"openid-client": "5.6.5",
 		"postgres": "3.4.4",
-		"react": "18.2.0",
-		"react-dom": "18.2.0",
-		"remix-routes": "1.7.6",
-		"remix-utils": "7.6.0",
+		"react": "18.3.1",
+		"react-dom": "18.3.1",
+		"remix-routes": "1.7.7",
+		"remix-utils": "7.7.0",
 		"sonner": "1.5.0",
-		"tailwind-merge": "2.4.0",
+		"tailwind-merge": "2.5.4",
 		"tailwindcss-animate": "1.0.7",
-		"ts-pattern": "5.2.0",
+		"ts-pattern": "5.5.0",
 		"ufo": "1.5.4",
 		"zod": "3.23.8",
 		"zodix": "0.4.4"
 	},
 	"devDependencies": {
-		"@remix-run/dev": "2.10.3",
-		"@tailwindcss/typography": "0.5.13",
-		"@types/crypto-js": "^4",
-		"@types/lodash": "4.17.7",
-		"@types/nodemailer": "6",
-		"@types/react": "18.2.20",
-		"@types/react-dom": "18.2.7",
-		"autoprefixer": "10.4.19",
-		"drizzle-kit": "0.23.0",
-		"postcss": "8.4.38",
-		"tailwindcss": "3.4.4",
-		"typescript": "5.1.6",
+		"@remix-run/dev": "2.13.1",
+		"@tailwindcss/typography": "0.5.15",
+		"@types/crypto-js": "^4.2.2",
+		"@types/lodash": "4.17.12",
+		"@types/nodemailer": "6.4.16",
+		"@types/react": "18.3.11",
+		"@types/react-dom": "18.3.1",
+		"autoprefixer": "10.4.20",
+		"drizzle-kit": "0.26.2",
+		"postcss": "8.4.47",
+		"tailwindcss": "3.4.14",
+		"typescript": "5.6.3",
 		"typescript-remix-routes-plugin": "1.0.1",
-		"vite": "5.1.0",
-		"vite-tsconfig-paths": "4.2.1"
+		"vite": "5.4.9",
+		"vite-tsconfig-paths": "5.0.1"
 	},
 	"engines": {
 		"node": ">=20.0.0"
diff --git a/apps/website/vite.config.ts b/apps/website/vite.config.ts
index 1180757683..b00785e4fe 100644
--- a/apps/website/vite.config.ts
+++ b/apps/website/vite.config.ts
@@ -3,14 +3,20 @@ import { remixRoutes } from "remix-routes/vite";
 import { defineConfig } from "vite";
 import tsconfigPaths from "vite-tsconfig-paths";
 
+declare module "@remix-run/server-runtime" {
+	interface Future {
+		v3_singleFetch: true;
+	}
+}
+
 export default defineConfig({
 	plugins: [
 		remix({
 			future: {
-				unstable_singleFetch: true,
+				v3_singleFetch: true,
 				v3_fetcherPersist: true,
-				v3_relativeSplatPath: true,
 				v3_throwAbortReason: true,
+				v3_relativeSplatPath: true,
 			},
 		}),
 		remixRoutes(),
diff --git a/crates/background/Cargo.toml b/crates/background/Cargo.toml
index 858b994826..03a49d4361 100644
--- a/crates/background/Cargo.toml
+++ b/crates/background/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
 apalis = { workspace = true }
 chrono = { workspace = true }
 chrono-tz = { workspace = true }
+database-models = { path = "../models/database" }
 enums = { path = "../enums" }
 fitness-models = { path = "../models/fitness" }
 media-models = { path = "../models/media" }
diff --git a/crates/background/src/lib.rs b/crates/background/src/lib.rs
index a97203d983..3b0cdb57dc 100644
--- a/crates/background/src/lib.rs
+++ b/crates/background/src/lib.rs
@@ -1,6 +1,7 @@
 use apalis::prelude::{Job, Message};
 use chrono::DateTime;
 use chrono_tz::Tz;
+use database_models::seen;
 use enums::{MediaLot, MediaSource};
 use fitness_models::GithubExercise;
 use media_models::{DeployImportJobInput, ProgressUpdateInput, ReviewPostedEvent};
@@ -37,6 +38,8 @@ pub enum ApplicationJob {
     UpdateExerciseLibrary,
     SyncIntegrationsData,
     HandleEntityAddedToCollectionEvent(Uuid),
+    HandleAfterMediaSeenTasks(seen::Model),
+    HandleOnSeenComplete(String),
 }
 
 impl Message for ApplicationJob {
diff --git a/crates/enums/src/lib.rs b/crates/enums/src/lib.rs
index a118a35211..fead503a2a 100644
--- a/crates/enums/src/lib.rs
+++ b/crates/enums/src/lib.rs
@@ -454,12 +454,13 @@ pub enum IntegrationLot {
 #[serde(rename_all = "snake_case")]
 pub enum IntegrationProvider {
     Audiobookshelf,
-    Jellyfin,
+    JellyfinSink,
     Emby,
     Plex,
     Kodi,
     Radarr,
     Sonarr,
+    JellyfinPush,
     Komga,
     GenericJson,
 }
diff --git a/crates/migrations/src/lib.rs b/crates/migrations/src/lib.rs
index fd2da26dae..a2605fafae 100644
--- a/crates/migrations/src/lib.rs
+++ b/crates/migrations/src/lib.rs
@@ -45,6 +45,7 @@ mod m20241006_changes_for_issue_1056;
 mod m20241010_changes_for_issue_708;
 mod m20241013_changes_for_issue_1052;
 mod m20241019_changes_for_issue_929;
+mod m20241019_changes_for_issue_964;
 
 pub use m20230410_create_metadata::Metadata as AliasedMetadata;
 pub use m20230413_create_person::Person as AliasedPerson;
@@ -110,6 +111,7 @@ impl MigratorTrait for Migrator {
             Box::new(m20241010_changes_for_issue_708::Migration),
             Box::new(m20241013_changes_for_issue_1052::Migration),
             Box::new(m20241019_changes_for_issue_929::Migration),
+            Box::new(m20241019_changes_for_issue_964::Migration),
         ]
     }
 }
diff --git a/crates/migrations/src/m20240827_create_daily_user_activity.rs b/crates/migrations/src/m20240827_create_daily_user_activity.rs
index e3610cfd5b..0d0f9c4142 100644
--- a/crates/migrations/src/m20240827_create_daily_user_activity.rs
+++ b/crates/migrations/src/m20240827_create_daily_user_activity.rs
@@ -39,6 +39,7 @@ pub enum DailyUserActivity {
     ShowCount,
     ShowDuration,
     VideoGameCount,
+    VideoGameDuration,
     VisualNovelCount,
     VisualNovelDuration,
     WorkoutPersonalBests,
@@ -96,6 +97,7 @@ impl MigrationTrait for Migration {
                     .col(integer_not_null(DailyUserActivity::ShowCount))
                     .col(integer_not_null(DailyUserActivity::ShowDuration))
                     .col(integer_not_null(DailyUserActivity::VideoGameCount))
+                    .col(integer_not_null(DailyUserActivity::VideoGameDuration))
                     .col(integer_not_null(DailyUserActivity::VisualNovelCount))
                     .col(integer_not_null(DailyUserActivity::VisualNovelDuration))
                     .col(integer_not_null(DailyUserActivity::WorkoutPersonalBests))
diff --git a/crates/migrations/src/m20241019_changes_for_issue_964.rs b/crates/migrations/src/m20241019_changes_for_issue_964.rs
new file mode 100644
index 0000000000..a1da3a8448
--- /dev/null
+++ b/crates/migrations/src/m20241019_changes_for_issue_964.rs
@@ -0,0 +1,23 @@
+use sea_orm_migration::prelude::*;
+
+#[derive(DeriveMigrationName)]
+pub struct Migration;
+
+#[async_trait::async_trait]
+impl MigrationTrait for Migration {
+    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
+        let db = manager.get_connection();
+        db.execute_unprepared(
+            r#"
+UPDATE "integration" SET "provider" = 'jellyfin_sink' WHERE "provider" = 'jellyfin';
+ALTER TABLE "daily_user_activity" ADD COLUMN IF NOT EXISTS "video_game_duration" INTEGER NOT NULL DEFAULT 0;
+        "#,
+        )
+        .await?;
+        Ok(())
+    }
+
+    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+        Ok(())
+    }
+}
diff --git a/crates/models/database/src/daily_user_activity.rs b/crates/models/database/src/daily_user_activity.rs
index dad603d890..73fdd5c14c 100644
--- a/crates/models/database/src/daily_user_activity.rs
+++ b/crates/models/database/src/daily_user_activity.rs
@@ -32,6 +32,7 @@ pub struct Model {
     pub show_count: i32,
     pub show_duration: i32,
     pub video_game_count: i32,
+    pub video_game_duration: i32,
     pub visual_novel_count: i32,
     pub visual_novel_duration: i32,
     pub workout_personal_bests: i32,
diff --git a/crates/models/media/src/lib.rs b/crates/models/media/src/lib.rs
index d40dec8425..fb319a47f8 100644
--- a/crates/models/media/src/lib.rs
+++ b/crates/models/media/src/lib.rs
@@ -931,22 +931,30 @@ pub struct PersonStateChanges {
 #[serde(rename_all = "snake_case")]
 pub struct IntegrationProviderSpecifics {
     pub plex_username: Option<String>,
+
     pub audiobookshelf_base_url: Option<String>,
     pub audiobookshelf_token: Option<String>,
+
     pub komga_base_url: Option<String>,
     pub komga_username: Option<String>,
     pub komga_password: Option<String>,
     pub komga_provider: Option<MediaSource>,
+
     pub radarr_base_url: Option<String>,
     pub radarr_api_key: Option<String>,
     pub radarr_profile_id: Option<i32>,
     pub radarr_root_folder_path: Option<String>,
     pub radarr_sync_collection_ids: Option<Vec<String>>,
+
     pub sonarr_base_url: Option<String>,
     pub sonarr_api_key: Option<String>,
     pub sonarr_profile_id: Option<i32>,
     pub sonarr_root_folder_path: Option<String>,
     pub sonarr_sync_collection_ids: Option<Vec<String>>,
+
+    pub jellyfin_push_base_url: Option<String>,
+    pub jellyfin_push_username: Option<String>,
+    pub jellyfin_push_password: Option<String>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
@@ -1625,6 +1633,7 @@ pub struct DailyUserActivityItem {
     pub show_count: i64,
     pub total_show_duration: i64,
     pub video_game_count: i64,
+    pub total_video_game_duration: i64,
     pub visual_novel_count: i64,
     pub total_visual_novel_duration: i64,
     pub total_workout_personal_bests: i64,
diff --git a/crates/services/exporter/Cargo.toml b/crates/services/exporter/Cargo.toml
index cf67ce8c4e..1d212e3daa 100644
--- a/crates/services/exporter/Cargo.toml
+++ b/crates/services/exporter/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-apalis = { workspace = true }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
 chrono = { workspace = true }
diff --git a/crates/services/exporter/src/lib.rs b/crates/services/exporter/src/lib.rs
index 2ac943aae4..52d1606c22 100644
--- a/crates/services/exporter/src/lib.rs
+++ b/crates/services/exporter/src/lib.rs
@@ -1,6 +1,5 @@
 use std::{collections::HashMap, fs::File as StdFile, path::PathBuf, sync::Arc};
 
-use apalis::prelude::MessageQueue;
 use async_graphql::{Error, Result};
 use background::ApplicationJob;
 use chrono::{DateTime, Utc};
@@ -55,11 +54,8 @@ pub struct ExporterService(pub Arc<SupportingService>);
 impl ExporterService {
     pub async fn deploy_export_job(&self, user_id: String) -> Result<bool> {
         self.0
-            .perform_application_job
-            .clone()
-            .enqueue(ApplicationJob::PerformExport(user_id))
-            .await
-            .unwrap();
+            .perform_application_job(ApplicationJob::PerformExport(user_id))
+            .await?;
         Ok(true)
     }
 
diff --git a/crates/services/fitness/Cargo.toml b/crates/services/fitness/Cargo.toml
index 4d6341b732..7ee7bda38e 100644
--- a/crates/services/fitness/Cargo.toml
+++ b/crates/services/fitness/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-apalis = { workspace = true }
 application-utils = { path = "../../utils/application" }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
diff --git a/crates/services/fitness/src/lib.rs b/crates/services/fitness/src/lib.rs
index 35b1efdc22..624cb285f1 100644
--- a/crates/services/fitness/src/lib.rs
+++ b/crates/services/fitness/src/lib.rs
@@ -1,6 +1,5 @@
 use std::sync::Arc;
 
-use apalis::prelude::MessageQueue;
 use application_utils::GraphqlRepresentation;
 use async_graphql::{Error, Result};
 use background::ApplicationJob;
@@ -437,11 +436,8 @@ impl ExerciseService {
         let exercises = self.get_all_exercises_from_dataset().await?;
         for exercise in exercises {
             self.0
-                .perform_application_job
-                .clone()
-                .enqueue(ApplicationJob::UpdateGithubExerciseJob(exercise))
-                .await
-                .unwrap();
+                .perform_application_job(ApplicationJob::UpdateGithubExerciseJob(exercise))
+                .await?;
         }
         Ok(true)
     }
@@ -569,8 +565,7 @@ impl ExerciseService {
             }
             if new_wkt.is_changed() {
                 new_wkt.update(&self.0.db).await?;
-                deploy_job_to_re_evaluate_user_workouts(&user_id, &self.0.perform_application_job)
-                    .await;
+                deploy_job_to_re_evaluate_user_workouts(&user_id, &self.0).await;
                 Ok(true)
             } else {
                 Ok(false)
@@ -660,11 +655,8 @@ impl ExerciseService {
         }
         wkt.delete(&self.0.db).await?;
         self.0
-            .perform_application_job
-            .clone()
-            .enqueue(ApplicationJob::ReEvaluateUserWorkouts(user_id))
-            .await
-            .ok();
+            .perform_application_job(ApplicationJob::ReEvaluateUserWorkouts(user_id))
+            .await?;
         Ok(true)
     }
 
diff --git a/crates/services/importer/Cargo.toml b/crates/services/importer/Cargo.toml
index a03ca9cb91..af3759a665 100644
--- a/crates/services/importer/Cargo.toml
+++ b/crates/services/importer/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 anyhow = { workspace = true }
-apalis = { workspace = true }
 application-utils = { path = "../../utils/application" }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
@@ -16,6 +15,7 @@ csv = "=1.3.0"
 data-encoding = "=2.6.0"
 enum_meta = { workspace = true }
 env-utils = { path = "../../utils/env" }
+external-utils = { path = "../../utils/external" }
 common-models = { path = "../../models/common" }
 common-utils = { path = "../../utils/common" }
 database-models = { path = "../../models/database" }
diff --git a/crates/services/importer/src/jellyfin.rs b/crates/services/importer/src/jellyfin.rs
index d949d8b5e7..6889398e45 100644
--- a/crates/services/importer/src/jellyfin.rs
+++ b/crates/services/importer/src/jellyfin.rs
@@ -1,114 +1,27 @@
 use async_graphql::Result;
-use common_utils::{ryot_log, APPLICATION_JSON_HEADER, USER_AGENT_STR};
+use common_utils::ryot_log;
 use dependent_models::ImportResult;
 use enum_meta::HashMap;
 use enums::{MediaLot, MediaSource};
+use external_utils::jellyfin::{get_authenticated_client, ItemResponse, ItemsResponse, MediaType};
 use media_models::{
     DeployUrlAndKeyAndUsernameImportInput, ImportOrExportMediaItem, ImportOrExportMediaItemSeen,
 };
-use reqwest::{
-    header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, USER_AGENT},
-    Client, ClientBuilder,
-};
-use sea_orm::prelude::DateTimeUtc;
-use serde::{Deserialize, Serialize};
 use serde_json::json;
 
 use super::{ImportFailStep, ImportFailedItem};
 
-static EMBY_HEADER_VALUE: &str =
-    r#"MediaBrowser , Client="other", Device="script", DeviceId="script", Version="0.0.0""#;
-
-#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
-enum MediaType {
-    Movie,
-    Series,
-    Episode,
-    #[serde(untagged)]
-    Unknown(String),
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "PascalCase")]
-struct ItemProviderIdsPayload {
-    tmdb: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "PascalCase")]
-struct ItemUserData {
-    last_played_date: Option<DateTimeUtc>,
-    is_favorite: Option<bool>,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "PascalCase")]
-struct ItemResponse {
-    id: String,
-    name: String,
-    #[serde(rename = "Type")]
-    type_: Option<MediaType>,
-    index_number: Option<i32>,
-    series_id: Option<String>,
-    series_name: Option<String>,
-    user_data: Option<ItemUserData>,
-    parent_index_number: Option<i32>,
-    provider_ids: Option<ItemProviderIdsPayload>,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "PascalCase")]
-struct ItemsResponse {
-    items: Vec<ItemResponse>,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "PascalCase")]
-struct AuthenticateResponse {
-    user: ItemResponse,
-    access_token: String,
-}
-
 pub async fn import(input: DeployUrlAndKeyAndUsernameImportInput) -> Result<ImportResult> {
-    let uri = format!("{}/Users/AuthenticateByName", input.api_url);
-    let client = Client::new();
-    let authenticate = client
-        .post(uri)
-        .header(AUTHORIZATION, EMBY_HEADER_VALUE)
-        .json(&serde_json::json!({ "Username": input.username, "Pw": input.password }))
-        .send()
-        .await
-        .unwrap()
-        .json::<AuthenticateResponse>()
-        .await
-        .unwrap();
-    ryot_log!(
-        debug,
-        "Authenticated with token: {}",
-        authenticate.access_token
-    );
-
-    let mut headers = HeaderMap::new();
-    headers.insert(USER_AGENT, HeaderValue::from_static(USER_AGENT_STR));
-    headers.insert(ACCEPT, APPLICATION_JSON_HEADER.clone());
-    headers.insert(
-        "X-Emby-Token",
-        HeaderValue::from_str(&authenticate.access_token).unwrap(),
-    );
-    let url = input.api_url;
-    let client: Client = ClientBuilder::new()
-        .default_headers(headers)
-        .build()
-        .unwrap();
-    let user_id = authenticate.user.id;
-    ryot_log!(debug, "Authenticated as user id: {}", user_id);
-
     let mut to_handle_media = vec![];
     let mut failed_items = vec![];
 
+    let base_url = input.api_url;
+    let (client, user_id) =
+        get_authenticated_client(&base_url, &input.username, &input.password).await?;
+
     let query = json!({ "recursive": true, "IsPlayed": true, "fields": "ProviderIds" });
     let library_data = client
-        .get(&format!("{}/Users/{}/Items", url, user_id))
+        .get(&format!("{}/Users/{}/Items", base_url, user_id))
         .query(&query)
         .send()
         .await
@@ -129,7 +42,7 @@ pub async fn import(input: DeployUrlAndKeyAndUsernameImportInput) -> Result<Impo
                     let mut tmdb_id = series_id_to_tmdb_id.get(&series_id).cloned().flatten();
                     if tmdb_id.is_none() {
                         let details = client
-                            .get(&format!("{}/Items/{}", url, series_id))
+                            .get(&format!("{}/Items/{}", base_url, series_id))
                             .send()
                             .await
                             .unwrap()
diff --git a/crates/services/importer/src/lib.rs b/crates/services/importer/src/lib.rs
index 9896428778..cb48be0d7b 100644
--- a/crates/services/importer/src/lib.rs
+++ b/crates/services/importer/src/lib.rs
@@ -1,6 +1,5 @@
 use std::sync::Arc;
 
-use apalis::prelude::MessageQueue;
 use async_graphql::Result;
 use background::ApplicationJob;
 use chrono::{DateTime, Duration, NaiveDateTime, Offset, TimeZone, Utc};
@@ -41,12 +40,7 @@ impl ImporterService {
         input: DeployImportJobInput,
     ) -> Result<bool> {
         let job = ApplicationJob::ImportFromExternalSource(user_id, Box::new(input));
-        self.0
-            .perform_application_job
-            .clone()
-            .enqueue(job)
-            .await
-            .unwrap();
+        self.0.perform_application_job(job).await?;
         ryot_log!(debug, "Deployed import job");
         Ok(true)
     }
diff --git a/crates/services/integration/Cargo.toml b/crates/services/integration/Cargo.toml
index 6e2906c7fd..664d0a35ea 100644
--- a/crates/services/integration/Cargo.toml
+++ b/crates/services/integration/Cargo.toml
@@ -16,6 +16,7 @@ dependent-models = { path = "../../models/dependent" }
 dependent-utils = { path = "../../utils/dependent" }
 enums = { path = "../../enums" }
 eventsource-stream = "=0.2.3"
+external-utils = { path = "../../utils/external" }
 itertools = { workspace = true }
 media-models = { path = "../../models/media" }
 providers = { path = "../../providers" }
diff --git a/crates/services/integration/src/lib.rs b/crates/services/integration/src/lib.rs
index 5277aad9bf..8f7abf5121 100644
--- a/crates/services/integration/src/lib.rs
+++ b/crates/services/integration/src/lib.rs
@@ -4,18 +4,20 @@ use async_graphql::{Error, Result as GqlResult};
 use chrono::Utc;
 use common_utils::ryot_log;
 use database_models::{
-    integration,
-    prelude::{CollectionToEntity, Integration, Metadata, UserToEntity},
-    user_to_entity,
+    integration, metadata,
+    prelude::{CollectionToEntity, Integration, Metadata, Seen, UserToEntity},
+    seen, user_to_entity,
 };
 use database_utils::user_preferences_by_id;
 use dependent_models::ImportResult;
 use dependent_utils::{commit_metadata, process_import};
 use enums::{EntityLot, IntegrationLot, IntegrationProvider, MediaLot};
+use media_models::SeenShowExtraInformation;
 use providers::google_books::GoogleBooksService;
+use push::jellyfin::JellyfinPushIntegration;
 use rust_decimal_macros::dec;
 use sea_orm::{ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
-use sink::generic_json::GenericJsonIntegration;
+use sink::generic_json::GenericJsonSinkIntegration;
 use supporting_service::SupportingService;
 use traits::TraceOk;
 use uuid::Uuid;
@@ -26,12 +28,12 @@ mod utils;
 mod yank;
 
 use crate::{
-    push::{radarr::RadarrIntegration, sonarr::SonarrIntegration},
+    push::{radarr::RadarrPushIntegration, sonarr::SonarrPushIntegration},
     sink::{
-        emby::EmbyIntegration, jellyfin::JellyfinIntegration, kodi::KodiIntegration,
-        plex::PlexIntegration,
+        emby::EmbySinkIntegration, jellyfin::JellyfinSinkIntegration, kodi::KodiSinkIntegration,
+        plex::PlexSinkIntegration,
     },
-    yank::{audiobookshelf::AudiobookshelfIntegration, komga::KomgaIntegration},
+    yank::{audiobookshelf::AudiobookshelfYankIntegration, komga::KomgaYankIntegration},
 };
 
 pub struct IntegrationService(pub Arc<SupportingService>);
@@ -101,25 +103,25 @@ impl IntegrationService {
         }
         let maybe_progress_update = match integration.provider {
             IntegrationProvider::Kodi => {
-                let kodi = KodiIntegration::new(payload);
+                let kodi = KodiSinkIntegration::new(payload);
                 kodi.yank_progress().await
             }
             IntegrationProvider::Emby => {
-                let emby = EmbyIntegration::new(payload, self.0.db.clone());
+                let emby = EmbySinkIntegration::new(payload, self.0.db.clone());
                 emby.yank_progress().await
             }
-            IntegrationProvider::Jellyfin => {
-                let jellyfin = JellyfinIntegration::new(payload);
+            IntegrationProvider::JellyfinSink => {
+                let jellyfin = JellyfinSinkIntegration::new(payload);
                 jellyfin.yank_progress().await
             }
             IntegrationProvider::Plex => {
                 let specifics = integration.clone().provider_specifics.unwrap();
                 let plex =
-                    PlexIntegration::new(payload, specifics.plex_username, self.0.db.clone());
+                    PlexSinkIntegration::new(payload, specifics.plex_username, self.0.db.clone());
                 plex.yank_progress().await
             }
             IntegrationProvider::GenericJson => {
-                let generic_json = GenericJsonIntegration::new(payload);
+                let generic_json = GenericJsonSinkIntegration::new(payload);
                 generic_json.yank_progress().await
             }
             _ => return Err(Error::new("Unsupported integration source".to_owned())),
@@ -189,7 +191,7 @@ impl IntegrationService {
                 if let Some(entity_id) = maybe_entity_id {
                     let _push_result = match integration.provider {
                         IntegrationProvider::Radarr => {
-                            let radarr = RadarrIntegration::new(
+                            let radarr = RadarrPushIntegration::new(
                                 specifics.radarr_base_url.unwrap(),
                                 specifics.radarr_api_key.unwrap(),
                                 specifics.radarr_profile_id.unwrap(),
@@ -199,7 +201,7 @@ impl IntegrationService {
                             radarr.push_progress().await
                         }
                         IntegrationProvider::Sonarr => {
-                            let sonarr = SonarrIntegration::new(
+                            let sonarr = SonarrPushIntegration::new(
                                 specifics.sonarr_base_url.unwrap(),
                                 specifics.sonarr_api_key.unwrap(),
                                 specifics.sonarr_profile_id.unwrap(),
@@ -216,6 +218,41 @@ impl IntegrationService {
         Ok(())
     }
 
+    pub async fn handle_on_seen_complete(&self, id: String) -> GqlResult<()> {
+        let (seen, show_extra_information, metadata_title) = Seen::find_by_id(id)
+            .left_join(Metadata)
+            .select_only()
+            .columns([seen::Column::UserId, seen::Column::ShowExtraInformation])
+            .columns([metadata::Column::Title])
+            .into_tuple::<(String, Option<SeenShowExtraInformation>, String)>()
+            .one(&self.0.db)
+            .await?
+            .ok_or_else(|| Error::new("Seen with the given ID could not be found"))?;
+        let integrations = Integration::find()
+            .filter(integration::Column::UserId.eq(seen))
+            .filter(integration::Column::Lot.eq(IntegrationLot::Push))
+            .filter(integration::Column::Provider.eq(IntegrationProvider::JellyfinPush))
+            .all(&self.0.db)
+            .await?;
+        for integration in integrations {
+            let specifics = integration.provider_specifics.unwrap();
+            match integration.provider {
+                IntegrationProvider::JellyfinPush => {
+                    let integration = JellyfinPushIntegration::new(
+                        specifics.jellyfin_push_base_url.unwrap(),
+                        specifics.jellyfin_push_username.unwrap(),
+                        specifics.jellyfin_push_password.unwrap(),
+                        &metadata_title,
+                        &show_extra_information,
+                    );
+                    integration.push_progress().await?;
+                }
+                _ => unreachable!(),
+            }
+        }
+        Ok(())
+    }
+
     pub async fn yank_integrations_data_for_user(&self, user_id: &String) -> GqlResult<bool> {
         let preferences = user_preferences_by_id(user_id, &self.0).await?;
         if preferences.general.disable_integrations {
@@ -236,7 +273,7 @@ impl IntegrationService {
             let specifics = integration.clone().provider_specifics.unwrap();
             let response = match integration.provider {
                 IntegrationProvider::Audiobookshelf => {
-                    let audiobookshelf = AudiobookshelfIntegration::new(
+                    let audiobookshelf = AudiobookshelfYankIntegration::new(
                         specifics.audiobookshelf_base_url.unwrap(),
                         specifics.audiobookshelf_token.unwrap(),
                         integration.sync_to_owned_collection,
@@ -251,7 +288,7 @@ impl IntegrationService {
                         .await
                 }
                 IntegrationProvider::Komga => {
-                    let komga = KomgaIntegration::new(
+                    let komga = KomgaYankIntegration::new(
                         specifics.komga_base_url.unwrap(),
                         specifics.komga_username.unwrap(),
                         specifics.komga_password.unwrap(),
diff --git a/crates/services/integration/src/push/jellyfin.rs b/crates/services/integration/src/push/jellyfin.rs
new file mode 100644
index 0000000000..6713adbc46
--- /dev/null
+++ b/crates/services/integration/src/push/jellyfin.rs
@@ -0,0 +1,90 @@
+use external_utils::jellyfin::{get_authenticated_client, ItemsResponse};
+use media_models::SeenShowExtraInformation;
+use serde_json::json;
+use traits::TraceOk;
+
+pub(crate) struct JellyfinPushIntegration<'a> {
+    base_url: String,
+    username: String,
+    password: String,
+    metadata_title: &'a String,
+    show_extra_information: &'a Option<SeenShowExtraInformation>,
+}
+
+impl<'a> JellyfinPushIntegration<'a> {
+    pub const fn new(
+        base_url: String,
+        username: String,
+        password: String,
+        metadata_title: &'a String,
+        show_extra_information: &'a Option<SeenShowExtraInformation>,
+    ) -> Self {
+        Self {
+            base_url,
+            username,
+            password,
+            metadata_title,
+            show_extra_information,
+        }
+    }
+
+    pub async fn push_progress(&self) -> anyhow::Result<()> {
+        let (client, user_id) =
+            get_authenticated_client(&self.base_url, &self.username, &self.password).await?;
+        let json =
+            json!({ "Recursive": true, "SearchTerm": self.metadata_title, "HasTmdbId": true });
+        let items = client
+            .get(format!("{}/Users/{}/Items", &self.base_url, user_id))
+            .query(&json)
+            .send()
+            .await?
+            .json::<ItemsResponse>()
+            .await?;
+        if let Some(selected_item) = items.items.first() {
+            let id = match self.show_extra_information {
+                Some(extra_information) => {
+                    let mut return_id = None;
+                    let id = selected_item.id.clone();
+                    let season = client
+                        .get(format!("{}/Shows/{}/Seasons", self.base_url, id))
+                        .query(&json!({ "UserId": user_id }))
+                        .send()
+                        .await?
+                        .json::<ItemsResponse>()
+                        .await?
+                        .items
+                        .into_iter()
+                        .find(|s| s.index_number == Some(extra_information.season));
+                    if let Some(season) = season {
+                        let episode = client
+                            .get(format!("{}/Shows/{}/Episodes", self.base_url, id))
+                            .query(&json!({ "UserId": user_id, "SeasonId": season.id }))
+                            .send()
+                            .await?
+                            .json::<ItemsResponse>()
+                            .await?
+                            .items
+                            .into_iter()
+                            .find(|e| e.index_number == Some(extra_information.episode));
+                        return_id = episode.map(|e| e.id);
+                    }
+                    return_id
+                }
+                None => Some(selected_item.id.clone()),
+            };
+            if let Some(id) = id {
+                client
+                    .post(format!(
+                        "{}/Users/{}/PlayedItems/{}",
+                        self.base_url, user_id, id
+                    ))
+                    .send()
+                    .await?
+                    .json::<serde_json::Value>()
+                    .await
+                    .trace_ok();
+            }
+        }
+        Ok(())
+    }
+}
diff --git a/crates/services/integration/src/push/mod.rs b/crates/services/integration/src/push/mod.rs
index a4ec084770..d926a1d07e 100644
--- a/crates/services/integration/src/push/mod.rs
+++ b/crates/services/integration/src/push/mod.rs
@@ -1,2 +1,3 @@
+pub mod jellyfin;
 pub mod radarr;
 pub mod sonarr;
diff --git a/crates/services/integration/src/push/radarr.rs b/crates/services/integration/src/push/radarr.rs
index a1ec0e3b15..f625db5c5d 100644
--- a/crates/services/integration/src/push/radarr.rs
+++ b/crates/services/integration/src/push/radarr.rs
@@ -8,42 +8,42 @@ use radarr_api_rs::{
 };
 use traits::TraceOk;
 
-pub(crate) struct RadarrIntegration {
-    radarr_base_url: String,
-    radarr_api_key: String,
-    radarr_profile_id: i32,
-    radarr_root_folder_path: String,
+pub(crate) struct RadarrPushIntegration {
+    base_url: String,
+    api_key: String,
+    profile_id: i32,
+    root_folder_path: String,
     tmdb_id: String,
 }
 
-impl RadarrIntegration {
+impl RadarrPushIntegration {
     pub const fn new(
-        radarr_base_url: String,
-        radarr_api_key: String,
-        radarr_profile_id: i32,
-        radarr_root_folder_path: String,
+        base_url: String,
+        api_key: String,
+        profile_id: i32,
+        root_folder_path: String,
         tmdb_id: String,
     ) -> Self {
         Self {
-            radarr_base_url,
-            radarr_api_key,
-            radarr_profile_id,
-            radarr_root_folder_path,
+            base_url,
+            api_key,
+            profile_id,
+            root_folder_path,
             tmdb_id,
         }
     }
 
-    async fn radarr_push(&self) -> anyhow::Result<()> {
+    pub async fn push_progress(&self) -> anyhow::Result<()> {
         let mut configuration = RadarrConfiguration::new();
-        configuration.base_path = self.radarr_base_url.clone();
+        configuration.base_path = self.base_url.clone();
         configuration.api_key = Some(RadarrApiKey {
-            key: self.radarr_api_key.clone(),
+            key: self.api_key.clone(),
             prefix: None,
         });
         let mut resource = RadarrMovieResource::new();
         resource.tmdb_id = Some(self.tmdb_id.parse().unwrap());
-        resource.quality_profile_id = Some(self.radarr_profile_id);
-        resource.root_folder_path = Some(Some(self.radarr_root_folder_path.clone()));
+        resource.quality_profile_id = Some(self.profile_id);
+        resource.root_folder_path = Some(Some(self.root_folder_path.clone()));
         resource.monitored = Some(true);
         let mut options = RadarrAddMovieOptions::new();
         options.search_for_movie = Some(true);
@@ -54,8 +54,4 @@ impl RadarrIntegration {
             .trace_ok();
         Ok(())
     }
-
-    pub async fn push_progress(&self) -> anyhow::Result<()> {
-        self.radarr_push().await
-    }
 }
diff --git a/crates/services/integration/src/push/sonarr.rs b/crates/services/integration/src/push/sonarr.rs
index e667d6d565..1024a8dac1 100644
--- a/crates/services/integration/src/push/sonarr.rs
+++ b/crates/services/integration/src/push/sonarr.rs
@@ -8,43 +8,43 @@ use sonarr_api_rs::{
 };
 use traits::TraceOk;
 
-pub(crate) struct SonarrIntegration {
-    sonarr_base_url: String,
-    sonarr_api_key: String,
-    sonarr_profile_id: i32,
-    sonarr_root_folder_path: String,
+pub(crate) struct SonarrPushIntegration {
+    base_url: String,
+    api_key: String,
+    profile_id: i32,
+    root_folder_path: String,
     tvdb_id: String,
 }
 
-impl SonarrIntegration {
+impl SonarrPushIntegration {
     pub const fn new(
-        sonarr_base_url: String,
-        sonarr_api_key: String,
-        sonarr_profile_id: i32,
-        sonarr_root_folder_path: String,
+        base_url: String,
+        api_key: String,
+        profile_id: i32,
+        root_folder_path: String,
         tvdb_id: String,
     ) -> Self {
         Self {
-            sonarr_base_url,
-            sonarr_api_key,
-            sonarr_profile_id,
-            sonarr_root_folder_path,
+            base_url,
+            api_key,
+            profile_id,
+            root_folder_path,
             tvdb_id,
         }
     }
 
-    async fn sonarr_push(&self) -> anyhow::Result<()> {
+    pub async fn push_progress(&self) -> anyhow::Result<()> {
         let mut configuration = SonarrConfiguration::new();
-        configuration.base_path = self.sonarr_base_url.clone();
+        configuration.base_path = self.base_url.clone();
         configuration.api_key = Some(SonarrApiKey {
-            key: self.sonarr_api_key.clone(),
+            key: self.api_key.clone(),
             prefix: None,
         });
         let mut resource = SonarrSeriesResource::new();
         resource.title = Some(Some(self.tvdb_id.clone()));
         resource.tvdb_id = Some(self.tvdb_id.parse().unwrap());
-        resource.quality_profile_id = Some(self.sonarr_profile_id);
-        resource.root_folder_path = Some(Some(self.sonarr_root_folder_path.clone()));
+        resource.quality_profile_id = Some(self.profile_id);
+        resource.root_folder_path = Some(Some(self.root_folder_path.clone()));
         resource.monitored = Some(true);
         resource.season_folder = Some(true);
         let mut options = SonarrAddSeriesOptions::new();
@@ -56,8 +56,4 @@ impl SonarrIntegration {
             .trace_ok();
         Ok(())
     }
-
-    pub async fn push_progress(&self) -> anyhow::Result<()> {
-        self.sonarr_push().await
-    }
 }
diff --git a/crates/services/integration/src/sink/emby.rs b/crates/services/integration/src/sink/emby.rs
index cc21a57cb7..93c266549f 100644
--- a/crates/services/integration/src/sink/emby.rs
+++ b/crates/services/integration/src/sink/emby.rs
@@ -47,19 +47,18 @@ mod models {
     }
 }
 
-pub(crate) struct EmbyIntegration {
+pub(crate) struct EmbySinkIntegration {
     payload: String,
     db: DatabaseConnection,
 }
 
-impl EmbyIntegration {
+impl EmbySinkIntegration {
     pub const fn new(payload: String, db: DatabaseConnection) -> Self {
         Self { payload, db }
     }
 
-    async fn emby_progress(&self) -> Result<ImportResult> {
+    pub async fn yank_progress(&self) -> Result<ImportResult> {
         let payload: models::EmbyWebhookPayload = serde_json::from_str(&self.payload)?;
-
         let runtime = payload
             .item
             .run_time_ticks
@@ -68,7 +67,6 @@ impl EmbyIntegration {
             .playback_info
             .position_ticks
             .ok_or_else(|| anyhow::anyhow!("No position associated with this media"))?;
-
         let (identifier, lot) =
             match payload.item.item_type.as_str() {
                 "Movie" => {
@@ -91,7 +89,6 @@ impl EmbyIntegration {
                 }
                 _ => bail!("Only movies and shows supported"),
             };
-
         Ok(ImportResult {
             metadata: vec![ImportOrExportMediaItem {
                 lot,
@@ -109,8 +106,4 @@ impl EmbyIntegration {
             ..Default::default()
         })
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        self.emby_progress().await
-    }
 }
diff --git a/crates/services/integration/src/sink/generic_json.rs b/crates/services/integration/src/sink/generic_json.rs
index 5069396d4c..ffb9fa3179 100644
--- a/crates/services/integration/src/sink/generic_json.rs
+++ b/crates/services/integration/src/sink/generic_json.rs
@@ -1,15 +1,15 @@
 use anyhow::{bail, Result};
 use dependent_models::{CompleteExport, ImportResult};
 
-pub(crate) struct GenericJsonIntegration {
+pub(crate) struct GenericJsonSinkIntegration {
     payload: String,
 }
-impl GenericJsonIntegration {
+impl GenericJsonSinkIntegration {
     pub const fn new(payload: String) -> Self {
         Self { payload }
     }
 
-    async fn generic_json_progress(&self) -> Result<ImportResult> {
+    pub async fn yank_progress(&self) -> Result<ImportResult> {
         let payload = match serde_json::from_str::<CompleteExport>(&self.payload) {
             Ok(val) => val,
             Err(err) => bail!(err),
@@ -23,8 +23,4 @@ impl GenericJsonIntegration {
             ..Default::default()
         })
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        self.generic_json_progress().await
-    }
 }
diff --git a/crates/services/integration/src/sink/jellyfin.rs b/crates/services/integration/src/sink/jellyfin.rs
index feced79b92..5e76411bfb 100644
--- a/crates/services/integration/src/sink/jellyfin.rs
+++ b/crates/services/integration/src/sink/jellyfin.rs
@@ -46,16 +46,16 @@ mod models {
     }
 }
 
-pub(crate) struct JellyfinIntegration {
+pub(crate) struct JellyfinSinkIntegration {
     payload: String,
 }
 
-impl JellyfinIntegration {
+impl JellyfinSinkIntegration {
     pub const fn new(payload: String) -> Self {
         Self { payload }
     }
 
-    async fn jellyfin_progress(&self) -> Result<ImportResult> {
+    pub async fn yank_progress(&self) -> Result<ImportResult> {
         let payload = serde_json::from_str::<models::JellyfinWebhookPayload>(&self.payload)?;
         let identifier = payload
             .item
@@ -105,8 +105,4 @@ impl JellyfinIntegration {
             ..Default::default()
         })
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        self.jellyfin_progress().await
-    }
 }
diff --git a/crates/services/integration/src/sink/kodi.rs b/crates/services/integration/src/sink/kodi.rs
index 0608a8491e..73001ce310 100644
--- a/crates/services/integration/src/sink/kodi.rs
+++ b/crates/services/integration/src/sink/kodi.rs
@@ -14,15 +14,15 @@ struct IntegrationMediaSeen {
     show_episode_number: Option<i32>,
 }
 
-pub(crate) struct KodiIntegration {
+pub(crate) struct KodiSinkIntegration {
     payload: String,
 }
-impl KodiIntegration {
+impl KodiSinkIntegration {
     pub const fn new(payload: String) -> Self {
         Self { payload }
     }
 
-    async fn kodi_progress(&self) -> Result<ImportResult> {
+    pub async fn yank_progress(&self) -> Result<ImportResult> {
         let payload = match serde_json::from_str::<IntegrationMediaSeen>(&self.payload) {
             Ok(val) => val,
             Err(err) => bail!(err),
@@ -45,8 +45,4 @@ impl KodiIntegration {
             ..Default::default()
         })
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        self.kodi_progress().await
-    }
 }
diff --git a/crates/services/integration/src/sink/plex.rs b/crates/services/integration/src/sink/plex.rs
index c72c96e2f5..a2b3d5b934 100644
--- a/crates/services/integration/src/sink/plex.rs
+++ b/crates/services/integration/src/sink/plex.rs
@@ -52,13 +52,13 @@ mod models {
     }
 }
 
-pub(crate) struct PlexIntegration {
+pub(crate) struct PlexSinkIntegration {
     payload: String,
     plex_user: Option<String>,
     db: DatabaseConnection,
 }
 
-impl PlexIntegration {
+impl PlexSinkIntegration {
     pub const fn new(payload: String, plex_user: Option<String>, db: DatabaseConnection) -> Self {
         Self {
             payload,
@@ -109,7 +109,7 @@ impl PlexIntegration {
         }
     }
 
-    async fn plex_progress(&self) -> Result<ImportResult> {
+    pub async fn yank_progress(&self) -> Result<ImportResult> {
         ryot_log!(debug, "Processing Plex payload {:#?}", self.payload);
 
         let payload = Self::parse_payload(&self.payload)?;
@@ -149,8 +149,4 @@ impl PlexIntegration {
             ..Default::default()
         })
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        self.plex_progress().await
-    }
 }
diff --git a/crates/services/integration/src/yank/audiobookshelf.rs b/crates/services/integration/src/yank/audiobookshelf.rs
index 9092fe3434..4a4bc7bbd2 100644
--- a/crates/services/integration/src/yank/audiobookshelf.rs
+++ b/crates/services/integration/src/yank/audiobookshelf.rs
@@ -14,14 +14,14 @@ use reqwest::header::{HeaderValue, AUTHORIZATION};
 use rust_decimal_macros::dec;
 use specific_models::audiobookshelf::{self, LibrariesListResponse, ListResponse};
 
-pub(crate) struct AudiobookshelfIntegration {
+pub(crate) struct AudiobookshelfYankIntegration {
     base_url: String,
     access_token: String,
     sync_to_owned_collection: Option<bool>,
     isbn_service: GoogleBooksService,
 }
 
-impl AudiobookshelfIntegration {
+impl AudiobookshelfYankIntegration {
     pub fn new(
         base_url: String,
         access_token: String,
diff --git a/crates/services/integration/src/yank/komga.rs b/crates/services/integration/src/yank/komga.rs
index 03ea600879..97bcf0d841 100644
--- a/crates/services/integration/src/yank/komga.rs
+++ b/crates/services/integration/src/yank/komga.rs
@@ -182,7 +182,7 @@ impl KomgaEventHandler {
     }
 }
 
-pub(crate) struct KomgaIntegration {
+pub(crate) struct KomgaYankIntegration {
     base_url: String,
     username: String,
     password: String,
@@ -193,7 +193,7 @@ pub(crate) struct KomgaIntegration {
 
 type ProcessEventReturn = (CommitMediaInput, ImportOrExportMediaItemSeen);
 
-impl KomgaIntegration {
+impl KomgaYankIntegration {
     pub fn new(
         base_url: String,
         username: String,
@@ -427,7 +427,7 @@ impl KomgaIntegration {
         Ok(())
     }
 
-    async fn komga_progress(&self) -> Result<ImportResult> {
+    pub async fn yank_progress(&self) -> Result<ImportResult> {
         let mut result = ImportResult::default();
         // DEV: This object needs global lifetime so we can continue to use the receiver if
         // we ever create more SSE Objects we may want to implement a higher level
@@ -513,8 +513,4 @@ impl KomgaIntegration {
 
         Ok(result)
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        self.komga_progress().await
-    }
 }
diff --git a/crates/services/miscellaneous/Cargo.toml b/crates/services/miscellaneous/Cargo.toml
index 3507dd0b89..e4dd2314c5 100644
--- a/crates/services/miscellaneous/Cargo.toml
+++ b/crates/services/miscellaneous/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-apalis = { workspace = true }
 application-utils = { path = "../../utils/application" }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs
index 96fa86fd0b..1589e4c802 100644
--- a/crates/services/miscellaneous/src/lib.rs
+++ b/crates/services/miscellaneous/src/lib.rs
@@ -4,7 +4,6 @@ use std::{
     sync::Arc,
 };
 
-use apalis::prelude::MessageQueue;
 use application_utils::get_current_date;
 use async_graphql::{Error, Result};
 use background::{ApplicationJob, CoreApplicationJob};
@@ -42,11 +41,11 @@ use dependent_models::{
     SearchResults, UserMetadataDetails, UserMetadataGroupDetails, UserPersonDetails,
 };
 use dependent_utils::{
-    after_media_seen_tasks, commit_metadata, commit_metadata_group_internal,
-    commit_metadata_internal, commit_person, create_partial_metadata, deploy_background_job,
+    commit_metadata, commit_metadata_group_internal, commit_metadata_internal, commit_person,
+    create_partial_metadata, deploy_after_handle_media_seen_tasks, deploy_background_job,
     deploy_update_metadata_job, get_metadata_provider, get_openlibrary_service,
     get_tmdb_non_media_service, get_users_and_cte_monitoring_entity, get_users_monitoring_entity,
-    is_metadata_finished_by_user, post_review, progress_update,
+    handle_after_media_seen_tasks, is_metadata_finished_by_user, post_review, progress_update,
     queue_media_state_changed_notification_for_user, queue_notifications_to_user_platforms,
     refresh_collection_to_entity_association, update_metadata_and_notify_users,
 };
@@ -420,7 +419,7 @@ ORDER BY RANDOM() LIMIT 10;
         metadata_id: &String,
         force_update: bool,
     ) -> Result<bool> {
-        deploy_update_metadata_job(metadata_id, force_update, &self.0.perform_application_job).await
+        deploy_update_metadata_job(metadata_id, force_update, &self.0).await
     }
 
     pub async fn metadata_details(&self, metadata_id: &String) -> Result<GraphqlMetadataDetails> {
@@ -1089,11 +1088,8 @@ ORDER BY RANDOM() LIMIT 10;
         input: Vec<ProgressUpdateInput>,
     ) -> Result<bool> {
         self.0
-            .perform_core_application_job
-            .clone()
-            .enqueue(CoreApplicationJob::BulkProgressUpdate(user_id, input))
-            .await
-            .unwrap();
+            .perform_core_application_job(CoreApplicationJob::BulkProgressUpdate(user_id, input))
+            .await?;
         Ok(true)
     }
 
@@ -1339,7 +1335,7 @@ ORDER BY RANDOM() LIMIT 10;
             seen.review_id = ActiveValue::Set(to_update_review_id);
         }
         let seen = seen.update(&self.0.db).await.unwrap();
-        after_media_seen_tasks(seen, &self.0).await?;
+        deploy_after_handle_media_seen_tasks(seen, &self.0).await?;
         Ok(true)
     }
 
@@ -1350,11 +1346,8 @@ ORDER BY RANDOM() LIMIT 10;
             .unwrap()
             .unwrap();
         self.0
-            .perform_application_job
-            .clone()
-            .enqueue(ApplicationJob::UpdatePerson(person.id))
-            .await
-            .unwrap();
+            .perform_application_job(ApplicationJob::UpdatePerson(person.id))
+            .await?;
         Ok(true)
     }
 
@@ -1368,11 +1361,8 @@ ORDER BY RANDOM() LIMIT 10;
             .unwrap()
             .unwrap();
         self.0
-            .perform_application_job
-            .clone()
-            .enqueue(ApplicationJob::UpdateMetadataGroup(metadata_group.id))
-            .await
-            .unwrap();
+            .perform_application_job(ApplicationJob::UpdateMetadataGroup(metadata_group.id))
+            .await?;
         Ok(true)
     }
 
@@ -1770,6 +1760,10 @@ ORDER BY RANDOM() LIMIT 10;
         }
     }
 
+    pub async fn handle_after_media_seen_tasks(&self, seen: seen::Model) -> Result<()> {
+        handle_after_media_seen_tasks(seen, &self.0).await
+    }
+
     pub async fn delete_seen_item(
         &self,
         user_id: &String,
@@ -1807,7 +1801,7 @@ ORDER BY RANDOM() LIMIT 10;
             si.delete(&self.0.db).await.trace_ok();
             associate_user_with_entity(&self.0.db, user_id, metadata_id, EntityLot::Metadata)
                 .await?;
-            after_media_seen_tasks(cloned_seen, &self.0).await?;
+            deploy_after_handle_media_seen_tasks(cloned_seen, &self.0).await?;
             Ok(StringIdObject { id: seen_id })
         } else {
             Err(Error::new("This seen item does not exist".to_owned()))
diff --git a/crates/services/statistics/src/lib.rs b/crates/services/statistics/src/lib.rs
index 0bd5425cbf..d854035275 100644
--- a/crates/services/statistics/src/lib.rs
+++ b/crates/services/statistics/src/lib.rs
@@ -137,6 +137,10 @@ impl StatisticsService {
                 daily_user_activity::Column::ShowDuration.sum(),
                 "total_show_duration",
             )
+            .column_as(
+                daily_user_activity::Column::VideoGameDuration.sum(),
+                "total_video_game_duration",
+            )
             .column_as(
                 daily_user_activity::Column::VideoGameCount.sum(),
                 "video_game_count",
diff --git a/crates/services/supporting/Cargo.toml b/crates/services/supporting/Cargo.toml
index 212b9efadb..9a1e6c4692 100644
--- a/crates/services/supporting/Cargo.toml
+++ b/crates/services/supporting/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+async-graphql = { workspace = true }
 apalis = { workspace = true }
 background = { path = "../../background" }
 cache-service = { path = "../../services/cache" }
diff --git a/crates/services/supporting/src/lib.rs b/crates/services/supporting/src/lib.rs
index b0ecf3103e..717cf75901 100644
--- a/crates/services/supporting/src/lib.rs
+++ b/crates/services/supporting/src/lib.rs
@@ -1,6 +1,7 @@
 use std::sync::Arc;
 
-use apalis::prelude::MemoryStorage;
+use apalis::prelude::{MemoryStorage, MessageQueue};
+use async_graphql::Result;
 use background::{ApplicationJob, CoreApplicationJob};
 use cache_service::CacheService;
 use file_storage_service::FileStorageService;
@@ -18,8 +19,9 @@ pub struct SupportingService {
     pub oidc_client: Option<CoreClient>,
     pub commit_cache: Cache<CommitCache, ()>,
     pub file_storage_service: Arc<FileStorageService>,
-    pub perform_application_job: MemoryStorage<ApplicationJob>,
-    pub perform_core_application_job: MemoryStorage<CoreApplicationJob>,
+
+    perform_application_job: MemoryStorage<ApplicationJob>,
+    perform_core_application_job: MemoryStorage<CoreApplicationJob>,
 }
 
 impl SupportingService {
@@ -49,4 +51,22 @@ impl SupportingService {
             perform_core_application_job: perform_core_application_job.clone(),
         }
     }
+
+    pub async fn perform_application_job(&self, job: ApplicationJob) -> Result<()> {
+        self.perform_application_job
+            .clone()
+            .enqueue(job)
+            .await
+            .unwrap();
+        Ok(())
+    }
+
+    pub async fn perform_core_application_job(&self, job: CoreApplicationJob) -> Result<()> {
+        self.perform_core_application_job
+            .clone()
+            .enqueue(job)
+            .await
+            .unwrap();
+        Ok(())
+    }
 }
diff --git a/crates/services/user/src/lib.rs b/crates/services/user/src/lib.rs
index ae76c06424..f7f9f4bdcf 100644
--- a/crates/services/user/src/lib.rs
+++ b/crates/services/user/src/lib.rs
@@ -303,12 +303,7 @@ impl UserService {
             .await
             .ok();
         }
-        deploy_job_to_calculate_user_activities_and_summary(
-            &self.0.perform_application_job,
-            &user.id,
-            false,
-        )
-        .await;
+        deploy_job_to_calculate_user_activities_and_summary(&user.id, false, &self.0).await;
         Ok(RegisterResult::Ok(StringIdObject { id: user.id }))
     }
 
@@ -799,8 +794,12 @@ impl UserService {
             ));
         }
         let lot = match input.provider {
-            IntegrationProvider::Audiobookshelf => IntegrationLot::Yank,
-            IntegrationProvider::Radarr | IntegrationProvider::Sonarr => IntegrationLot::Push,
+            IntegrationProvider::Audiobookshelf | IntegrationProvider::Komga => {
+                IntegrationLot::Yank
+            }
+            IntegrationProvider::Radarr
+            | IntegrationProvider::Sonarr
+            | IntegrationProvider::JellyfinPush => IntegrationLot::Push,
             _ => IntegrationLot::Sink,
         };
         let to_insert = integration::ActiveModel {
diff --git a/crates/utils/common/src/lib.rs b/crates/utils/common/src/lib.rs
index a1dde168e7..a0ffd5d774 100644
--- a/crates/utils/common/src/lib.rs
+++ b/crates/utils/common/src/lib.rs
@@ -9,7 +9,7 @@ pub const USER_AGENT_STR: &str = const_str::concat!(
     AUTHOR,
     "/",
     PROJECT_NAME,
-    "-v",
+    "-",
     APP_VERSION,
     " (",
     AUTHOR_EMAIL,
diff --git a/crates/utils/database/Cargo.toml b/crates/utils/database/Cargo.toml
index a9d376194b..9937a07ea8 100644
--- a/crates/utils/database/Cargo.toml
+++ b/crates/utils/database/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-apalis = { workspace = true }
 application-utils = { path = "../../utils/application" }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
diff --git a/crates/utils/database/src/lib.rs b/crates/utils/database/src/lib.rs
index 04765c852b..f40c62baa5 100644
--- a/crates/utils/database/src/lib.rs
+++ b/crates/utils/database/src/lib.rs
@@ -1,6 +1,5 @@
 use std::{collections::HashMap, sync::Arc};
 
-use apalis::prelude::{MemoryStorage, MessageQueue};
 use application_utils::GraphqlRepresentation;
 use async_graphql::{Error, Result};
 use background::ApplicationJob;
@@ -34,7 +33,7 @@ use media_models::{
     ShowSpecifics, VideoGameSpecifics, VisualNovelSpecifics,
 };
 use migrations::AliasedCollectionToEntity;
-use rust_decimal::prelude::ToPrimitive;
+use rust_decimal::{prelude::ToPrimitive, Decimal};
 use rust_decimal_macros::dec;
 use sea_orm::{
     prelude::{Date, DateTimeUtc, Expr},
@@ -337,10 +336,8 @@ pub async fn add_entity_to_collection(
         }
         created
     };
-    ss.perform_application_job
-        .enqueue(ApplicationJob::HandleEntityAddedToCollectionEvent(resp.id))
-        .await
-        .unwrap();
+    ss.perform_application_job(ApplicationJob::HandleEntityAddedToCollectionEvent(resp.id))
+        .await?;
     Ok(true)
 }
 
@@ -582,6 +579,7 @@ pub async fn calculate_user_activities_and_summary(
         podcast_specifics: Option<PodcastSpecifics>,
         show_specifics: Option<ShowSpecifics>,
         video_game_specifics: Option<VideoGameSpecifics>,
+        manual_time_spent: Option<Decimal>,
         visual_novel_specifics: Option<VisualNovelSpecifics>,
         anime_specifics: Option<AnimeSpecifics>,
         manga_specifics: Option<MangaSpecifics>,
@@ -637,6 +635,7 @@ pub async fn calculate_user_activities_and_summary(
             seen::Column::MetadataId,
             seen::Column::FinishedOn,
             seen::Column::LastUpdatedOn,
+            seen::Column::ManualTimeSpent,
         ])
         .column_as(metadata::Column::Lot, "metadata_lot")
         .columns([
@@ -692,17 +691,22 @@ pub async fn calculate_user_activities_and_summary(
             if let Some(runtime) = visual_novel_extra.length {
                 activity.visual_novel_duration += runtime;
             }
+        } else if let Some(_video_game_extra) = seen.video_game_specifics {
+            if let Some(manual_time_spent) = seen.manual_time_spent {
+                activity.video_game_duration +=
+                    (manual_time_spent / dec!(60)).to_i32().unwrap_or_default();
+            }
         }
         match seen.metadata_lot {
+            MediaLot::Book => activity.book_count += 1,
+            MediaLot::Show => activity.show_count += 1,
             MediaLot::Anime => activity.anime_count += 1,
+            MediaLot::Movie => activity.movie_count += 1,
             MediaLot::Manga => activity.manga_count += 1,
             MediaLot::Podcast => activity.podcast_count += 1,
-            MediaLot::Show => activity.show_count += 1,
             MediaLot::VideoGame => activity.video_game_count += 1,
-            MediaLot::VisualNovel => activity.visual_novel_count += 1,
-            MediaLot::Book => activity.book_count += 1,
             MediaLot::AudioBook => activity.audio_book_count += 1,
-            MediaLot::Movie => activity.movie_count += 1,
+            MediaLot::VisualNovel => activity.visual_novel_count += 1,
         };
     }
 
@@ -745,11 +749,11 @@ pub async fn calculate_user_activities_and_summary(
         let date = item.posted_on.date_naive();
         let activity = get_activity_count(item.id, &mut activities, user_id, date);
         match item.entity_lot {
-            EntityLot::Metadata => activity.metadata_review_count += 1,
             EntityLot::Person => activity.person_review_count += 1,
-            EntityLot::MetadataGroup => activity.metadata_group_review_count += 1,
-            EntityLot::Collection => activity.collection_review_count += 1,
             EntityLot::Exercise => activity.exercise_review_count += 1,
+            EntityLot::Metadata => activity.metadata_review_count += 1,
+            EntityLot::Collection => activity.collection_review_count += 1,
+            EntityLot::MetadataGroup => activity.metadata_group_review_count += 1,
             _ => {}
         }
     }
@@ -784,7 +788,8 @@ pub async fn calculate_user_activities_and_summary(
             + activity.podcast_duration
             + activity.movie_duration
             + activity.show_duration
-            + activity.visual_novel_duration;
+            + activity.visual_novel_duration
+            + activity.video_game_duration;
         let mut model: daily_user_activity::ActiveModel = activity.into();
         model.total_review_count = ActiveValue::Set(total_review_count);
         model.total_metadata_count = ActiveValue::Set(total_metadata_count);
@@ -797,27 +802,23 @@ pub async fn calculate_user_activities_and_summary(
 }
 
 pub async fn deploy_job_to_calculate_user_activities_and_summary(
-    perform_application_job: &MemoryStorage<ApplicationJob>,
     user_id: &String,
     calculate_from_beginning: bool,
+    ss: &Arc<SupportingService>,
 ) {
-    perform_application_job
-        .clone()
-        .enqueue(ApplicationJob::RecalculateUserActivitiesAndSummary(
-            user_id.to_owned(),
-            calculate_from_beginning,
-        ))
-        .await
-        .unwrap();
+    ss.perform_application_job(ApplicationJob::RecalculateUserActivitiesAndSummary(
+        user_id.to_owned(),
+        calculate_from_beginning,
+    ))
+    .await
+    .unwrap();
 }
 
 pub async fn deploy_job_to_re_evaluate_user_workouts(
     user_id: &String,
-    perform_application_job: &MemoryStorage<ApplicationJob>,
+    ss: &Arc<SupportingService>,
 ) {
-    perform_application_job
-        .clone()
-        .enqueue(ApplicationJob::ReEvaluateUserWorkouts(user_id.to_owned()))
+    ss.perform_application_job(ApplicationJob::ReEvaluateUserWorkouts(user_id.to_owned()))
         .await
         .unwrap();
 }
diff --git a/crates/utils/dependent/Cargo.toml b/crates/utils/dependent/Cargo.toml
index 8b23cc7585..4fbaf6c30c 100644
--- a/crates/utils/dependent/Cargo.toml
+++ b/crates/utils/dependent/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 anyhow = { workspace = true }
-apalis = { workspace = true }
 application-utils = { path = "../application" }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
diff --git a/crates/utils/dependent/src/lib.rs b/crates/utils/dependent/src/lib.rs
index 9375f2f2b2..867263597e 100644
--- a/crates/utils/dependent/src/lib.rs
+++ b/crates/utils/dependent/src/lib.rs
@@ -1,7 +1,6 @@
 use std::{collections::HashMap, iter::zip, sync::Arc};
 
 use anyhow::{bail, Result as AnyhowResult};
-use apalis::prelude::{MemoryStorage, MessageQueue};
 use application_utils::get_current_date;
 use async_graphql::{Enum, Error, Result};
 use background::{ApplicationJob, CoreApplicationJob};
@@ -336,16 +335,12 @@ async fn deploy_associate_group_with_metadata_job(
     lot: MediaLot,
     source: MediaSource,
     identifier: String,
-    perform_application_job: &MemoryStorage<ApplicationJob>,
+    ss: &Arc<SupportingService>,
 ) -> Result<()> {
-    perform_application_job
-        .clone()
-        .enqueue(ApplicationJob::AssociateGroupWithMetadata(
-            lot, source, identifier,
-        ))
-        .await
-        .unwrap();
-    Ok(())
+    ss.perform_application_job(ApplicationJob::AssociateGroupWithMetadata(
+        lot, source, identifier,
+    ))
+    .await
 }
 
 #[allow(clippy::too_many_arguments)]
@@ -388,14 +383,9 @@ async fn change_metadata_associations(
             .ok();
     }
     for group_identifier in groups {
-        deploy_associate_group_with_metadata_job(
-            lot,
-            source,
-            group_identifier,
-            &ss.perform_application_job,
-        )
-        .await
-        .ok();
+        deploy_associate_group_with_metadata_job(lot, source, group_identifier, ss)
+            .await
+            .ok();
     }
     Ok(())
 }
@@ -874,16 +864,13 @@ pub async fn commit_metadata(
 pub async fn deploy_update_metadata_job(
     metadata_id: &String,
     force_update: bool,
-    perform_application_job: &MemoryStorage<ApplicationJob>,
+    ss: &Arc<SupportingService>,
 ) -> Result<bool> {
-    perform_application_job
-        .clone()
-        .enqueue(ApplicationJob::UpdateMetadata(
-            metadata_id.to_owned(),
-            force_update,
-        ))
-        .await
-        .unwrap();
+    ss.perform_application_job(ApplicationJob::UpdateMetadata(
+        metadata_id.to_owned(),
+        force_update,
+    ))
+    .await?;
     Ok(true)
 }
 
@@ -892,8 +879,6 @@ pub async fn deploy_background_job(
     job_name: BackgroundJob,
     ss: &Arc<SupportingService>,
 ) -> Result<bool> {
-    let core_storage = &mut ss.perform_core_application_job.clone();
-    let storage = &mut ss.perform_application_job.clone();
     match job_name {
         BackgroundJob::UpdateAllMetadata
         | BackgroundJob::UpdateAllExercises
@@ -914,47 +899,37 @@ pub async fn deploy_background_job(
                 .await
                 .unwrap();
             for metadata_id in many_metadata {
-                deploy_update_metadata_job(&metadata_id, true, storage).await?;
+                deploy_update_metadata_job(&metadata_id, true, ss).await?;
             }
         }
         BackgroundJob::UpdateAllExercises => {
-            storage
-                .enqueue(ApplicationJob::UpdateExerciseLibrary)
-                .await
-                .unwrap();
+            ss.perform_application_job(ApplicationJob::UpdateExerciseLibrary)
+                .await?;
         }
         BackgroundJob::RecalculateCalendarEvents => {
-            storage
-                .enqueue(ApplicationJob::RecalculateCalendarEvents)
-                .await
-                .unwrap();
+            ss.perform_application_job(ApplicationJob::RecalculateCalendarEvents)
+                .await?;
         }
         BackgroundJob::PerformBackgroundTasks => {
-            storage
-                .enqueue(ApplicationJob::PerformBackgroundTasks)
-                .await
-                .unwrap();
+            ss.perform_application_job(ApplicationJob::PerformBackgroundTasks)
+                .await?;
         }
         BackgroundJob::SyncIntegrationsData => {
-            core_storage
-                .enqueue(CoreApplicationJob::SyncIntegrationsData(user_id.to_owned()))
-                .await
-                .unwrap();
+            ss.perform_core_application_job(CoreApplicationJob::SyncIntegrationsData(
+                user_id.to_owned(),
+            ))
+            .await?;
         }
         BackgroundJob::CalculateUserActivitiesAndSummary => {
-            storage
-                .enqueue(ApplicationJob::RecalculateUserActivitiesAndSummary(
-                    user_id.to_owned(),
-                    true,
-                ))
-                .await
-                .unwrap();
+            ss.perform_application_job(ApplicationJob::RecalculateUserActivitiesAndSummary(
+                user_id.to_owned(),
+                true,
+            ))
+            .await?;
         }
         BackgroundJob::ReEvaluateUserWorkouts => {
-            storage
-                .enqueue(ApplicationJob::ReEvaluateUserWorkouts(user_id.to_owned()))
-                .await
-                .unwrap();
+            ss.perform_application_job(ApplicationJob::ReEvaluateUserWorkouts(user_id.to_owned()))
+                .await?;
         }
     };
     Ok(true)
@@ -1058,17 +1033,14 @@ pub async fn post_review(
         let user = user_by_id(&ss.db, &insert.user_id.unwrap()).await?;
         // DEV: Do not send notification if updating a review
         if input.review_id.is_none() {
-            ss.perform_core_application_job
-                .clone()
-                .enqueue(CoreApplicationJob::ReviewPosted(ReviewPostedEvent {
-                    obj_title,
-                    entity_lot,
-                    obj_id: id,
-                    username: user.name,
-                    review_id: insert.id.clone().unwrap(),
-                }))
-                .await
-                .unwrap();
+            ss.perform_core_application_job(CoreApplicationJob::ReviewPosted(ReviewPostedEvent {
+                obj_title,
+                entity_lot,
+                obj_id: id,
+                username: user.name,
+                review_id: insert.id.clone().unwrap(),
+            }))
+            .await?;
         }
     }
     Ok(StringIdObject {
@@ -1201,7 +1173,18 @@ pub async fn is_metadata_finished_by_user(
     Ok((is_finished, seen_history))
 }
 
-pub async fn after_media_seen_tasks(seen: seen::Model, ss: &Arc<SupportingService>) -> Result<()> {
+pub async fn deploy_after_handle_media_seen_tasks(
+    seen: seen::Model,
+    ss: &Arc<SupportingService>,
+) -> Result<()> {
+    ss.perform_application_job(ApplicationJob::HandleAfterMediaSeenTasks(seen))
+        .await
+}
+
+pub async fn handle_after_media_seen_tasks(
+    seen: seen::Model,
+    ss: &Arc<SupportingService>,
+) -> Result<()> {
     let add_entity_to_collection = |collection_name: &str| {
         add_entity_to_collection(
             &seen.user_id,
@@ -1506,7 +1489,11 @@ pub async fn progress_update(
             .set_with_expiry(cache, ss.config.server.progress_update_threshold)
             .await?;
     }
-    after_media_seen_tasks(seen, ss).await?;
+    if seen.state == SeenState::Completed {
+        ss.perform_application_job(ApplicationJob::HandleOnSeenComplete(seen.id.clone()))
+            .await?;
+    }
+    deploy_after_handle_media_seen_tasks(seen, ss).await?;
     Ok(ProgressUpdateResultUnion::Ok(StringIdObject { id }))
 }
 
@@ -1841,7 +1828,7 @@ pub async fn create_or_update_workout(
     }
     let data = insert.insert(&ss.db).await?;
     if to_update_workout.is_some() {
-        deploy_job_to_re_evaluate_user_workouts(user_id, &ss.perform_application_job).await;
+        deploy_job_to_re_evaluate_user_workouts(user_id, ss).await;
     }
     Ok(data.id)
 }
diff --git a/crates/utils/external/Cargo.toml b/crates/utils/external/Cargo.toml
new file mode 100644
index 0000000000..b5dbcb8a64
--- /dev/null
+++ b/crates/utils/external/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "external-utils"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = { workspace = true }
+common-utils = { path = "../common" }
+reqwest = { workspace = true }
+sea-orm = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+tracing = { workspace = true }
+
+[package.metadata.cargo-machete]
+ignored = ["tracing"]
diff --git a/crates/utils/external/src/lib.rs b/crates/utils/external/src/lib.rs
new file mode 100644
index 0000000000..96ce6c4a4c
--- /dev/null
+++ b/crates/utils/external/src/lib.rs
@@ -0,0 +1,109 @@
+use anyhow::Result;
+use common_utils::{ryot_log, APPLICATION_JSON_HEADER, USER_AGENT_STR};
+use reqwest::{
+    header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, USER_AGENT},
+    Client, ClientBuilder,
+};
+use sea_orm::prelude::DateTimeUtc;
+use serde::{Deserialize, Serialize};
+
+pub mod jellyfin {
+    use super::*;
+
+    #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
+    pub enum MediaType {
+        Movie,
+        Series,
+        Episode,
+        #[serde(untagged)]
+        Unknown(String),
+    }
+
+    #[derive(Serialize, Deserialize, Debug, Clone)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct ItemProviderIdsPayload {
+        pub tmdb: Option<String>,
+    }
+
+    #[derive(Serialize, Deserialize, Debug, Clone)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct ItemUserData {
+        pub last_played_date: Option<DateTimeUtc>,
+        pub is_favorite: Option<bool>,
+    }
+
+    #[derive(Serialize, Deserialize, Debug, Clone)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct ItemResponse {
+        pub id: String,
+        pub name: String,
+        #[serde(rename = "Type")]
+        pub type_: Option<MediaType>,
+        pub index_number: Option<i32>,
+        pub series_id: Option<String>,
+        pub series_name: Option<String>,
+        pub user_data: Option<ItemUserData>,
+        pub parent_index_number: Option<i32>,
+        pub provider_ids: Option<ItemProviderIdsPayload>,
+    }
+
+    #[derive(Serialize, Deserialize, Debug, Clone)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct ItemsResponse {
+        pub items: Vec<ItemResponse>,
+    }
+
+    #[derive(Serialize, Deserialize, Debug, Clone)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct AuthenticateResponse {
+        pub user: ItemResponse,
+        pub access_token: String,
+    }
+
+    pub async fn get_authenticated_client(
+        base_url: &String,
+        username: &String,
+        password: &String,
+    ) -> Result<(Client, String)> {
+        let mut emby_header_value =
+            r#"MediaBrowser , Client="other", Device="script", DeviceId="script", Version="0.0.0""#
+                .to_string();
+        let uri = format!("{}/Users/AuthenticateByName", base_url);
+        let client = Client::new();
+        let authenticate_request = client
+            .post(uri)
+            .header(AUTHORIZATION, &emby_header_value)
+            .json(&serde_json::json!({ "Username": username, "Pw": password }));
+        ryot_log!(debug, "Authentication request: {:?}", authenticate_request);
+        let authenticate = authenticate_request
+            .send()
+            .await
+            .unwrap()
+            .json::<AuthenticateResponse>()
+            .await
+            .unwrap();
+        ryot_log!(
+            debug,
+            "Authenticated with token: {}",
+            authenticate.access_token
+        );
+
+        emby_header_value.push_str(&format!(r#", Token="{}""#, authenticate.access_token));
+
+        let mut headers = HeaderMap::new();
+        headers.insert(USER_AGENT, HeaderValue::from_static(USER_AGENT_STR));
+        headers.insert(ACCEPT, APPLICATION_JSON_HEADER.clone());
+        headers.insert(
+            AUTHORIZATION,
+            HeaderValue::from_str(&emby_header_value).unwrap(),
+        );
+        let client: Client = ClientBuilder::new()
+            .default_headers(headers)
+            .build()
+            .unwrap();
+        let user_id = authenticate.user.id;
+        ryot_log!(debug, "Authenticated as user id: {}", user_id);
+
+        Ok((client, user_id))
+    }
+}
diff --git a/docs/content/integrations.md b/docs/content/integrations.md
index 7082258728..c70a4a4b50 100644
--- a/docs/content/integrations.md
+++ b/docs/content/integrations.md
@@ -3,28 +3,10 @@
 Integrations can be used to continuously update your media progress or inform external
 services about changes. They can be of following types:
 
-- _Push_: Ryot sends data to an external service when an event occurs.
 - _Yank_: Progress data is downloaded from an externally running server at a periodic
   interval.
 - _Sink_: An external client publishes progress updates to the Ryot server.
-
-## Push integrations
-
-You can enable the following push integrations:
-
-### Radarr
-
-Automatically add movies in the selected collections to Radarr.
-
-1. Obtain your Radarr API key by going to the Radarr general settings page.
-2. Fill the inputs in the integration settings page with the correct details.
-
-### Sonarr
-
-Automatically add shows in the selected collections to Sonarr.
-
-1. Obtain your Sonarr API key by going to the Sonarr general settings page.
-2. Fill the inputs in the integration settings page with the correct details.
+- _Push_: Ryot sends data to an external service when an event occurs.
 
 ## Yank integrations
 
@@ -166,3 +148,31 @@ format. The format of the JSON file should be `CompleteExport` as described in t
 
 You can use this to build integrations with other services that Ryot does not support
 natively.
+
+## Push integrations
+
+You can enable the following push integrations:
+
+### Radarr
+
+Automatically add movies in the selected collections to Radarr.
+
+1. Obtain your Radarr API key by going to the Radarr general settings page.
+2. Fill the inputs in the integration settings page with the correct details.
+
+### Sonarr
+
+Automatically add shows in the selected collections to Sonarr.
+
+1. Obtain your Sonarr API key by going to the Sonarr general settings page.
+2. Fill the inputs in the integration settings page with the correct details.
+
+### Jellyfin
+
+Automatically mark movies and shows as watched in Jellyfin when you mark them as watched
+in Ryot.
+
+1. While creating the integration, you will be asked to provide your Jellyfin username and
+   password.
+2. Every time you mark a movie or show as watched in Ryot, the integration will mark it as
+   watched in Jellyfin.
diff --git a/docs/pyproject.toml b/docs/pyproject.toml
index 59dc852d88..3c3997dfb7 100644
--- a/docs/pyproject.toml
+++ b/docs/pyproject.toml
@@ -4,7 +4,7 @@ version = "0.1.0"
 description = "Documentation website for Ryot"
 authors = ["Diptesh Choudhuri <ignisda2001@gmail.com>"]
 license = "GPL-v3"
-readme = "README.md"
+package-mode = false
 
 [tool.poetry.dependencies]
 python = "^3.11"
diff --git a/libs/generated/package.json b/libs/generated/package.json
index 97ed4aa0fd..a9509561a6 100644
--- a/libs/generated/package.json
+++ b/libs/generated/package.json
@@ -5,7 +5,7 @@
 		"graphql": "16.9.0"
 	},
 	"devDependencies": {
-		"@graphql-codegen/cli": "5.0.2",
-		"@graphql-codegen/client-preset": "4.3.3"
+		"@graphql-codegen/cli": "5.0.3",
+		"@graphql-codegen/client-preset": "4.4.0"
 	}
 }
diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts
index 5a48d7a738..6b0fe4ff17 100644
--- a/libs/generated/src/graphql/backend/gql.ts
+++ b/libs/generated/src/graphql/backend/gql.ts
@@ -22,7 +22,7 @@ const documents = {
     "query GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}": types.GenreDetailsDocument,
     "query GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\n    }\n  }\n}": types.GenresListDocument,
     "query ImportReports {\n  importReports {\n    id\n    source\n    startedOn\n    finishedOn\n    wasSuccess\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        identifier\n        error\n      }\n    }\n  }\n}": types.ImportReportsDocument,
-    "query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}": types.LatestUserSummaryDocument,
+    "query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    totalVideoGameDuration\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}": types.LatestUserSummaryDocument,
     "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    productionStatus\n    originalLanguage\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    bookSpecifics {\n      pages\n    }\n    movieSpecifics {\n      runtime\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n  }\n}": types.MetadataDetailsDocument,
     "query MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\n    }\n  }\n}": types.MetadataGroupDetailsDocument,
     "query MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}": types.MetadataGroupSearchDocument,
@@ -98,7 +98,7 @@ export function graphql(source: "query ImportReports {\n  importReports {\n    i
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}"): (typeof documents)["query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}"];
+export function graphql(source: "query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    totalVideoGameDuration\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}"): (typeof documents)["query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    totalVideoGameDuration\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts
index 897d36e744..00c26b990f 100644
--- a/libs/generated/src/graphql/backend/graphql.ts
+++ b/libs/generated/src/graphql/backend/graphql.ts
@@ -359,6 +359,7 @@ export type DailyUserActivityItem = {
   totalPodcastDuration: Scalars['Int']['output'];
   totalReviewCount: Scalars['Int']['output'];
   totalShowDuration: Scalars['Int']['output'];
+  totalVideoGameDuration: Scalars['Int']['output'];
   totalVisualNovelDuration: Scalars['Int']['output'];
   totalWorkoutDistance: Scalars['Int']['output'];
   totalWorkoutDuration: Scalars['Int']['output'];
@@ -834,7 +835,8 @@ export enum IntegrationProvider {
   Audiobookshelf = 'AUDIOBOOKSHELF',
   Emby = 'EMBY',
   GenericJson = 'GENERIC_JSON',
-  Jellyfin = 'JELLYFIN',
+  JellyfinPush = 'JELLYFIN_PUSH',
+  JellyfinSink = 'JELLYFIN_SINK',
   Kodi = 'KODI',
   Komga = 'KOMGA',
   Plex = 'PLEX',
@@ -845,6 +847,9 @@ export enum IntegrationProvider {
 export type IntegrationSourceSpecificsInput = {
   audiobookshelfBaseUrl?: InputMaybe<Scalars['String']['input']>;
   audiobookshelfToken?: InputMaybe<Scalars['String']['input']>;
+  jellyfinPushBaseUrl?: InputMaybe<Scalars['String']['input']>;
+  jellyfinPushPassword?: InputMaybe<Scalars['String']['input']>;
+  jellyfinPushUsername?: InputMaybe<Scalars['String']['input']>;
   komgaBaseUrl?: InputMaybe<Scalars['String']['input']>;
   komgaPassword?: InputMaybe<Scalars['String']['input']>;
   komgaProvider?: InputMaybe<MediaSource>;
@@ -3078,7 +3083,7 @@ export type ImportReportsQuery = { importReports: Array<{ id: string, source: Im
 export type LatestUserSummaryQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type LatestUserSummaryQuery = { latestUserSummary: { totalMetadataReviewCount: number, totalCollectionReviewCount: number, totalMetadataGroupReviewCount: number, totalPersonReviewCount: number, measurementCount: number, workoutCount: number, totalWorkoutDuration: number, audioBookCount: number, totalAudioBookDuration: number, animeCount: number, bookCount: number, totalBookPages: number, podcastCount: number, totalPodcastDuration: number, mangaCount: number, movieCount: number, totalMovieDuration: number, showCount: number, totalShowDuration: number, videoGameCount: number, visualNovelCount: number, totalVisualNovelDuration: number, totalWorkoutPersonalBests: number, totalWorkoutWeight: number, totalWorkoutReps: number, totalWorkoutDistance: number, totalWorkoutRestTime: number, totalMetadataCount: number, totalReviewCount: number, totalCount: number, totalDuration: number } };
+export type LatestUserSummaryQuery = { latestUserSummary: { totalMetadataReviewCount: number, totalCollectionReviewCount: number, totalMetadataGroupReviewCount: number, totalPersonReviewCount: number, measurementCount: number, workoutCount: number, totalWorkoutDuration: number, audioBookCount: number, totalAudioBookDuration: number, animeCount: number, bookCount: number, totalBookPages: number, podcastCount: number, totalPodcastDuration: number, mangaCount: number, movieCount: number, totalMovieDuration: number, showCount: number, totalShowDuration: number, videoGameCount: number, totalVideoGameDuration: number, visualNovelCount: number, totalVisualNovelDuration: number, totalWorkoutPersonalBests: number, totalWorkoutWeight: number, totalWorkoutReps: number, totalWorkoutDistance: number, totalWorkoutRestTime: number, totalMetadataCount: number, totalReviewCount: number, totalCount: number, totalDuration: number } };
 
 export type MetadataDetailsQueryVariables = Exact<{
   metadataId: Scalars['String']['input'];
@@ -3420,7 +3425,7 @@ export const ExercisesListDocument = {"kind":"Document","definitions":[{"kind":"
 export const GenreDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GenreDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenreDetailsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genreDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"}}]}}]}}]}}]} as unknown as DocumentNode<GenreDetailsQuery, GenreDetailsQueryVariables>;
 export const GenresListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GenresList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genresList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}}]}}]}}]} as unknown as DocumentNode<GenresListQuery, GenresListQueryVariables>;
 export const ImportReportsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ImportReports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"importReports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"wasSuccess"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"import"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"failedItems"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"step"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]}}]}}]} as unknown as DocumentNode<ImportReportsQuery, ImportReportsQueryVariables>;
-export const LatestUserSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LatestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"latestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalMetadataReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCollectionReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataGroupReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalPersonReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalAudioBookDuration"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalBookPages"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalPodcastDuration"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMovieDuration"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalShowDuration"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVisualNovelDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}}]}}]}}]} as unknown as DocumentNode<LatestUserSummaryQuery, LatestUserSummaryQueryVariables>;
+export const LatestUserSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LatestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"latestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalMetadataReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCollectionReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataGroupReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalPersonReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalAudioBookDuration"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalBookPages"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalPodcastDuration"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMovieDuration"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalShowDuration"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVideoGameDuration"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVisualNovelDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}}]}}]}}]} as unknown as DocumentNode<LatestUserSummaryQuery, LatestUserSummaryQueryVariables>;
 export const MetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"isNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"suggestions"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"providerRating"}},{"kind":"Field","name":{"kind":"Name","value":"productionStatus"}},{"kind":"Field","name":{"kind":"Name","value":"originalLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"genres"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"part"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"videoId"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"creators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"languages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movieSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volumes"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"length"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataDetailsQuery, MetadataDetailsQueryVariables>;
 export const MetadataGroupDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataGroupDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataGroupId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataGroupDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataGroupId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataGroupId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"contents"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"parts"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataGroupDetailsQuery, MetadataGroupDetailsQueryVariables>;
 export const MetadataGroupSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataGroupSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataGroupSearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataGroupSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"parts"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataGroupSearchQuery, MetadataGroupSearchQueryVariables>;
diff --git a/libs/graphql/src/backend/mutations/combined.gql b/libs/graphql/src/backend/mutations/combined.gql
index abebb61cca..50545a0d3e 100644
--- a/libs/graphql/src/backend/mutations/combined.gql
+++ b/libs/graphql/src/backend/mutations/combined.gql
@@ -1,251 +1,256 @@
 mutation RegisterUser($input: RegisterUserInput!) {
-  registerUser(input: $input){
-    __typename
-    ... on RegisterError {
-      error
-    }
-    ... on StringIdObject {
-      id
-    }
-  }
+	registerUser(input: $input) {
+		__typename
+		... on RegisterError {
+			error
+		}
+		... on StringIdObject {
+			id
+		}
+	}
 }
 
 mutation LoginUser($input: AuthUserInput!) {
-  loginUser(input: $input) {
-    __typename
-    ... on LoginError {
-      error
-    }
-    ... on LoginResponse {
-      apiKey
-    }
-  }
+	loginUser(input: $input) {
+		__typename
+		... on LoginError {
+			error
+		}
+		... on LoginResponse {
+			apiKey
+		}
+	}
 }
 
 mutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {
-  addEntityToCollection(input: $input)
+	addEntityToCollection(input: $input)
 }
 
 mutation CommitMetadata($input: CommitMediaInput!) {
-  commitMetadata(input: $input) {
-    id
-  }
+	commitMetadata(input: $input) {
+		id
+	}
 }
 
 mutation CommitMetadataGroup($input: CommitMediaInput!) {
-  commitMetadataGroup(input: $input) {
-    id
-  }
+	commitMetadataGroup(input: $input) {
+		id
+	}
 }
 
 mutation CommitPerson($input: CommitPersonInput!) {
-  commitPerson(input: $input) {
-    id
-  }
+	commitPerson(input: $input) {
+		id
+	}
 }
 
 mutation CreateCustomExercise($input: ExerciseInput!) {
-  createCustomExercise(input: $input)
+	createCustomExercise(input: $input)
 }
 
 mutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {
-  updateCustomExercise(input: $input)
+	updateCustomExercise(input: $input)
 }
 
 mutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {
-  updateUserIntegration(input: $input)
+	updateUserIntegration(input: $input)
 }
 
 mutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {
-  createCustomMetadata(input: $input) {
-    id
-  }
+	createCustomMetadata(input: $input) {
+		id
+	}
 }
 
 mutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {
-  createOrUpdateCollection(input: $input) {
-    id
-  }
+	createOrUpdateCollection(input: $input) {
+		id
+	}
 }
 
 mutation CreateReviewComment($input: CreateReviewCommentInput!) {
-  createReviewComment(input: $input)
+	createReviewComment(input: $input)
 }
 
 mutation CreateUserMeasurement($input: UserMeasurementInput!) {
-  createUserMeasurement(input: $input)
+	createUserMeasurement(input: $input)
 }
 
-mutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {
-  createUserNotificationPlatform(input: $input)
+mutation CreateUserNotificationPlatform(
+	$input: CreateUserNotificationPlatformInput!
+) {
+	createUserNotificationPlatform(input: $input)
 }
 
 mutation CreateUserIntegration($input: CreateUserIntegrationInput!) {
-  createUserIntegration(input: $input) {
-    id
-  }
+	createUserIntegration(input: $input) {
+		id
+	}
 }
 
 mutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {
-  createOrUpdateUserWorkout(input: $input)
+	createOrUpdateUserWorkout(input: $input)
 }
 
 mutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {
-  createOrUpdateUserWorkoutTemplate(input: $input)
+	createOrUpdateUserWorkoutTemplate(input: $input)
 }
 
 mutation DeleteCollection($collectionName: String!) {
-  deleteCollection(collectionName: $collectionName)
+	deleteCollection(collectionName: $collectionName)
 }
 
 mutation DeleteReview($reviewId: String!) {
-  deleteReview(reviewId: $reviewId)
+	deleteReview(reviewId: $reviewId)
 }
 
 mutation DeleteS3Object($key: String!) {
-  deleteS3Object(key: $key)
+	deleteS3Object(key: $key)
 }
 
 mutation DeleteSeenItem($seenId: String!) {
-  deleteSeenItem(seenId: $seenId) {
-    id
-  }
+	deleteSeenItem(seenId: $seenId) {
+		id
+	}
 }
 
 mutation DeleteUser($toDeleteUserId: String!) {
-  deleteUser(toDeleteUserId: $toDeleteUserId)
+	deleteUser(toDeleteUserId: $toDeleteUserId)
 }
 
 mutation DeleteUserIntegration($integrationId: String!) {
-  deleteUserIntegration(integrationId: $integrationId)
+	deleteUserIntegration(integrationId: $integrationId)
 }
 
 mutation DeleteUserMeasurement($timestamp: DateTime!) {
-  deleteUserMeasurement(timestamp: $timestamp)
+	deleteUserMeasurement(timestamp: $timestamp)
 }
 
 mutation DeleteUserNotificationPlatform($notificationId: String!) {
-  deleteUserNotificationPlatform(notificationId: $notificationId)
+	deleteUserNotificationPlatform(notificationId: $notificationId)
 }
 
 mutation DeleteUserWorkout($workoutId: String!) {
-  deleteUserWorkout(workoutId: $workoutId)
+	deleteUserWorkout(workoutId: $workoutId)
 }
 
 mutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {
-  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)
+	deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)
 }
 
 mutation DeployBackgroundJob($jobName: BackgroundJob!) {
-  deployBackgroundJob(jobName: $jobName)
+	deployBackgroundJob(jobName: $jobName)
 }
 
 mutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {
-  deployBulkProgressUpdate(input: $input)
+	deployBulkProgressUpdate(input: $input)
 }
 
 mutation DeployExportJob {
-  deployExportJob
+	deployExportJob
 }
 
 mutation DeployImportJob($input: DeployImportJobInput!) {
-  deployImportJob(input: $input)
+	deployImportJob(input: $input)
 }
 
 mutation DeployUpdateMetadataJob($metadataId: String!) {
-  deployUpdateMetadataJob(metadataId: $metadataId)
+	deployUpdateMetadataJob(metadataId: $metadataId)
 }
 
 mutation DeployUpdatePersonJob($personId: String!) {
-  deployUpdatePersonJob(personId: $personId)
+	deployUpdatePersonJob(personId: $personId)
 }
 
 mutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {
-  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)
+	deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)
 }
 
 mutation UpdateSeenItem($input: UpdateSeenItemInput!) {
-  updateSeenItem(input: $input)
+	updateSeenItem(input: $input)
 }
 
-mutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {
-  updateUserNotificationPlatform(input: $input)
+mutation UpdateUserNotificationPlatform(
+	$input: UpdateUserNotificationPlatformInput!
+) {
+	updateUserNotificationPlatform(input: $input)
 }
 
-mutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {
-  updateUserWorkoutAttributes(input: $input)
+mutation UpdateUserWorkoutAttributes(
+	$input: UpdateUserWorkoutAttributesInput!
+) {
+	updateUserWorkoutAttributes(input: $input)
 }
 
 mutation GenerateAuthToken {
-  generateAuthToken
+	generateAuthToken
 }
 
 mutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {
-  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)
+	mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)
 }
 
 mutation DisassociateMetadata($metadataId: String!) {
-  disassociateMetadata(metadataId: $metadataId)
+	disassociateMetadata(metadataId: $metadataId)
 }
 
 mutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {
-  createOrUpdateReview(input: $input) {
-    id
-  }
+	createOrUpdateReview(input: $input) {
+		id
+	}
 }
 
 mutation PresignedPutS3Url($input: PresignedPutUrlInput!) {
-  presignedPutS3Url(input: $input) {
-    key
-    uploadUrl
-  }
+	presignedPutS3Url(input: $input) {
+		key
+		uploadUrl
+	}
 }
 
 mutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {
-  removeEntityFromCollection(input: $input) {
-    id
-  }
+	removeEntityFromCollection(input: $input) {
+		id
+	}
 }
 
 mutation TestUserNotificationPlatforms {
-  testUserNotificationPlatforms
+	testUserNotificationPlatforms
 }
 
 mutation UpdateUser($input: UpdateUserInput!) {
-  updateUser(input: $input) {
-    id
-  }
+	updateUser(input: $input) {
+		id
+	}
 }
 
 mutation UpdateUserPreference($input: UpdateComplexJsonInput!) {
-  updateUserPreference(input: $input)
+	updateUserPreference(input: $input)
 }
 
-
 mutation CreateAccessLink($input: CreateAccessLinkInput!) {
-  createAccessLink(input: $input) {
-    id
-  }
+	createAccessLink(input: $input) {
+		id
+	}
 }
 
 mutation ProcessAccessLink($input: ProcessAccessLinkInput!) {
-  processAccessLink(input: $input) {
-    __typename
-    ... on ProcessAccessLinkError {
-      error
-    }
-    ... on ProcessAccessLinkResponse {
-      apiKey
-      redirectTo
-      tokenValidForDays
-    }
-  }
+	processAccessLink(input: $input) {
+		__typename
+		... on ProcessAccessLinkError {
+			error
+		}
+		... on ProcessAccessLinkResponse {
+			apiKey
+			redirectTo
+			tokenValidForDays
+		}
+	}
 }
 
 mutation RevokeAccessLink($accessLinkId: String!) {
-  revokeAccessLink(accessLinkId: $accessLinkId)
+	revokeAccessLink(accessLinkId: $accessLinkId)
 }
 
 mutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {
-  updateUserExerciseSettings(input: $input)
+	updateUserExerciseSettings(input: $input)
 }
diff --git a/libs/graphql/src/backend/queries/CollectionContents.gql b/libs/graphql/src/backend/queries/CollectionContents.gql
index 0d5ea54dfc..8d3da84d59 100644
--- a/libs/graphql/src/backend/queries/CollectionContents.gql
+++ b/libs/graphql/src/backend/queries/CollectionContents.gql
@@ -1,26 +1,26 @@
 query CollectionContents($input: CollectionContentsInput!) {
-  collectionContents(input: $input) {
-    user {
-      id
-      name
-    }
-    reviews {
-      ...ReviewItemPart
-    }
-    results {
-      details {
-        total
-        nextPage
-      }
-      items {
-        entityId
-        entityLot
-      }
-    }
-    details {
-      name
-      description
-      createdOn
-    }
-  }
+	collectionContents(input: $input) {
+		user {
+			id
+			name
+		}
+		reviews {
+			...ReviewItemPart
+		}
+		results {
+			details {
+				total
+				nextPage
+			}
+			items {
+				entityId
+				entityLot
+			}
+		}
+		details {
+			name
+			description
+			createdOn
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/CoreDetails.gql b/libs/graphql/src/backend/queries/CoreDetails.gql
index 5871f3ff87..c7dd702298 100644
--- a/libs/graphql/src/backend/queries/CoreDetails.gql
+++ b/libs/graphql/src/backend/queries/CoreDetails.gql
@@ -1,16 +1,16 @@
 query CoreDetails {
-  coreDetails {
-    isPro
-    version
-    docsLink
-    pageLimit
-    websiteUrl
-    smtpEnabled
-    oidcEnabled
-    signupAllowed
-    repositoryLink
-    localAuthDisabled
-    fileStorageEnabled
-    tokenValidForDays
-  }
+	coreDetails {
+		isPro
+		version
+		docsLink
+		pageLimit
+		websiteUrl
+		smtpEnabled
+		oidcEnabled
+		signupAllowed
+		repositoryLink
+		localAuthDisabled
+		fileStorageEnabled
+		tokenValidForDays
+	}
 }
diff --git a/libs/graphql/src/backend/queries/ExerciseDetails.gql b/libs/graphql/src/backend/queries/ExerciseDetails.gql
index 75f93fc4cc..b0aed0111b 100644
--- a/libs/graphql/src/backend/queries/ExerciseDetails.gql
+++ b/libs/graphql/src/backend/queries/ExerciseDetails.gql
@@ -1,17 +1,17 @@
 query ExerciseDetails($exerciseId: String!) {
-  exerciseDetails(exerciseId: $exerciseId) {
-    id
-    lot
-    source
-    level
-    force
-    mechanic
-    equipment
-    muscles
-    createdByUserId
-    attributes {
-      instructions
-      images
-    }
-  }
+	exerciseDetails(exerciseId: $exerciseId) {
+		id
+		lot
+		source
+		level
+		force
+		mechanic
+		equipment
+		muscles
+		createdByUserId
+		attributes {
+			instructions
+			images
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/ExerciseParameters.gql b/libs/graphql/src/backend/queries/ExerciseParameters.gql
index e1b8ae1c5e..ff18e7a0a5 100644
--- a/libs/graphql/src/backend/queries/ExerciseParameters.gql
+++ b/libs/graphql/src/backend/queries/ExerciseParameters.gql
@@ -1,17 +1,17 @@
 query ExerciseParameters {
-  exerciseParameters {
-    downloadRequired
-    filters {
-      type
-      level
-      force
-      mechanic
-      equipment
-      muscle
-    }
-    lotMapping {
-      lot
-      bests
-    }
-  }
+	exerciseParameters {
+		downloadRequired
+		filters {
+			type
+			level
+			force
+			mechanic
+			equipment
+			muscle
+		}
+		lotMapping {
+			lot
+			bests
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/ExercisesList.gql b/libs/graphql/src/backend/queries/ExercisesList.gql
index ee41535a6f..071cb233df 100644
--- a/libs/graphql/src/backend/queries/ExercisesList.gql
+++ b/libs/graphql/src/backend/queries/ExercisesList.gql
@@ -1,16 +1,16 @@
 query ExercisesList($input: ExercisesListInput!) {
-  exercisesList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      id
-      lot
-      image
-      muscle
-      numTimesInteracted
-      lastUpdatedOn
-    }
-  }
+	exercisesList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			id
+			lot
+			image
+			muscle
+			numTimesInteracted
+			lastUpdatedOn
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/GenreDetails.gql b/libs/graphql/src/backend/queries/GenreDetails.gql
index 8ac6efc6b4..2cf2db54be 100644
--- a/libs/graphql/src/backend/queries/GenreDetails.gql
+++ b/libs/graphql/src/backend/queries/GenreDetails.gql
@@ -1,16 +1,16 @@
 query GenreDetails($input: GenreDetailsInput!) {
-  genreDetails(input: $input) {
-    details {
-      id
-      name
-      numItems
-    }
-    contents {
-      details {
-        total
-        nextPage
-      }
-      items
-    }
-  }
+	genreDetails(input: $input) {
+		details {
+			id
+			name
+			numItems
+		}
+		contents {
+			details {
+				total
+				nextPage
+			}
+			items
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/GenresList.gql b/libs/graphql/src/backend/queries/GenresList.gql
index a81024ab19..26192b446c 100644
--- a/libs/graphql/src/backend/queries/GenresList.gql
+++ b/libs/graphql/src/backend/queries/GenresList.gql
@@ -1,13 +1,13 @@
 query GenresList($input: SearchInput!) {
-  genresList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      id
-      name
-      numItems
-    }
-  }
+	genresList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			id
+			name
+			numItems
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/ImportReports.gql b/libs/graphql/src/backend/queries/ImportReports.gql
index 5896a37a50..8b20a9581a 100644
--- a/libs/graphql/src/backend/queries/ImportReports.gql
+++ b/libs/graphql/src/backend/queries/ImportReports.gql
@@ -1,20 +1,20 @@
 query ImportReports {
-  importReports {
-    id
-    source
-    startedOn
-    finishedOn
-    wasSuccess
-    details {
-      import {
-        total
-      }
-      failedItems {
-        lot
-        step
-        identifier
-        error
-      }
-    }
-  }
+	importReports {
+		id
+		source
+		startedOn
+		finishedOn
+		wasSuccess
+		details {
+			import {
+				total
+			}
+			failedItems {
+				lot
+				step
+				identifier
+				error
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/MetadataDetails.gql b/libs/graphql/src/backend/queries/MetadataDetails.gql
index 6f5fdde134..caa2c9aea4 100644
--- a/libs/graphql/src/backend/queries/MetadataDetails.gql
+++ b/libs/graphql/src/backend/queries/MetadataDetails.gql
@@ -1,106 +1,106 @@
 query MetadataDetails($metadataId: String!) {
-  metadataDetails(metadataId: $metadataId) {
-    id
-    lot
-    title
-    source
-    isNsfw
-    isPartial
-    sourceUrl
-    identifier
-    description
-    suggestions
-    publishYear
-    publishDate
-    providerRating
-    productionStatus
-    originalLanguage
-    genres {
-      id
-      name
-    }
-    group {
-      id
-      name
-      part
-    }
-    assets {
-      images
-      videos {
-        videoId
-        source
-      }
-    }
-    creators {
-      name
-      items {
-        id
-        name
-        image
-        character
-      }
-    }
-    watchProviders {
-      name
-      image
-      languages
-    }
-    animeSpecifics {
-      episodes
-    }
-    audioBookSpecifics {
-      runtime
-    }
-    bookSpecifics {
-      pages
-    }
-    movieSpecifics {
-      runtime
-    }
-    mangaSpecifics {
-      volumes
-      chapters
-    }
-    podcastSpecifics {
-      episodes {
-        id
-        title
-        overview
-        thumbnail
-        number
-        runtime
-        publishDate
-      }
-      totalEpisodes
-    }
-    showSpecifics {
-      totalSeasons
-      totalEpisodes
-      runtime
-      seasons {
-        id
-        seasonNumber
-        name
-        overview
-        backdropImages
-        posterImages
-        episodes {
-          id
-          name
-          posterImages
-          episodeNumber
-          publishDate
-          name
-          overview
-          runtime
-        }
-      }
-    }
-    visualNovelSpecifics {
-      length
-    }
-    videoGameSpecifics {
-      platforms
-    }
-  }
+	metadataDetails(metadataId: $metadataId) {
+		id
+		lot
+		title
+		source
+		isNsfw
+		isPartial
+		sourceUrl
+		identifier
+		description
+		suggestions
+		publishYear
+		publishDate
+		providerRating
+		productionStatus
+		originalLanguage
+		genres {
+			id
+			name
+		}
+		group {
+			id
+			name
+			part
+		}
+		assets {
+			images
+			videos {
+				videoId
+				source
+			}
+		}
+		creators {
+			name
+			items {
+				id
+				name
+				image
+				character
+			}
+		}
+		watchProviders {
+			name
+			image
+			languages
+		}
+		animeSpecifics {
+			episodes
+		}
+		audioBookSpecifics {
+			runtime
+		}
+		bookSpecifics {
+			pages
+		}
+		movieSpecifics {
+			runtime
+		}
+		mangaSpecifics {
+			volumes
+			chapters
+		}
+		podcastSpecifics {
+			episodes {
+				id
+				title
+				overview
+				thumbnail
+				number
+				runtime
+				publishDate
+			}
+			totalEpisodes
+		}
+		showSpecifics {
+			totalSeasons
+			totalEpisodes
+			runtime
+			seasons {
+				id
+				seasonNumber
+				name
+				overview
+				backdropImages
+				posterImages
+				episodes {
+					id
+					name
+					posterImages
+					episodeNumber
+					publishDate
+					name
+					overview
+					runtime
+				}
+			}
+		}
+		visualNovelSpecifics {
+			length
+		}
+		videoGameSpecifics {
+			platforms
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/MetadataGroupDetails.gql b/libs/graphql/src/backend/queries/MetadataGroupDetails.gql
index d9dfec1bb0..e88e1f388c 100644
--- a/libs/graphql/src/backend/queries/MetadataGroupDetails.gql
+++ b/libs/graphql/src/backend/queries/MetadataGroupDetails.gql
@@ -1,16 +1,16 @@
 query MetadataGroupDetails($metadataGroupId: String!) {
-  metadataGroupDetails(metadataGroupId: $metadataGroupId) {
-    contents
-    sourceUrl
-    details {
-      id
-      title
-      lot
-      source
-      displayImages
-      identifier
-      parts
-      isPartial
-    }
-  }
+	metadataGroupDetails(metadataGroupId: $metadataGroupId) {
+		contents
+		sourceUrl
+		details {
+			id
+			title
+			lot
+			source
+			displayImages
+			identifier
+			parts
+			isPartial
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/MetadataGroupSearch.gql b/libs/graphql/src/backend/queries/MetadataGroupSearch.gql
index 2a5f6f1d7e..8f9b48fe4c 100644
--- a/libs/graphql/src/backend/queries/MetadataGroupSearch.gql
+++ b/libs/graphql/src/backend/queries/MetadataGroupSearch.gql
@@ -1,14 +1,14 @@
 query MetadataGroupSearch($input: MetadataGroupSearchInput!) {
-  metadataGroupSearch(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      identifier
-      name
-      image
-      parts
-    }
-  }
+	metadataGroupSearch(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			identifier
+			name
+			image
+			parts
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/MetadataList.gql b/libs/graphql/src/backend/queries/MetadataList.gql
index f8661edb5e..ef571424dd 100644
--- a/libs/graphql/src/backend/queries/MetadataList.gql
+++ b/libs/graphql/src/backend/queries/MetadataList.gql
@@ -1,9 +1,9 @@
 query MetadataList($input: MetadataListInput!) {
-  metadataList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items
-  }
+	metadataList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items
+	}
 }
diff --git a/libs/graphql/src/backend/queries/MetadataSearch.gql b/libs/graphql/src/backend/queries/MetadataSearch.gql
index 09346fe334..cc777ab81f 100644
--- a/libs/graphql/src/backend/queries/MetadataSearch.gql
+++ b/libs/graphql/src/backend/queries/MetadataSearch.gql
@@ -1,18 +1,18 @@
 query MetadataSearch($input: MetadataSearchInput!) {
-  metadataSearch(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      databaseId
-      hasInteracted
-      item {
-        identifier
-        title
-        image
-        publishYear
-      }
-    }
-  }
+	metadataSearch(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			databaseId
+			hasInteracted
+			item {
+				identifier
+				title
+				image
+				publishYear
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/PeopleSearch.gql b/libs/graphql/src/backend/queries/PeopleSearch.gql
index 30225d9977..06180cadbb 100644
--- a/libs/graphql/src/backend/queries/PeopleSearch.gql
+++ b/libs/graphql/src/backend/queries/PeopleSearch.gql
@@ -1,14 +1,14 @@
 query PeopleSearch($input: PeopleSearchInput!) {
-  peopleSearch(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      identifier
-      name
-      image
-      birthYear
-    }
-  }
+	peopleSearch(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			identifier
+			name
+			image
+			birthYear
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/PersonDetails.gql b/libs/graphql/src/backend/queries/PersonDetails.gql
index c663285f6e..d8d31b67c3 100644
--- a/libs/graphql/src/backend/queries/PersonDetails.gql
+++ b/libs/graphql/src/backend/queries/PersonDetails.gql
@@ -1,27 +1,27 @@
 query PersonDetails($personId: String!) {
-  personDetails(personId: $personId) {
-    sourceUrl
-    details {
-      id
-      name
-      source
-      identifier
-      isPartial
-      description
-      birthDate
-      deathDate
-      place
-      website
-      gender
-      displayImages
-    }
-    contents {
-      name
-      count
-      items {
-        character
-        metadataId
-      }
-    }
-  }
+	personDetails(personId: $personId) {
+		sourceUrl
+		details {
+			id
+			name
+			source
+			identifier
+			isPartial
+			description
+			birthDate
+			deathDate
+			place
+			website
+			gender
+			displayImages
+		}
+		contents {
+			name
+			count
+			items {
+				character
+				metadataId
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserDetails.gql b/libs/graphql/src/backend/queries/UserDetails.gql
index 0c8c91894b..b0988914cd 100644
--- a/libs/graphql/src/backend/queries/UserDetails.gql
+++ b/libs/graphql/src/backend/queries/UserDetails.gql
@@ -1,11 +1,11 @@
 query UserDetails {
-  userDetails {
-    __typename
-    ... on User {
-      id
-      lot
-      name
-      oidcIssuerId
-    }
-  }
+	userDetails {
+		__typename
+		... on User {
+			id
+			lot
+			name
+			oidcIssuerId
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserExerciseDetails.gql b/libs/graphql/src/backend/queries/UserExerciseDetails.gql
index 55309ca4a9..2604e85489 100644
--- a/libs/graphql/src/backend/queries/UserExerciseDetails.gql
+++ b/libs/graphql/src/backend/queries/UserExerciseDetails.gql
@@ -1,46 +1,46 @@
 query UserExerciseDetails($exerciseId: String!) {
-  userExerciseDetails(exerciseId: $exerciseId) {
-    collections {
-      ...CollectionPart
-    }
-    reviews {
-      ...ReviewItemPart
-    }
-    history {
-      idx
-      workoutId
-      workoutEndOn
-      bestSet {
-        ...WorkoutSetRecordPart
-      }
-    }
-    details {
-      exerciseId
-      createdOn
-      lastUpdatedOn
-      exerciseNumTimesInteracted
-      exerciseExtraInformation {
-        settings {
-          setRestTimers {
-            ...SetRestTimersPart
-          }
-        }
-        lifetimeStats {
-          weight
-          reps
-          distance
-          duration
-          personalBestsAchieved
-        }
-        personalBests {
-          lot
-          sets {
-            workoutId
-            exerciseIdx
-            setIdx
-          }
-        }
-      }
-    }
-  }
+	userExerciseDetails(exerciseId: $exerciseId) {
+		collections {
+			...CollectionPart
+		}
+		reviews {
+			...ReviewItemPart
+		}
+		history {
+			idx
+			workoutId
+			workoutEndOn
+			bestSet {
+				...WorkoutSetRecordPart
+			}
+		}
+		details {
+			exerciseId
+			createdOn
+			lastUpdatedOn
+			exerciseNumTimesInteracted
+			exerciseExtraInformation {
+				settings {
+					setRestTimers {
+						...SetRestTimersPart
+					}
+				}
+				lifetimeStats {
+					weight
+					reps
+					distance
+					duration
+					personalBestsAchieved
+				}
+				personalBests {
+					lot
+					sets {
+						workoutId
+						exerciseIdx
+						setIdx
+					}
+				}
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserMeasurementsList.gql b/libs/graphql/src/backend/queries/UserMeasurementsList.gql
index f4752491cb..7b189c308b 100644
--- a/libs/graphql/src/backend/queries/UserMeasurementsList.gql
+++ b/libs/graphql/src/backend/queries/UserMeasurementsList.gql
@@ -1,33 +1,33 @@
 query UserMeasurementsList($input: UserMeasurementsListInput!) {
-  userMeasurementsList(input: $input) {
-    timestamp
-    name
-    comment
-    stats {
-      weight
-      bodyMassIndex
-      totalBodyWater
-      muscle
-      leanBodyMass
-      bodyFat
-      boneMass
-      visceralFat
-      waistCircumference
-      waistToHeightRatio
-      hipCircumference
-      waistToHipRatio
-      chestCircumference
-      thighCircumference
-      bicepsCircumference
-      neckCircumference
-      bodyFatCaliper
-      chestSkinfold
-      abdominalSkinfold
-      thighSkinfold
-      basalMetabolicRate
-      totalDailyEnergyExpenditure
-      calories
-      custom
-    }
-  }
+	userMeasurementsList(input: $input) {
+		timestamp
+		name
+		comment
+		stats {
+			weight
+			bodyMassIndex
+			totalBodyWater
+			muscle
+			leanBodyMass
+			bodyFat
+			boneMass
+			visceralFat
+			waistCircumference
+			waistToHeightRatio
+			hipCircumference
+			waistToHipRatio
+			chestCircumference
+			thighCircumference
+			bicepsCircumference
+			neckCircumference
+			bodyFatCaliper
+			chestSkinfold
+			abdominalSkinfold
+			thighSkinfold
+			basalMetabolicRate
+			totalDailyEnergyExpenditure
+			calories
+			custom
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserMetadataDetails.gql b/libs/graphql/src/backend/queries/UserMetadataDetails.gql
index 3c242068f2..9b3bdeb9a6 100644
--- a/libs/graphql/src/backend/queries/UserMetadataDetails.gql
+++ b/libs/graphql/src/backend/queries/UserMetadataDetails.gql
@@ -1,39 +1,39 @@
 query UserMetadataDetails($metadataId: String!) {
-  userMetadataDetails(metadataId: $metadataId) {
-    mediaReason
-    hasInteracted
-    collections {
-      ...CollectionPart
-    }
-    inProgress {
-      ...SeenPart
-    }
-    history {
-      ...SeenPart
-    }
-    averageRating
-    reviews {
-      ...ReviewItemPart
-    }
-    seenByAllCount
-    seenByUserCount
-    nextEntry {
-      season
-      volume
-      episode
-      chapter
-    }
-    showProgress {
-      timesSeen
-      seasonNumber
-      episodes {
-        episodeNumber
-        timesSeen
-      }
-    }
-    podcastProgress {
-      episodeNumber
-      timesSeen
-    }
-  }
+	userMetadataDetails(metadataId: $metadataId) {
+		mediaReason
+		hasInteracted
+		collections {
+			...CollectionPart
+		}
+		inProgress {
+			...SeenPart
+		}
+		history {
+			...SeenPart
+		}
+		averageRating
+		reviews {
+			...ReviewItemPart
+		}
+		seenByAllCount
+		seenByUserCount
+		nextEntry {
+			season
+			volume
+			episode
+			chapter
+		}
+		showProgress {
+			timesSeen
+			seasonNumber
+			episodes {
+				episodeNumber
+				timesSeen
+			}
+		}
+		podcastProgress {
+			episodeNumber
+			timesSeen
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserMetadataGroupDetails.gql b/libs/graphql/src/backend/queries/UserMetadataGroupDetails.gql
index 8ad73f8770..fcf020378f 100644
--- a/libs/graphql/src/backend/queries/UserMetadataGroupDetails.gql
+++ b/libs/graphql/src/backend/queries/UserMetadataGroupDetails.gql
@@ -1,10 +1,10 @@
 query UserMetadataGroupDetails($metadataGroupId: String!) {
-  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {
-    reviews {
-      ...ReviewItemPart
-    }
-    collections {
-      ...CollectionPart
-    }
-  }
+	userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {
+		reviews {
+			...ReviewItemPart
+		}
+		collections {
+			...CollectionPart
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserPersonDetails.gql b/libs/graphql/src/backend/queries/UserPersonDetails.gql
index 79cdb6e256..4461438471 100644
--- a/libs/graphql/src/backend/queries/UserPersonDetails.gql
+++ b/libs/graphql/src/backend/queries/UserPersonDetails.gql
@@ -1,10 +1,10 @@
 query UserPersonDetails($personId: String!) {
-  userPersonDetails(personId: $personId) {
-    collections {
-      ...CollectionPart
-    }
-    reviews {
-      ...ReviewItemPart
-    }
-  }
+	userPersonDetails(personId: $personId) {
+		collections {
+			...CollectionPart
+		}
+		reviews {
+			...ReviewItemPart
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserPreferences.gql b/libs/graphql/src/backend/queries/UserPreferences.gql
index 0994072e8c..e52c602b15 100644
--- a/libs/graphql/src/backend/queries/UserPreferences.gql
+++ b/libs/graphql/src/backend/queries/UserPreferences.gql
@@ -1,94 +1,94 @@
 query UserPreferences {
-  userPreferences {
-    general {
-      reviewScale
-      gridPacking
-      displayNsfw
-      disableVideos
-      persistQueries
-      disableReviews
-      disableIntegrations
-      disableWatchProviders
-      disableNavigationAnimation
-      dashboard {
-        section
-        hidden
-        numElements
-      }
-      watchProviders {
-        lot
-        values
-      }
-    }
-    fitness {
-      measurements {
-        custom {
-          name
-          dataType
-        }
-        inbuilt {
-          weight
-          bodyMassIndex
-          totalBodyWater
-          muscle
-          leanBodyMass
-          bodyFat
-          boneMass
-          visceralFat
-          waistCircumference
-          waistToHeightRatio
-          hipCircumference
-          waistToHipRatio
-          chestCircumference
-          thighCircumference
-          bicepsCircumference
-          neckCircumference
-          bodyFatCaliper
-          chestSkinfold
-          abdominalSkinfold
-          thighSkinfold
-          basalMetabolicRate
-          totalDailyEnergyExpenditure
-          calories
-        }
-      }
-      exercises {
-        unitSystem
-        setRestTimers {
-          ...SetRestTimersPart
-        }
-      }
-    }
-    notifications {
-      toSend
-      enabled
-    }
-    featuresEnabled {
-      others {
-        calendar
-        collections
-      }
-      fitness {
-        enabled
-        workouts
-        templates
-        measurements
-      }
-      media {
-        enabled
-        anime
-        audioBook
-        book
-        manga
-        movie
-        podcast
-        show
-        videoGame
-        visualNovel
-        people
-        groups
-        genres
-      }
-    }
-  }
+	userPreferences {
+		general {
+			reviewScale
+			gridPacking
+			displayNsfw
+			disableVideos
+			persistQueries
+			disableReviews
+			disableIntegrations
+			disableWatchProviders
+			disableNavigationAnimation
+			dashboard {
+				section
+				hidden
+				numElements
+			}
+			watchProviders {
+				lot
+				values
+			}
+		}
+		fitness {
+			measurements {
+				custom {
+					name
+					dataType
+				}
+				inbuilt {
+					weight
+					bodyMassIndex
+					totalBodyWater
+					muscle
+					leanBodyMass
+					bodyFat
+					boneMass
+					visceralFat
+					waistCircumference
+					waistToHeightRatio
+					hipCircumference
+					waistToHipRatio
+					chestCircumference
+					thighCircumference
+					bicepsCircumference
+					neckCircumference
+					bodyFatCaliper
+					chestSkinfold
+					abdominalSkinfold
+					thighSkinfold
+					basalMetabolicRate
+					totalDailyEnergyExpenditure
+					calories
+				}
+			}
+			exercises {
+				unitSystem
+				setRestTimers {
+					...SetRestTimersPart
+				}
+			}
+		}
+		notifications {
+			toSend
+			enabled
+		}
+		featuresEnabled {
+			others {
+				calendar
+				collections
+			}
+			fitness {
+				enabled
+				workouts
+				templates
+				measurements
+			}
+			media {
+				enabled
+				anime
+				audioBook
+				book
+				manga
+				movie
+				podcast
+				show
+				videoGame
+				visualNovel
+				people
+				groups
+				genres
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserWorkoutDetails.gql b/libs/graphql/src/backend/queries/UserWorkoutDetails.gql
index b50f704563..bbd2a383aa 100644
--- a/libs/graphql/src/backend/queries/UserWorkoutDetails.gql
+++ b/libs/graphql/src/backend/queries/UserWorkoutDetails.gql
@@ -1,22 +1,22 @@
 query UserWorkoutDetails($workoutId: String!) {
-  userWorkoutDetails(workoutId: $workoutId) {
-    collections {
-      ...CollectionPart
-    }
-    details {
-      id
-      name
-      endTime
-      duration
-      startTime
-      templateId
-      repeatedFrom
-      summary {
-        ...WorkoutSummaryPart
-      }
-      information {
-        ...WorkoutInformationPart
-      }
-    }
-  }
+	userWorkoutDetails(workoutId: $workoutId) {
+		collections {
+			...CollectionPart
+		}
+		details {
+			id
+			name
+			endTime
+			duration
+			startTime
+			templateId
+			repeatedFrom
+			summary {
+				...WorkoutSummaryPart
+			}
+			information {
+				...WorkoutInformationPart
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserWorkoutTemplateDetails.gql b/libs/graphql/src/backend/queries/UserWorkoutTemplateDetails.gql
index a6b9fbc27b..7b51652335 100644
--- a/libs/graphql/src/backend/queries/UserWorkoutTemplateDetails.gql
+++ b/libs/graphql/src/backend/queries/UserWorkoutTemplateDetails.gql
@@ -1,19 +1,19 @@
 query UserWorkoutTemplateDetails($workoutTemplateId: String!) {
-  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {
-    collections {
-      ...CollectionPart
-    }
-    details {
-      id
-      name
-      createdOn
-      visibility
-      summary {
-        ...WorkoutSummaryPart
-      }
-      information {
-        ...WorkoutInformationPart
-      }
-    }
-  }
+	userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {
+		collections {
+			...CollectionPart
+		}
+		details {
+			id
+			name
+			createdOn
+			visibility
+			summary {
+				...WorkoutSummaryPart
+			}
+			information {
+				...WorkoutInformationPart
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserWorkoutTemplatesList.gql b/libs/graphql/src/backend/queries/UserWorkoutTemplatesList.gql
index 30a6c13583..8185202772 100644
--- a/libs/graphql/src/backend/queries/UserWorkoutTemplatesList.gql
+++ b/libs/graphql/src/backend/queries/UserWorkoutTemplatesList.gql
@@ -1,17 +1,17 @@
 query UserWorkoutTemplatesList($input: SearchInput!) {
-  userWorkoutTemplatesList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      id
-      name
-      createdOn
-      visibility
-      summary {
-        ...WorkoutSummaryPart
-      }
-    }
-  }
+	userWorkoutTemplatesList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			id
+			name
+			createdOn
+			visibility
+			summary {
+				...WorkoutSummaryPart
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/UserWorkoutsList.gql b/libs/graphql/src/backend/queries/UserWorkoutsList.gql
index 941df44c3f..ccb4e1b1d3 100644
--- a/libs/graphql/src/backend/queries/UserWorkoutsList.gql
+++ b/libs/graphql/src/backend/queries/UserWorkoutsList.gql
@@ -1,18 +1,18 @@
 query UserWorkoutsList($input: SearchInput!) {
-  userWorkoutsList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items {
-      id
-      name
-      endTime
-      duration
-      startTime
-      summary {
-        ...WorkoutSummaryPart
-      }
-    }
-  }
+	userWorkoutsList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items {
+			id
+			name
+			endTime
+			duration
+			startTime
+			summary {
+				...WorkoutSummaryPart
+			}
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/combined.gql b/libs/graphql/src/backend/queries/combined.gql
index be673a7b59..79b55d6e35 100644
--- a/libs/graphql/src/backend/queries/combined.gql
+++ b/libs/graphql/src/backend/queries/combined.gql
@@ -1,180 +1,180 @@
 query GetOidcRedirectUrl {
-  getOidcRedirectUrl
+	getOidcRedirectUrl
 }
 
 query UserByOidcIssuerId($oidcIssuerId: String!) {
-  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)
+	userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)
 }
 
 query GetOidcToken($code: String!) {
-  getOidcToken(code: $code) {
-    subject
-    email
-  }
+	getOidcToken(code: $code) {
+		subject
+		email
+	}
 }
 
 query GetPresignedS3Url($key: String!) {
-  getPresignedS3Url(key: $key)
+	getPresignedS3Url(key: $key)
 }
 
 query ProvidersLanguageInformation {
-  providersLanguageInformation {
-    supported
-    default
-    source
-  }
+	providersLanguageInformation {
+		supported
+		default
+		source
+	}
 }
 
 query UserExports {
-  userExports {
-    url
-    size
-    endedAt
-    startedAt
-  }
+	userExports {
+		url
+		size
+		endedAt
+		startedAt
+	}
 }
 
 query UserCollectionsList($name: String) {
-  userCollectionsList(name: $name) {
-    id
-    name
-    count
-    isDefault
-    description
-    creator {
-      id
-      name
-    }
-    collaborators {
-      id
-      name
-    }
-    informationTemplate {
-      lot
-      name
-      required
-      description
-      defaultValue
-    }
-  }
+	userCollectionsList(name: $name) {
+		id
+		name
+		count
+		isDefault
+		description
+		creator {
+			id
+			name
+		}
+		collaborators {
+			id
+			name
+		}
+		informationTemplate {
+			lot
+			name
+			required
+			description
+			defaultValue
+		}
+	}
 }
 
 query UserIntegrations {
-  userIntegrations {
-    id
-    lot
-    provider
-    createdOn
-    isDisabled
-    maximumProgress
-    minimumProgress
-    lastTriggeredOn
-    syncToOwnedCollection
-  }
+	userIntegrations {
+		id
+		lot
+		provider
+		createdOn
+		isDisabled
+		maximumProgress
+		minimumProgress
+		lastTriggeredOn
+		syncToOwnedCollection
+	}
 }
 
 query UserNotificationPlatforms {
-  userNotificationPlatforms {
-    id
-    lot
-    createdOn
-    isDisabled
-    description
-  }
+	userNotificationPlatforms {
+		id
+		lot
+		createdOn
+		isDisabled
+		description
+	}
 }
 
 query UsersList($query: String) {
-  usersList(query: $query) {
-    id
-    lot
-    name
-    isDisabled
-  }
+	usersList(query: $query) {
+		id
+		lot
+		name
+		isDisabled
+	}
 }
 
 query UserRecommendations {
-  userRecommendations
+	userRecommendations
 }
 
 query UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {
-  userUpcomingCalendarEvents(input: $input) {
-    ...CalendarEventPart
-  }
+	userUpcomingCalendarEvents(input: $input) {
+		...CalendarEventPart
+	}
 }
 
 query UserCalendarEvents($input: UserCalendarEventInput!) {
-  userCalendarEvents(input: $input) {
-    date
-    events {
-      ...CalendarEventPart
-    }
-  }
+	userCalendarEvents(input: $input) {
+		date
+		events {
+			...CalendarEventPart
+		}
+	}
 }
 
 query MetadataPartialDetails($metadataId: String!) {
-  metadataPartialDetails(metadataId: $metadataId) {
-    id
-    lot
-    title
-    image
-    publishYear
-  }
+	metadataPartialDetails(metadataId: $metadataId) {
+		id
+		lot
+		title
+		image
+		publishYear
+	}
 }
 
 query MetadataGroupsList($input: MetadataGroupsListInput!) {
-  metadataGroupsList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items
-  }
+	metadataGroupsList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items
+	}
 }
 
 query PeopleList($input: PeopleListInput!) {
-  peopleList(input: $input) {
-    details {
-      total
-      nextPage
-    }
-    items
-  }
+	peopleList(input: $input) {
+		details {
+			total
+			nextPage
+		}
+		items
+	}
 }
 
 query UserAccessLinks {
-  userAccessLinks {
-    id
-    name
-    isDemo
-    createdOn
-    expiresOn
-    timesUsed
-    isRevoked
-    maximumUses
-    isAccountDefault
-    isMutationAllowed
-  }
+	userAccessLinks {
+		id
+		name
+		isDemo
+		createdOn
+		expiresOn
+		timesUsed
+		isRevoked
+		maximumUses
+		isAccountDefault
+		isMutationAllowed
+	}
 }
 
 query DailyUserActivities($input: DailyUserActivitiesInput!) {
-  dailyUserActivities(input: $input) {
-    groupedBy
-    totalCount
-    totalDuration
-    items {
-      day
-      totalReviewCount
-      workoutCount
-      measurementCount
-      audioBookCount
-      animeCount
-      bookCount
-      podcastCount
-      mangaCount
-      showCount
-      movieCount
-      videoGameCount
-      visualNovelCount
-    }
-  }
+	dailyUserActivities(input: $input) {
+		groupedBy
+		totalCount
+		totalDuration
+		items {
+			day
+			totalReviewCount
+			workoutCount
+			measurementCount
+			audioBookCount
+			animeCount
+			bookCount
+			podcastCount
+			mangaCount
+			showCount
+			movieCount
+			videoGameCount
+			visualNovelCount
+		}
+	}
 }
diff --git a/libs/graphql/src/backend/queries/fragments.gql b/libs/graphql/src/backend/queries/fragments.gql
index 2eee276e82..f253e8beab 100644
--- a/libs/graphql/src/backend/queries/fragments.gql
+++ b/libs/graphql/src/backend/queries/fragments.gql
@@ -1,196 +1,196 @@
 fragment SeenPodcastExtraInformationPart on SeenPodcastExtraInformation {
-  episode
+	episode
 }
 
 fragment SeenShowExtraInformationPart on SeenShowExtraInformation {
-  episode
-  season
+	episode
+	season
 }
 
 fragment SeenAnimeExtraInformationPart on SeenAnimeExtraInformation {
-  episode
+	episode
 }
 
 fragment SeenMangaExtraInformationPart on SeenMangaExtraInformation {
-  volume
-  chapter
+	volume
+	chapter
 }
 
 fragment CalendarEventPart on GraphqlCalendarEvent {
-  date
-  metadataId
-  metadataLot
-  episodeName
-  metadataTitle
-  metadataImage
-  calendarEventId
-  showExtraInformation {
-    ...SeenShowExtraInformationPart
-  }
-  podcastExtraInformation {
-    ...SeenPodcastExtraInformationPart
-  }
-  animeExtraInformation {
-    ...SeenAnimeExtraInformationPart
-  }
+	date
+	metadataId
+	metadataLot
+	episodeName
+	metadataTitle
+	metadataImage
+	calendarEventId
+	showExtraInformation {
+		...SeenShowExtraInformationPart
+	}
+	podcastExtraInformation {
+		...SeenPodcastExtraInformationPart
+	}
+	animeExtraInformation {
+		...SeenAnimeExtraInformationPart
+	}
 }
 
 fragment SeenPart on Seen {
-  id
-  state
-  progress
-  reviewId
-  startedOn
-  finishedOn
-  lastUpdatedOn
-  manualTimeSpent
-  numTimesUpdated
-  providerWatchedOn
-  showExtraInformation {
-    ...SeenShowExtraInformationPart
-  }
-  podcastExtraInformation {
-    ...SeenPodcastExtraInformationPart
-  }
-  animeExtraInformation {
-    ...SeenAnimeExtraInformationPart
-  }
-  mangaExtraInformation {
-    ...SeenMangaExtraInformationPart
-  }
+	id
+	state
+	progress
+	reviewId
+	startedOn
+	finishedOn
+	lastUpdatedOn
+	manualTimeSpent
+	numTimesUpdated
+	providerWatchedOn
+	showExtraInformation {
+		...SeenShowExtraInformationPart
+	}
+	podcastExtraInformation {
+		...SeenPodcastExtraInformationPart
+	}
+	animeExtraInformation {
+		...SeenAnimeExtraInformationPart
+	}
+	mangaExtraInformation {
+		...SeenMangaExtraInformationPart
+	}
 }
 
 fragment MetadataSearchItemPart on MetadataSearchItem {
-  title
-  image
-  identifier
-  publishYear
+	title
+	image
+	identifier
+	publishYear
 }
 
 fragment WorkoutOrExerciseTotalsPart on WorkoutOrExerciseTotals {
-  reps
-  weight
-  distance
-  duration
-  restTime
-  personalBestsAchieved
+	reps
+	weight
+	distance
+	duration
+	restTime
+	personalBestsAchieved
 }
 
 fragment EntityAssetsPart on EntityAssets {
-  images
-  videos
+	images
+	videos
 }
 
 fragment WorkoutSetStatisticPart on WorkoutSetStatistic {
-  reps
-  pace
-  oneRm
-  weight
-  volume
-  duration
-  distance
+	reps
+	pace
+	oneRm
+	weight
+	volume
+	duration
+	distance
 }
 
 fragment WorkoutSetRecordPart on WorkoutSetRecord {
-  lot
-  personalBests
-  statistic {
-    ...WorkoutSetStatisticPart
-  }
+	lot
+	personalBests
+	statistic {
+		...WorkoutSetStatisticPart
+	}
 }
 
 fragment WorkoutSummaryPart on WorkoutSummary {
-  total {
-    ...WorkoutOrExerciseTotalsPart
-  }
-  exercises {
-    lot
-    name
-    numSets
-    bestSet {
-      ...WorkoutSetRecordPart
-    }
-  }
+	total {
+		...WorkoutOrExerciseTotalsPart
+	}
+	exercises {
+		lot
+		name
+		numSets
+		bestSet {
+			...WorkoutSetRecordPart
+		}
+	}
 }
 
 fragment CollectionPart on Collection {
-  id
-  name
-  userId
+	id
+	name
+	userId
 }
 
 fragment ReviewItemPart on ReviewItem {
-  id
-  rating
-  postedOn
-  isSpoiler
-  visibility
-  textOriginal
-  textRendered
-  seenItemsAssociatedWith
-  postedBy {
-    id
-    name
-  }
-  comments {
-    id
-    text
-    likedBy
-    createdOn
-    user {
-      id
-      name
-    }
-  }
-  showExtraInformation {
-    ...SeenShowExtraInformationPart
-  }
-  podcastExtraInformation {
-    ...SeenPodcastExtraInformationPart
-  }
-  animeExtraInformation {
-    ...SeenAnimeExtraInformationPart
-  }
-  mangaExtraInformation {
-    ...SeenMangaExtraInformationPart
-  }
+	id
+	rating
+	postedOn
+	isSpoiler
+	visibility
+	textOriginal
+	textRendered
+	seenItemsAssociatedWith
+	postedBy {
+		id
+		name
+	}
+	comments {
+		id
+		text
+		likedBy
+		createdOn
+		user {
+			id
+			name
+		}
+	}
+	showExtraInformation {
+		...SeenShowExtraInformationPart
+	}
+	podcastExtraInformation {
+		...SeenPodcastExtraInformationPart
+	}
+	animeExtraInformation {
+		...SeenAnimeExtraInformationPart
+	}
+	mangaExtraInformation {
+		...SeenMangaExtraInformationPart
+	}
 }
 
 fragment WorkoutInformationPart on WorkoutInformation {
-  comment
-  assets {
-    ...EntityAssetsPart
-  }
-  supersets {
-    color
-    exercises
-  }
-  exercises {
-    lot
-    name
-    notes
-    total {
-      ...WorkoutOrExerciseTotalsPart
-    }
-    assets {
-      ...EntityAssetsPart
-    }
-    sets {
-      lot
-      note
-      restTime
-      confirmedAt
-      personalBests
-      statistic {
-        ...WorkoutSetStatisticPart
-      }
-    }
-  }
+	comment
+	assets {
+		...EntityAssetsPart
+	}
+	supersets {
+		color
+		exercises
+	}
+	exercises {
+		lot
+		name
+		notes
+		total {
+			...WorkoutOrExerciseTotalsPart
+		}
+		assets {
+			...EntityAssetsPart
+		}
+		sets {
+			lot
+			note
+			restTime
+			confirmedAt
+			personalBests
+			statistic {
+				...WorkoutSetStatisticPart
+			}
+		}
+	}
 }
 
 fragment SetRestTimersPart on SetRestTimersSettings {
-  drop
-  warmup
-  normal
-  failure
+	drop
+	warmup
+	normal
+	failure
 }
diff --git a/libs/transactional/package.json b/libs/transactional/package.json
index 14e7734301..e2ca63d46d 100644
--- a/libs/transactional/package.json
+++ b/libs/transactional/package.json
@@ -4,7 +4,12 @@
 	"dependencies": {
 		"@react-email/components": "0.0.22",
 		"@ryot/ts-utils": "workspace:*",
+		"react": "18.3.1",
 		"react-dom": "18.3.1",
 		"react-email": "2.1.6"
+	},
+	"devDependencies": {
+		"@types/react": "18.3.11",
+		"@types/react-dom": "18.3.1"
 	}
 }
diff --git a/libs/ts-utils/package.json b/libs/ts-utils/package.json
index 1f4ccdff0d..55feea8f15 100644
--- a/libs/ts-utils/package.json
+++ b/libs/ts-utils/package.json
@@ -2,13 +2,14 @@
 	"name": "@ryot/ts-utils",
 	"main": "src/index.ts",
 	"dependencies": {
-		"@conform-to/zod": "1.1.5",
+		"@conform-to/zod": "1.2.2",
 		"@ryot/generated": "workspace:*",
 		"dayjs": "1.11.13",
 		"humanize-duration-ts": "2.1.1",
-		"lodash": "4.17.21"
+		"lodash": "4.17.21",
+		"tiny-invariant": "1.3.3"
 	},
 	"devDependencies": {
-		"@types/lodash": "4.17.7"
+		"@types/lodash": "4.17.12"
 	}
 }
diff --git a/libs/ts-utils/src/index.ts b/libs/ts-utils/src/index.ts
index 0070c57e1d..6b3a10f0c8 100644
--- a/libs/ts-utils/src/index.ts
+++ b/libs/ts-utils/src/index.ts
@@ -25,6 +25,7 @@ import sortBy from "lodash/sortBy";
 import startCase from "lodash/startCase";
 import sum from "lodash/sum";
 import truncate from "lodash/truncate";
+import invariant from "tiny-invariant";
 import type { ZodTypeAny, output } from "zod";
 
 /**
@@ -94,6 +95,13 @@ export const processSubmission = <Schema extends ZodTypeAny>(
 	return submission.value;
 };
 
+export const getActionIntent = (request: Request) => {
+	const url = new URL(request.url);
+	const intent = url.searchParams.get("intent");
+	invariant(intent);
+	return intent;
+};
+
 export {
 	camelCase,
 	cloneDeep,
diff --git a/package.json b/package.json
index d7641da1af..e67dbbdc90 100644
--- a/package.json
+++ b/package.json
@@ -13,9 +13,9 @@
 		"node": "20.10.0"
 	},
 	"devDependencies": {
-		"@biomejs/biome": "1.8.3",
+		"@biomejs/biome": "1.9.4",
 		"tsconfig-moon": "1.3.0",
-		"typescript": "5.5.4"
+		"typescript": "5.6.3"
 	},
 	"resolutions": {
 		"@types/react": "18.3.4"
diff --git a/yarn.lock b/yarn.lock
index f437cc8af9..2d77a6599e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1472,12 +1472,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.9.2":
-  version: 7.23.1
-  resolution: "@babel/runtime@npm:7.23.1"
+"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.23.5, @babel/runtime@npm:^7.25.6":
+  version: 7.25.9
+  resolution: "@babel/runtime@npm:7.25.9"
   dependencies:
     regenerator-runtime: "npm:^0.14.0"
-  checksum: 10/a9fdd322ae1f5d5e3446b6181745300f863164a30acb35ee296c6989cf0ecfd57598a7a6ef209b414575cabe81ef17756412052b3d85fcaf8729332b5b70c45f
+  checksum: 10/8d904cfcb433374b3bb90369452751c94ae69547cdd3679950de4527ac5d04195b9c4a1840482a6f3a84694cb22a6403a7f98b826d60cd945918223a4a6b479c
   languageName: node
   linkType: hard
 
@@ -1490,7 +1490,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.21.0":
+"@babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.18.3":
   version: 7.25.6
   resolution: "@babel/runtime@npm:7.25.6"
   dependencies:
@@ -1499,21 +1499,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/runtime@npm:^7.23.5":
-  version: 7.24.8
-  resolution: "@babel/runtime@npm:7.24.8"
-  dependencies:
-    regenerator-runtime: "npm:^0.14.0"
-  checksum: 10/e6f335e472a8a337379effc15815dd0eddf6a7d0c00b50deb4f9e9585819b45431d0ff3c2d3d0fa58c227a9b04dcc4a85e7245fb57493adb2863b5208c769cbd
-  languageName: node
-  linkType: hard
-
-"@babel/runtime@npm:^7.24.1":
-  version: 7.24.4
-  resolution: "@babel/runtime@npm:7.24.4"
+"@babel/runtime@npm:^7.20.13":
+  version: 7.23.1
+  resolution: "@babel/runtime@npm:7.23.1"
   dependencies:
     regenerator-runtime: "npm:^0.14.0"
-  checksum: 10/8ec8ce2c145bc7e31dd39ab66df124f357f65c11489aefacb30f431bae913b9aaa66aa5efe5321ea2bf8878af3fcee338c87e7599519a952e3a6f83aa1b03308
+  checksum: 10/a9fdd322ae1f5d5e3446b6181745300f863164a30acb35ee296c6989cf0ecfd57598a7a6ef209b414575cabe81ef17756412052b3d85fcaf8729332b5b70c45f
   languageName: node
   linkType: hard
 
@@ -1736,18 +1727,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@biomejs/biome@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/biome@npm:1.8.3"
+"@biomejs/biome@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/biome@npm:1.9.4"
   dependencies:
-    "@biomejs/cli-darwin-arm64": "npm:1.8.3"
-    "@biomejs/cli-darwin-x64": "npm:1.8.3"
-    "@biomejs/cli-linux-arm64": "npm:1.8.3"
-    "@biomejs/cli-linux-arm64-musl": "npm:1.8.3"
-    "@biomejs/cli-linux-x64": "npm:1.8.3"
-    "@biomejs/cli-linux-x64-musl": "npm:1.8.3"
-    "@biomejs/cli-win32-arm64": "npm:1.8.3"
-    "@biomejs/cli-win32-x64": "npm:1.8.3"
+    "@biomejs/cli-darwin-arm64": "npm:1.9.4"
+    "@biomejs/cli-darwin-x64": "npm:1.9.4"
+    "@biomejs/cli-linux-arm64": "npm:1.9.4"
+    "@biomejs/cli-linux-arm64-musl": "npm:1.9.4"
+    "@biomejs/cli-linux-x64": "npm:1.9.4"
+    "@biomejs/cli-linux-x64-musl": "npm:1.9.4"
+    "@biomejs/cli-win32-arm64": "npm:1.9.4"
+    "@biomejs/cli-win32-x64": "npm:1.9.4"
   dependenciesMeta:
     "@biomejs/cli-darwin-arm64":
       optional: true
@@ -1767,62 +1758,62 @@ __metadata:
       optional: true
   bin:
     biome: bin/biome
-  checksum: 10/62dfa5147712ef21c384ea7b3c93c0ccac58291a85f2bbd2dee22c8381da5e347cd07bdb7bfcafcecb07fc112349e9d101e697774155553bde987fd47f9b12a1
+  checksum: 10/bd8ff8fb4dc0581bd60a9b9ac28d0cd03ba17c6a1de2ab6228b7fda582079594ceee774f47e41aac2fc6d35de1637def2e32ef2e58fa24e22d1b24ef9ee5cefa
   languageName: node
   linkType: hard
 
-"@biomejs/cli-darwin-arm64@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-darwin-arm64@npm:1.8.3"
+"@biomejs/cli-darwin-arm64@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-darwin-arm64@npm:1.9.4"
   conditions: os=darwin & cpu=arm64
   languageName: node
   linkType: hard
 
-"@biomejs/cli-darwin-x64@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-darwin-x64@npm:1.8.3"
+"@biomejs/cli-darwin-x64@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-darwin-x64@npm:1.9.4"
   conditions: os=darwin & cpu=x64
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-arm64-musl@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-linux-arm64-musl@npm:1.8.3"
+"@biomejs/cli-linux-arm64-musl@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-linux-arm64-musl@npm:1.9.4"
   conditions: os=linux & cpu=arm64 & libc=musl
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-arm64@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-linux-arm64@npm:1.8.3"
+"@biomejs/cli-linux-arm64@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-linux-arm64@npm:1.9.4"
   conditions: os=linux & cpu=arm64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-x64-musl@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-linux-x64-musl@npm:1.8.3"
+"@biomejs/cli-linux-x64-musl@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-linux-x64-musl@npm:1.9.4"
   conditions: os=linux & cpu=x64 & libc=musl
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-x64@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-linux-x64@npm:1.8.3"
+"@biomejs/cli-linux-x64@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-linux-x64@npm:1.9.4"
   conditions: os=linux & cpu=x64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@biomejs/cli-win32-arm64@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-win32-arm64@npm:1.8.3"
+"@biomejs/cli-win32-arm64@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-win32-arm64@npm:1.9.4"
   conditions: os=win32 & cpu=arm64
   languageName: node
   linkType: hard
 
-"@biomejs/cli-win32-x64@npm:1.8.3":
-  version: 1.8.3
-  resolution: "@biomejs/cli-win32-x64@npm:1.8.3"
+"@biomejs/cli-win32-x64@npm:1.9.4":
+  version: 1.9.4
+  resolution: "@biomejs/cli-win32-x64@npm:1.9.4"
   conditions: os=win32 & cpu=x64
   languageName: node
   linkType: hard
@@ -1836,32 +1827,56 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@conform-to/dom@npm:1.1.5":
-  version: 1.1.5
-  resolution: "@conform-to/dom@npm:1.1.5"
-  checksum: 10/2b5a7d3aedde7e426e9befbf1ef9bc2103e141e80b3a6bf2bd4ebe51bcdaea6aed61fcb2b84ebd3d094822fd3df6a09c256726b3608edd1b903bb47599b0023f
+"@bkrem/react-transition-group@npm:^1.3.3":
+  version: 1.3.3
+  resolution: "@bkrem/react-transition-group@npm:1.3.3"
+  dependencies:
+    chain-function: "npm:^1.0.0"
+    dom-helpers: "npm:^3.3.1"
+    loose-envify: "npm:^1.3.1"
+    prop-types: "npm:^15.5.6"
+    react-lifecycles-compat: "npm:^3.0.4"
+    warning: "npm:^3.0.0"
+  peerDependencies:
+    react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+    react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+  checksum: 10/0263651a3f36f453a8fb1a9928e96c2b4d9329f02bbc3bcbf367cc27f7dbe3a1777b70e3e751eef7f1e9d3c962eb7e5dc3da0e9785d7f5651b309efdd4758762
   languageName: node
   linkType: hard
 
-"@conform-to/react@npm:1.1.5":
-  version: 1.1.5
-  resolution: "@conform-to/react@npm:1.1.5"
+"@conform-to/dom@npm:1.2.2":
+  version: 1.2.2
+  resolution: "@conform-to/dom@npm:1.2.2"
+  checksum: 10/7276d53750b3cf631b14c14bbb009dec08e2008b9c47e4a7fa51ee938b8cd9ab90c9612d9376b4301c1ababda2a0ece48cb3a58104b4d59c37c622bf8f61f0c8
+  languageName: node
+  linkType: hard
+
+"@conform-to/react@npm:1.2.2":
+  version: 1.2.2
+  resolution: "@conform-to/react@npm:1.2.2"
   dependencies:
-    "@conform-to/dom": "npm:1.1.5"
+    "@conform-to/dom": "npm:1.2.2"
   peerDependencies:
     react: ">=18"
-  checksum: 10/04bcc830e2ea2ed54dc71a05d5b02368305a102496a4a4ad671d964030280f2a4aa731449ef28c51c044061c4155d8a0cb6fed8898bf217b75160cf5b3039ec0
+  checksum: 10/966cceade7fda8019eccc3d0efb0e7525016a17f2fb44767c4a2d0870c47da3af81624ee51dabe88772654e99a7fa373b5345811654acd7c19e39300dfd7cb63
   languageName: node
   linkType: hard
 
-"@conform-to/zod@npm:1.1.5":
-  version: 1.1.5
-  resolution: "@conform-to/zod@npm:1.1.5"
+"@conform-to/zod@npm:1.2.2":
+  version: 1.2.2
+  resolution: "@conform-to/zod@npm:1.2.2"
   dependencies:
-    "@conform-to/dom": "npm:1.1.5"
+    "@conform-to/dom": "npm:1.2.2"
   peerDependencies:
     zod: ^3.21.0
-  checksum: 10/81abe866548445e1239c9905865fb4613a9a2db9d35f21a35d2e06962331d8bd431cea16f3f8ec570fd6c72620b72414cf0c97efee679441f94f29dad2cb8f88
+  checksum: 10/1fbc452aeebe0d3a4b347637f83d871f13cd4b64fe1e8baed2b82f8f51eceee532b78eb369e278be0792671c1d9245a0c7c449056ea74150613bf8907b69c758
+  languageName: node
+  linkType: hard
+
+"@drizzle-team/brocli@npm:^0.10.1":
+  version: 0.10.1
+  resolution: "@drizzle-team/brocli@npm:0.10.1"
+  checksum: 10/4f14ca31b2fe3561cbf8e71e119fb6d7f78123f63b404ee631bbae2fc756eaf3eded222f30e70d92203ec0679b6ae8685183dbff9e9e8547e70fdcf608a66013
   languageName: node
   linkType: hard
 
@@ -2903,18 +2918,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/add@npm:^5.0.2":
-  version: 5.0.2
-  resolution: "@graphql-codegen/add@npm:5.0.2"
-  dependencies:
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/60f32609d9e97ca2c48dc6fc4ff212c5e600e02c1cd583448a4fea01b9d24a9c75e70691ae56aabdd3d4c1e918825c8d813705300ca68047046f3a8cddc0c0c8
-  languageName: node
-  linkType: hard
-
 "@graphql-codegen/add@npm:^5.0.3":
   version: 5.0.3
   resolution: "@graphql-codegen/add@npm:5.0.3"
@@ -2927,14 +2930,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/cli@npm:5.0.2":
-  version: 5.0.2
-  resolution: "@graphql-codegen/cli@npm:5.0.2"
+"@graphql-codegen/cli@npm:5.0.3":
+  version: 5.0.3
+  resolution: "@graphql-codegen/cli@npm:5.0.3"
   dependencies:
     "@babel/generator": "npm:^7.18.13"
     "@babel/template": "npm:^7.18.10"
     "@babel/types": "npm:^7.18.13"
-    "@graphql-codegen/client-preset": "npm:^4.2.2"
+    "@graphql-codegen/client-preset": "npm:^4.4.0"
     "@graphql-codegen/core": "npm:^4.0.2"
     "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
     "@graphql-tools/apollo-engine-loader": "npm:^8.0.0"
@@ -2947,12 +2950,12 @@ __metadata:
     "@graphql-tools/prisma-loader": "npm:^8.0.0"
     "@graphql-tools/url-loader": "npm:^8.0.0"
     "@graphql-tools/utils": "npm:^10.0.0"
-    "@whatwg-node/fetch": "npm:^0.8.0"
+    "@whatwg-node/fetch": "npm:^0.9.20"
     chalk: "npm:^4.1.0"
     cosmiconfig: "npm:^8.1.3"
     debounce: "npm:^1.2.0"
     detect-indent: "npm:^6.0.0"
-    graphql-config: "npm:^5.0.2"
+    graphql-config: "npm:^5.1.1"
     inquirer: "npm:^8.0.0"
     is-glob: "npm:^4.0.1"
     jiti: "npm:^1.17.1"
@@ -2977,53 +2980,30 @@ __metadata:
     graphql-code-generator: cjs/bin.js
     graphql-codegen: cjs/bin.js
     graphql-codegen-esm: esm/bin.js
-  checksum: 10/24f5a4d441e4af2f0cae1818c8643a5400718cc1f08ca829a9110a35d99cb5529b567991ce826544b5a2aab36d0be3b10309dc112343bab1232d7c6f2fa14008
+  checksum: 10/c3359668f824246e78656d26af506b5b279d50e08a56f54db87da492bd4d0a8e8b6540a6119402d7f5026c137babfd79e628897c6038e199ee6322f688eec757
   languageName: node
   linkType: hard
 
-"@graphql-codegen/client-preset@npm:4.3.3":
-  version: 4.3.3
-  resolution: "@graphql-codegen/client-preset@npm:4.3.3"
+"@graphql-codegen/client-preset@npm:4.4.0, @graphql-codegen/client-preset@npm:^4.4.0":
+  version: 4.4.0
+  resolution: "@graphql-codegen/client-preset@npm:4.4.0"
   dependencies:
     "@babel/helper-plugin-utils": "npm:^7.20.2"
     "@babel/template": "npm:^7.20.7"
     "@graphql-codegen/add": "npm:^5.0.3"
-    "@graphql-codegen/gql-tag-operations": "npm:4.0.9"
+    "@graphql-codegen/gql-tag-operations": "npm:4.0.10"
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/typed-document-node": "npm:^5.0.9"
-    "@graphql-codegen/typescript": "npm:^4.0.9"
-    "@graphql-codegen/typescript-operations": "npm:^4.2.3"
-    "@graphql-codegen/visitor-plugin-common": "npm:^5.3.1"
-    "@graphql-tools/documents": "npm:^1.0.0"
-    "@graphql-tools/utils": "npm:^10.0.0"
-    "@graphql-typed-document-node/core": "npm:3.2.0"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/fd58f6d0bad4f687af84015a487e02f01c9cb4a1e310b59ceda48f872885e9dbe69e87271363bf51a805e7970169a121cd13b5df20b124d1d0054530dedbcf81
-  languageName: node
-  linkType: hard
-
-"@graphql-codegen/client-preset@npm:^4.2.2":
-  version: 4.2.2
-  resolution: "@graphql-codegen/client-preset@npm:4.2.2"
-  dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.20.2"
-    "@babel/template": "npm:^7.20.7"
-    "@graphql-codegen/add": "npm:^5.0.2"
-    "@graphql-codegen/gql-tag-operations": "npm:4.0.4"
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    "@graphql-codegen/typed-document-node": "npm:^5.0.4"
-    "@graphql-codegen/typescript": "npm:^4.0.4"
-    "@graphql-codegen/typescript-operations": "npm:^4.1.2"
-    "@graphql-codegen/visitor-plugin-common": "npm:^4.1.2"
+    "@graphql-codegen/typed-document-node": "npm:^5.0.10"
+    "@graphql-codegen/typescript": "npm:^4.1.0"
+    "@graphql-codegen/typescript-operations": "npm:^4.3.0"
+    "@graphql-codegen/visitor-plugin-common": "npm:^5.4.0"
     "@graphql-tools/documents": "npm:^1.0.0"
     "@graphql-tools/utils": "npm:^10.0.0"
     "@graphql-typed-document-node/core": "npm:3.2.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/85b167796b5e4665dc36bc9310880f5493a1b2c6b55985c6f056c97adfb9f3b353d425df348e7a5a027e3999efb5dff7a64c62f9dfb40134bf481248f087f53a
+  checksum: 10/f9cf01d61e26cf44f1a064977b3d5835755e72042d0dfdcd298d43c62ccc74b240d678d13625b11d93f0f825742c44324066f873568879faee0eb5499e7d9aa3
   languageName: node
   linkType: hard
 
@@ -3041,33 +3021,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/gql-tag-operations@npm:4.0.4":
-  version: 4.0.4
-  resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.4"
-  dependencies:
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    "@graphql-codegen/visitor-plugin-common": "npm:4.1.2"
-    "@graphql-tools/utils": "npm:^10.0.0"
-    auto-bind: "npm:~4.0.0"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/d094b6c32568a0d4a3fee3f6c3f3997e28b2f42aaa6c0ba3d68e6fbfc1fce3c1ad68777d85700bbd4e53d5d67a52758db0c950d9ae213c04b318ddc66f99dc27
-  languageName: node
-  linkType: hard
-
-"@graphql-codegen/gql-tag-operations@npm:4.0.9":
-  version: 4.0.9
-  resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.9"
+"@graphql-codegen/gql-tag-operations@npm:4.0.10":
+  version: 4.0.10
+  resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.10"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.4.0"
     "@graphql-tools/utils": "npm:^10.0.0"
     auto-bind: "npm:~4.0.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/90eaecbe0742ee65f0e25df471760664d615bb6396d7e35c2d33615128849c1bdb1c0479c75de589479b7b1a0ee99b34684a2faeeff6245e01ffa7dcfff4b0c6
+  checksum: 10/ff644cf5955a4706f245aea1313e276cc8d4346ad7b11371084977fb65c36b3c84538ef393d1f73b2cffa547ab7da0851b80f86f765de2bed07f228f82465593
   languageName: node
   linkType: hard
 
@@ -3116,119 +3081,54 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/typed-document-node@npm:^5.0.4":
-  version: 5.0.4
-  resolution: "@graphql-codegen/typed-document-node@npm:5.0.4"
-  dependencies:
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    "@graphql-codegen/visitor-plugin-common": "npm:4.1.2"
-    auto-bind: "npm:~4.0.0"
-    change-case-all: "npm:1.0.15"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/9f56c4fc59b1f1db09617fe3714bd42c6243856813fe8c5bfa7712c49e6ce9ddf39d3388a431f0690577048c59491f19d52c9640d8502b9c7f82f98fa08c0f0b
-  languageName: node
-  linkType: hard
-
-"@graphql-codegen/typed-document-node@npm:^5.0.9":
-  version: 5.0.9
-  resolution: "@graphql-codegen/typed-document-node@npm:5.0.9"
+"@graphql-codegen/typed-document-node@npm:^5.0.10":
+  version: 5.0.10
+  resolution: "@graphql-codegen/typed-document-node@npm:5.0.10"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.4.0"
     auto-bind: "npm:~4.0.0"
     change-case-all: "npm:1.0.15"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/0cac39bf2d8304660dc99b46b9c6ed811be79aca8a2cd1f58d9a16ff83fb644e985d872456eed5fdba27fa1727e76c3d3190e251c0171d83aff1af58cbcbc3c8
-  languageName: node
-  linkType: hard
-
-"@graphql-codegen/typescript-operations@npm:^4.1.2":
-  version: 4.1.2
-  resolution: "@graphql-codegen/typescript-operations@npm:4.1.2"
-  dependencies:
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    "@graphql-codegen/typescript": "npm:^4.0.4"
-    "@graphql-codegen/visitor-plugin-common": "npm:4.1.2"
-    auto-bind: "npm:~4.0.0"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/114a9f63d30977a7b55632e2ac18c3b3a095221c61b2976b400b8765c948db43eb5c98041c61b67852e155670da809c0e938411fb206d9212d86fda54f89d8f3
+  checksum: 10/0aa3814359f75273971afb91e67aff30c4128fdd49f5dda1d6496ba061b3c24af0a9825238be6014a275f8fb65c97190611dd65eef126a82f666764d6b4d350a
   languageName: node
   linkType: hard
 
-"@graphql-codegen/typescript-operations@npm:^4.2.3":
-  version: 4.2.3
-  resolution: "@graphql-codegen/typescript-operations@npm:4.2.3"
+"@graphql-codegen/typescript-operations@npm:^4.3.0":
+  version: 4.3.0
+  resolution: "@graphql-codegen/typescript-operations@npm:4.3.0"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/typescript": "npm:^4.0.9"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
+    "@graphql-codegen/typescript": "npm:^4.1.0"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.4.0"
     auto-bind: "npm:~4.0.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/b501b43c5a686847e12812690e0aea3842c6efa10d53703db063d82f2a4f3c469bb02c430a29ac1d1b450ffc82cf97c0ca1b9caa8e8979279f72646f010301bf
-  languageName: node
-  linkType: hard
-
-"@graphql-codegen/typescript@npm:^4.0.4":
-  version: 4.0.4
-  resolution: "@graphql-codegen/typescript@npm:4.0.4"
-  dependencies:
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    "@graphql-codegen/schema-ast": "npm:^4.0.2"
-    "@graphql-codegen/visitor-plugin-common": "npm:4.1.2"
-    auto-bind: "npm:~4.0.0"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/0f451fd6fc13ef486fc80f74d3489d8de45e7c69ee684e9fbae441c2b0d70560f3139db4ff2c179cd10277c5375cb8e24d52234ec11fa19fde1855c21e6e3819
+  checksum: 10/0c178d7bd72284359aab0e523d2033cfa4ee4ebe1c0ff2c98b41a0fd09c5396e7f8a1e51f1f39374e0d29a5445193047f2da383230ae1ac1a6b41fefa53d8fac
   languageName: node
   linkType: hard
 
-"@graphql-codegen/typescript@npm:^4.0.9":
-  version: 4.0.9
-  resolution: "@graphql-codegen/typescript@npm:4.0.9"
+"@graphql-codegen/typescript@npm:^4.1.0":
+  version: 4.1.0
+  resolution: "@graphql-codegen/typescript@npm:4.1.0"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
     "@graphql-codegen/schema-ast": "npm:^4.0.2"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.4.0"
     auto-bind: "npm:~4.0.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/304026adfe622530b8a2827569dd5bbd390177051be8c214fb79873ec64ef21793635c91657703bfd229a3d06f1a8a6f1addd8ae7eab20d1eff2efe6fb044df7
-  languageName: node
-  linkType: hard
-
-"@graphql-codegen/visitor-plugin-common@npm:4.1.2, @graphql-codegen/visitor-plugin-common@npm:^4.1.2":
-  version: 4.1.2
-  resolution: "@graphql-codegen/visitor-plugin-common@npm:4.1.2"
-  dependencies:
-    "@graphql-codegen/plugin-helpers": "npm:^5.0.3"
-    "@graphql-tools/optimize": "npm:^2.0.0"
-    "@graphql-tools/relay-operation-optimizer": "npm:^7.0.0"
-    "@graphql-tools/utils": "npm:^10.0.0"
-    auto-bind: "npm:~4.0.0"
-    change-case-all: "npm:1.0.15"
-    dependency-graph: "npm:^0.11.0"
-    graphql-tag: "npm:^2.11.0"
-    parse-filepath: "npm:^1.0.2"
-    tslib: "npm:~2.6.0"
-  peerDependencies:
-    graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/a852d88b401161f650700f731d63743d191a375a9b77e49c117d0884b0a3d4c109332d00b393711039de5c1586ad2810c30dd46450d4d0d1eac6336c34138bfe
+  checksum: 10/e18bebd494fcfd9f76b1bdb4cbf75ae4de5fc9bc87a675eae0859c5ad3d073c9ae1d8452819a2e404e281602c44dcbb03659725c3949ae6829cc99c138edf96c
   languageName: node
   linkType: hard
 
-"@graphql-codegen/visitor-plugin-common@npm:5.3.1, @graphql-codegen/visitor-plugin-common@npm:^5.3.1":
-  version: 5.3.1
-  resolution: "@graphql-codegen/visitor-plugin-common@npm:5.3.1"
+"@graphql-codegen/visitor-plugin-common@npm:5.4.0, @graphql-codegen/visitor-plugin-common@npm:^5.4.0":
+  version: 5.4.0
+  resolution: "@graphql-codegen/visitor-plugin-common@npm:5.4.0"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
     "@graphql-tools/optimize": "npm:^2.0.0"
@@ -3242,7 +3142,7 @@ __metadata:
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/6dd0464d9099d5aeabeb766515fc8dd2fc84bcae4cb0e3653d7f38aea716d6622d35d7cbb57a1954e6bc1cde10f4dd8c4a75ceb4e8bb8cdbba16219615666a5f
+  checksum: 10/cbfa918b6a5ee5ab4c5edf0af53adaf5b4bbc5e7a0a1355fd0736ec859a9b41f4d5313489d6fa13963dc7d5724ff16f1c00c2f31ffe2b7f201a74a900ad818f9
   languageName: node
   linkType: hard
 
@@ -3624,21 +3524,21 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@hello-pangea/dnd@npm:16.6.0":
-  version: 16.6.0
-  resolution: "@hello-pangea/dnd@npm:16.6.0"
+"@hello-pangea/dnd@npm:17.0.0":
+  version: 17.0.0
+  resolution: "@hello-pangea/dnd@npm:17.0.0"
   dependencies:
-    "@babel/runtime": "npm:^7.24.1"
+    "@babel/runtime": "npm:^7.25.6"
     css-box-model: "npm:^1.2.1"
     memoize-one: "npm:^6.0.0"
     raf-schd: "npm:^4.0.3"
-    react-redux: "npm:^8.1.3"
-    redux: "npm:^4.2.1"
+    react-redux: "npm:^9.1.2"
+    redux: "npm:^5.0.1"
     use-memo-one: "npm:^1.1.3"
   peerDependencies:
-    react: ^16.8.5 || ^17.0.0 || ^18.0.0
-    react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
-  checksum: 10/f377461d400c8223174745e4d7ecf4fb0146f9e807413f98120ebbcf075282e631273988d336daaf1fb8e6b6c6a1a8e4f99beefecd7a6b68ccc3bb064d38f13f
+    react: ^18.0.0
+    react-dom: ^18.0.0
+  checksum: 10/4795063e249a818c60e223f3527797878cb546ef007a52a7dd6c1a01094d3b2107820476a10fc83c0ba9dc4387c1ae49e70c8f8cff9722636219773caad19372
   languageName: node
   linkType: hard
 
@@ -3771,6 +3671,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@kamilkisiela/fast-url-parser@npm:^1.1.4":
+  version: 1.1.4
+  resolution: "@kamilkisiela/fast-url-parser@npm:1.1.4"
+  checksum: 10/5b79438235a81817b02b96ddc581c996961cec5b40c7d6ebabd01ac6af8d4a35a43b9b263144af25386cef92c054c3ef6b1723b09eb0d8cf7b4053781a474c5f
+  languageName: node
+  linkType: hard
+
 "@lukemorales/query-key-factory@npm:1.3.4":
   version: 1.3.4
   resolution: "@lukemorales/query-key-factory@npm:1.3.4"
@@ -3781,35 +3688,35 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@mantine/carousel@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/carousel@npm:7.12.1"
+"@mantine/carousel@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/carousel@npm:7.13.3"
   peerDependencies:
-    "@mantine/core": 7.12.1
-    "@mantine/hooks": 7.12.1
+    "@mantine/core": 7.13.3
+    "@mantine/hooks": 7.13.3
     embla-carousel-react: ">=7.0.0"
     react: ^18.2.0
     react-dom: ^18.2.0
-  checksum: 10/a46c9dc9ad09c3235574a4517fea00c9727a2af2629a47f48cf9f884c8dc7282a4837f41227864b941c63622235bed03da175a5ab80a5ad3ac20f4a2b2635fd4
+  checksum: 10/1fef3f47ee4a35877c39514df8fb8c6bb8a23132d3fb6d2a6939f454eda82d0abb4d0b6cc2c42353695fb49d8dc093b18c1edfbf9e551de7592c61e463dbf1a9
   languageName: node
   linkType: hard
 
-"@mantine/charts@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/charts@npm:7.12.1"
+"@mantine/charts@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/charts@npm:7.13.3"
   peerDependencies:
-    "@mantine/core": 7.12.1
-    "@mantine/hooks": 7.12.1
+    "@mantine/core": 7.13.3
+    "@mantine/hooks": 7.13.3
     react: ^18.2.0
     react-dom: ^18.2.0
     recharts: ^2.10.3
-  checksum: 10/c38b5cb1f3925d452cf02ab6cef946e6a645f0e102a1eafe39072d84461c7713efc11089b094323db5b491475b1cdcd4fb1d821cd0b76b9f5dab26dfe013a42f
+  checksum: 10/55681425074251f45b4892586c60146909eae3f6d86e543dbec1fd771a2bb286f2027baa9f6d5f048da522b162c00dfd41d6809275f69cdb8175886836035916
   languageName: node
   linkType: hard
 
-"@mantine/core@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/core@npm:7.12.1"
+"@mantine/core@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/core@npm:7.13.3"
   dependencies:
     "@floating-ui/react": "npm:^0.26.9"
     clsx: "npm:^2.1.1"
@@ -3818,70 +3725,70 @@ __metadata:
     react-textarea-autosize: "npm:8.5.3"
     type-fest: "npm:^4.12.0"
   peerDependencies:
-    "@mantine/hooks": 7.12.1
+    "@mantine/hooks": 7.13.3
     react: ^18.2.0
     react-dom: ^18.2.0
-  checksum: 10/c90c9496b06dcef4e969659bb4ff281175dda26faa091cf4a684c99c7eedcf59b749f570c361a3ffeb6f85b923087d1cc037bc4944ff34f453ea595eb205ea18
+  checksum: 10/db53ae05c80f7d05f5eae78df30e5398c4a5c3594fdaadb3d6260227be3557b89ce671e8bb5db6e9a5f6c410bdad76199cd833d1406f4b97e7cabe14f552bb30
   languageName: node
   linkType: hard
 
-"@mantine/dates@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/dates@npm:7.12.1"
+"@mantine/dates@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/dates@npm:7.13.3"
   dependencies:
     clsx: "npm:^2.1.1"
   peerDependencies:
-    "@mantine/core": 7.12.1
-    "@mantine/hooks": 7.12.1
+    "@mantine/core": 7.13.3
+    "@mantine/hooks": 7.13.3
     dayjs: ">=1.0.0"
     react: ^18.2.0
     react-dom: ^18.2.0
-  checksum: 10/b019c24dfdd316fbc58223ddbce34236fde8e6c40eacd3ec725082a10cbec094ffec3e9d4e95a71cc7c564e8222178443ae2aacaabfe1d21b4769463b156f159
+  checksum: 10/b7b9057471003908898e89d6be36f1058d138a052b0f4a93ca6fd5c4fc5bd7f04133a7cc1214db367802219bf36495153690674e5dca3d37bc0be934c47c71c1
   languageName: node
   linkType: hard
 
-"@mantine/form@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/form@npm:7.12.1"
+"@mantine/form@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/form@npm:7.13.3"
   dependencies:
     fast-deep-equal: "npm:^3.1.3"
     klona: "npm:^2.0.6"
   peerDependencies:
     react: ^18.2.0
-  checksum: 10/8832579175a8c443c2a92ea9d362e63c27540ad7403a4281241e7a9f378bc0a1f67d70b28f8b0eb21c2bcce56b5b66cf0ea8eb4509f893ac976685c8bb9c4574
+  checksum: 10/34312932855103888ebe9be99c8ddf379691aa9d0ba0cc75e97289c4d20154a9367e228e0e3f5e22fffecec645b57343f5e7906be2ea29e5918561bf12484e77
   languageName: node
   linkType: hard
 
-"@mantine/hooks@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/hooks@npm:7.12.1"
+"@mantine/hooks@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/hooks@npm:7.13.3"
   peerDependencies:
     react: ^18.2.0
-  checksum: 10/ea00e49028f04f30678173a78048e4fd97ae4c8fbc32cf1b422e3f6b5fdc72c388ed5a9a7ae6169906b0c9b6bd662beb53d84ed0d665771f6b1ed1bab0c569c1
+  checksum: 10/720a121f746c009952e0fc606918347f6071a548325358c65bb88bf2071eb6c5cdbebc2c2e4f2ac3324c8116525490c7d169c52ce62ac6d56022edf0e2d4f1ab
   languageName: node
   linkType: hard
 
-"@mantine/notifications@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/notifications@npm:7.12.1"
+"@mantine/notifications@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/notifications@npm:7.13.3"
   dependencies:
-    "@mantine/store": "npm:7.12.1"
+    "@mantine/store": "npm:7.13.3"
     react-transition-group: "npm:4.4.5"
   peerDependencies:
-    "@mantine/core": 7.12.1
-    "@mantine/hooks": 7.12.1
+    "@mantine/core": 7.13.3
+    "@mantine/hooks": 7.13.3
     react: ^18.2.0
     react-dom: ^18.2.0
-  checksum: 10/492e1a90a4bade6add29d817038d7a7195f30ca6c58318d6ffcfa46b29672b7c262257cbc9ff1773fed0a9bf41d73ba8eadee0a70b381784d1f08f6f3e2e126b
+  checksum: 10/b0db5bfdd9999f83e5d003908d6dd1b3bcb92a7b949cb4a3cb5e6fa204391b137bff15e8ab04a6b7d99041d06bce38510138a355256231f2a964691aff352a97
   languageName: node
   linkType: hard
 
-"@mantine/store@npm:7.12.1":
-  version: 7.12.1
-  resolution: "@mantine/store@npm:7.12.1"
+"@mantine/store@npm:7.13.3":
+  version: 7.13.3
+  resolution: "@mantine/store@npm:7.13.3"
   peerDependencies:
     react: ^18.2.0
-  checksum: 10/0aec736d2a9cf37b45534ab353c7eb2c6ab603aade9778ebd81ea63ce0b751c20fdf5eb2a206293a17180daae3afa580bcdf7cc6a029f58c225bf776e69bbc88
+  checksum: 10/72b3f7b3c4ddd15acce5b694b1d34a09260a3f6c0cdf059bcf007f9ecfc71fb0a394de0e204015ee740ba403a68dff6c73150f9286e637763883a605bfd8d694
   languageName: node
   linkType: hard
 
@@ -4111,53 +4018,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@paddle/paddle-js@npm:1.2.0":
-  version: 1.2.0
-  resolution: "@paddle/paddle-js@npm:1.2.0"
-  checksum: 10/87bf72de59f6ef728dcba9581c893608c09b8d897414f8fff4544461db3a54231709e723a79c3090768072169b9981f92fbb51b6ea836142328741593c077192
+"@paddle/paddle-js@npm:1.2.3":
+  version: 1.2.3
+  resolution: "@paddle/paddle-js@npm:1.2.3"
+  checksum: 10/6d4cc3b90305b26b30acdbf2b199a949f2e89dffbd7eba1dbebd5a1c58be533dc476984a49778a9284c3a026abba21b3ab1c1136d400974635641de96450d5d1
   languageName: node
   linkType: hard
 
-"@paddle/paddle-node-sdk@npm:1.4.1":
-  version: 1.4.1
-  resolution: "@paddle/paddle-node-sdk@npm:1.4.1"
+"@paddle/paddle-node-sdk@npm:1.9.1":
+  version: 1.9.1
+  resolution: "@paddle/paddle-node-sdk@npm:1.9.1"
   dependencies:
     lodash: "npm:^4.17.21"
     node-fetch: "npm:^2.7.0"
-  checksum: 10/7b2b4f8a11b3740f87311fba3fbf12498044cfa0631adade896bb11603901d3b1e84b84251a9a1107e21e3a0ba15e352d3791bc5aa967212798e6df1387cca0d
-  languageName: node
-  linkType: hard
-
-"@peculiar/asn1-schema@npm:^2.3.6":
-  version: 2.3.6
-  resolution: "@peculiar/asn1-schema@npm:2.3.6"
-  dependencies:
-    asn1js: "npm:^3.0.5"
-    pvtsutils: "npm:^1.3.2"
-    tslib: "npm:^2.4.0"
-  checksum: 10/09e8292b19cca0952629fb26b897de02fd16d1ef897fa7257de350220223b1e544a398f3487e6e2022495ef7c5d186320b8e65c3f640886409c946bbdc277c96
-  languageName: node
-  linkType: hard
-
-"@peculiar/json-schema@npm:^1.1.12":
-  version: 1.1.12
-  resolution: "@peculiar/json-schema@npm:1.1.12"
-  dependencies:
-    tslib: "npm:^2.0.0"
-  checksum: 10/dfec178afe63a02b6d45da8a18e51ef417e9f5412a8c2809c9a07b29b9376fadee1b4f2ea2d92d4e5a7b8eba76d9e99afbef6d7e9a27bd85257f69c4da228cbc
-  languageName: node
-  linkType: hard
-
-"@peculiar/webcrypto@npm:^1.4.0":
-  version: 1.4.3
-  resolution: "@peculiar/webcrypto@npm:1.4.3"
-  dependencies:
-    "@peculiar/asn1-schema": "npm:^2.3.6"
-    "@peculiar/json-schema": "npm:^1.1.12"
-    pvtsutils: "npm:^1.3.2"
-    tslib: "npm:^2.5.0"
-    webcrypto-core: "npm:^1.7.7"
-  checksum: 10/548f5e32badcfdb02c903ca240daccac5d87ba841e436bd6d30e5455ced22917146130dab21afb718568ea935d6b04dc66fb33a4b6ab652dd868abff81e74a81
+  checksum: 10/6a57f801cc27f7b81a01c5621ac3a1e8533b9ab62ab184ed466d90772fe110f14458f6ea6692a483b2f87dc9e2390550098f12130e81f5e8f12d1b3209a77285
   languageName: node
   linkType: hard
 
@@ -4393,6 +4267,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@radix-ui/react-context@npm:1.1.1":
+  version: 1.1.1
+  resolution: "@radix-ui/react-context@npm:1.1.1"
+  peerDependencies:
+    "@types/react": "*"
+    react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+  checksum: 10/f6469583bf11cc7bff3ea5c95c56b0774a959512adead00dc64b0527cca01b90b476ca39a64edfd7e18e428e17940aa0339116b1ce5b6e8eab513cfd1065d391
+  languageName: node
+  linkType: hard
+
 "@radix-ui/react-direction@npm:1.0.1":
   version: 1.0.1
   resolution: "@radix-ui/react-direction@npm:1.0.1"
@@ -4468,6 +4355,29 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@radix-ui/react-dismissable-layer@npm:1.1.1":
+  version: 1.1.1
+  resolution: "@radix-ui/react-dismissable-layer@npm:1.1.1"
+  dependencies:
+    "@radix-ui/primitive": "npm:1.1.0"
+    "@radix-ui/react-compose-refs": "npm:1.1.0"
+    "@radix-ui/react-primitive": "npm:2.0.0"
+    "@radix-ui/react-use-callback-ref": "npm:1.1.0"
+    "@radix-ui/react-use-escape-keydown": "npm:1.1.0"
+  peerDependencies:
+    "@types/react": "*"
+    "@types/react-dom": "*"
+    react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+    "@types/react-dom":
+      optional: true
+  checksum: 10/4f2346846f15f3482b8b6cd850448838d01520366deef840d8e80f5a3443aa7f21f86bd5b9a26f2709dcf94f1ec3f2bdfe80d5c37cba504c41e487c7ff8af3de
+  languageName: node
+  linkType: hard
+
 "@radix-ui/react-focus-guards@npm:1.0.1":
   version: 1.0.1
   resolution: "@radix-ui/react-focus-guards@npm:1.0.1"
@@ -4719,6 +4629,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@radix-ui/react-portal@npm:1.1.2":
+  version: 1.1.2
+  resolution: "@radix-ui/react-portal@npm:1.1.2"
+  dependencies:
+    "@radix-ui/react-primitive": "npm:2.0.0"
+    "@radix-ui/react-use-layout-effect": "npm:1.1.0"
+  peerDependencies:
+    "@types/react": "*"
+    "@types/react-dom": "*"
+    react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+    "@types/react-dom":
+      optional: true
+  checksum: 10/2f737dc0445f02f512f814ba140227e1a049b3d215d79e22ead412c9befe830292c48a559a8ad1514a474ae8f0c4c43954dfbe294b93a0279d8747d08f7b7924
+  languageName: node
+  linkType: hard
+
 "@radix-ui/react-presence@npm:1.1.0":
   version: 1.1.0
   resolution: "@radix-ui/react-presence@npm:1.1.0"
@@ -4739,6 +4669,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@radix-ui/react-presence@npm:1.1.1":
+  version: 1.1.1
+  resolution: "@radix-ui/react-presence@npm:1.1.1"
+  dependencies:
+    "@radix-ui/react-compose-refs": "npm:1.1.0"
+    "@radix-ui/react-use-layout-effect": "npm:1.1.0"
+  peerDependencies:
+    "@types/react": "*"
+    "@types/react-dom": "*"
+    react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+    "@types/react-dom":
+      optional: true
+  checksum: 10/1ae074efae47ab52a63239a5936fddb334b2f66ed91e74bfe8b1ae591e5db01fa7e9ddb1412002cc043066d40478ba05187a27eb2684dcd68dea545993f9ee20
+  languageName: node
+  linkType: hard
+
 "@radix-ui/react-primitive@npm:1.0.3":
   version: 1.0.3
   resolution: "@radix-ui/react-primitive@npm:1.0.3"
@@ -4952,18 +4902,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@radix-ui/react-tooltip@npm:1.1.2":
-  version: 1.1.2
-  resolution: "@radix-ui/react-tooltip@npm:1.1.2"
+"@radix-ui/react-tooltip@npm:1.1.3":
+  version: 1.1.3
+  resolution: "@radix-ui/react-tooltip@npm:1.1.3"
   dependencies:
     "@radix-ui/primitive": "npm:1.1.0"
     "@radix-ui/react-compose-refs": "npm:1.1.0"
-    "@radix-ui/react-context": "npm:1.1.0"
-    "@radix-ui/react-dismissable-layer": "npm:1.1.0"
+    "@radix-ui/react-context": "npm:1.1.1"
+    "@radix-ui/react-dismissable-layer": "npm:1.1.1"
     "@radix-ui/react-id": "npm:1.1.0"
     "@radix-ui/react-popper": "npm:1.2.0"
-    "@radix-ui/react-portal": "npm:1.1.1"
-    "@radix-ui/react-presence": "npm:1.1.0"
+    "@radix-ui/react-portal": "npm:1.1.2"
+    "@radix-ui/react-presence": "npm:1.1.1"
     "@radix-ui/react-primitive": "npm:2.0.0"
     "@radix-ui/react-slot": "npm:1.1.0"
     "@radix-ui/react-use-controllable-state": "npm:1.1.0"
@@ -4978,7 +4928,7 @@ __metadata:
       optional: true
     "@types/react-dom":
       optional: true
-  checksum: 10/d8386260d2710720b3d87fdacc04597935e5f6eb1d0375516dd362c2f00bf07ea1fceb4c4bda76ec332ebbcf6a4b88f04050add1a9a4db0f13a5ac12b1190355
+  checksum: 10/6a0eeabee30173b3d885c12c666614d673352037c0018a57e4cf220d4da73b6ebefa8610fe26a99d83fc00672a2b7417d8dc194096dd106865de1326d8cd9166
   languageName: node
   linkType: hard
 
@@ -5232,6 +5182,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/body@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/body@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/68c1cd8e5f53476849981c625cabc172eca02b53dc97a883e5d7b21264d4b53e005e085abbec7b1635ad575b570becfa6e6a96adeb9e75a086c23243bc0d7080
+  languageName: node
+  linkType: hard
+
 "@react-email/body@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/body@npm:0.0.9"
@@ -5250,6 +5209,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/button@npm:0.0.17":
+  version: 0.0.17
+  resolution: "@react-email/button@npm:0.0.17"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/9fab2e3d4ba39990816c49d3265ff89598cc804f6e3d1e85afaf5264657d1ead6acf88e0ad0991ece8d685c1722179d1d4a6274168548c0a765cb7b2519896af
+  languageName: node
+  linkType: hard
+
 "@react-email/code-block@npm:0.0.6":
   version: 0.0.6
   resolution: "@react-email/code-block@npm:0.0.6"
@@ -5261,6 +5229,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/code-block@npm:0.0.9":
+  version: 0.0.9
+  resolution: "@react-email/code-block@npm:0.0.9"
+  dependencies:
+    prismjs: "npm:1.29.0"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/77a8b89119b6f88d423d931b2d3ac3faefbbc25290c53219db45021f12691d4deda675929cf5ba4acc7eb8740e11408b048ce3f5c86fe715f551e9a70c1e4998
+  languageName: node
+  linkType: hard
+
 "@react-email/code-inline@npm:0.0.3":
   version: 0.0.3
   resolution: "@react-email/code-inline@npm:0.0.3"
@@ -5270,6 +5249,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/code-inline@npm:0.0.4":
+  version: 0.0.4
+  resolution: "@react-email/code-inline@npm:0.0.4"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/6d379ce85f76e27074b3494ed21ba32047583bf4f9a68c7256c95c7b1b4a67fd69ffc7f94a923a7149e5378b17164bb1cc2ccec82fe0a309f2c62a0a2fd90cb7
+  languageName: node
+  linkType: hard
+
 "@react-email/column@npm:0.0.11":
   version: 0.0.11
   resolution: "@react-email/column@npm:0.0.11"
@@ -5279,6 +5267,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/column@npm:0.0.12":
+  version: 0.0.12
+  resolution: "@react-email/column@npm:0.0.12"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/d6b70e3c7a3ebc405945572a3d3dae8ec9e5c4ab1c70ab58c00c8467a1dcce29c44a513f550f5c70d0d960111d7000c487d75751e6f1f27b6c9c2c08245b787c
+  languageName: node
+  linkType: hard
+
 "@react-email/components@npm:0.0.22":
   version: 0.0.22
   resolution: "@react-email/components@npm:0.0.22"
@@ -5309,6 +5306,36 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/components@npm:0.0.25":
+  version: 0.0.25
+  resolution: "@react-email/components@npm:0.0.25"
+  dependencies:
+    "@react-email/body": "npm:0.0.10"
+    "@react-email/button": "npm:0.0.17"
+    "@react-email/code-block": "npm:0.0.9"
+    "@react-email/code-inline": "npm:0.0.4"
+    "@react-email/column": "npm:0.0.12"
+    "@react-email/container": "npm:0.0.14"
+    "@react-email/font": "npm:0.0.8"
+    "@react-email/head": "npm:0.0.11"
+    "@react-email/heading": "npm:0.0.14"
+    "@react-email/hr": "npm:0.0.10"
+    "@react-email/html": "npm:0.0.10"
+    "@react-email/img": "npm:0.0.10"
+    "@react-email/link": "npm:0.0.10"
+    "@react-email/markdown": "npm:0.0.12"
+    "@react-email/preview": "npm:0.0.11"
+    "@react-email/render": "npm:1.0.1"
+    "@react-email/row": "npm:0.0.10"
+    "@react-email/section": "npm:0.0.14"
+    "@react-email/tailwind": "npm:0.1.0"
+    "@react-email/text": "npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/e4fbefc1883043d375b53ffab7d8ebc93f605ac227203256535701592c819be69c4fbfc637c85ebfacc897b0ae0f2f0183049acdb4674233dd8049e7d9eba09c
+  languageName: node
+  linkType: hard
+
 "@react-email/container@npm:0.0.13":
   version: 0.0.13
   resolution: "@react-email/container@npm:0.0.13"
@@ -5318,6 +5345,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/container@npm:0.0.14":
+  version: 0.0.14
+  resolution: "@react-email/container@npm:0.0.14"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/2dc58f162e41af49a1304216fc3e97a60ef4868670dd2aa0d860a8db2ac38622522ec4364bf9ac7541acb321baaaf76b29f8f7bfa3543c3360a584521eb48c6c
+  languageName: node
+  linkType: hard
+
 "@react-email/font@npm:0.0.7":
   version: 0.0.7
   resolution: "@react-email/font@npm:0.0.7"
@@ -5327,6 +5363,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/font@npm:0.0.8":
+  version: 0.0.8
+  resolution: "@react-email/font@npm:0.0.8"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/ce93b03c29d721f6a93f63fdfa29590c96785b8e9689bdb01e70ee482ff43a4834096092b7f888ee750fb57fe8fed4f9c990dd5c63d22af12ee592ca8f1fb2db
+  languageName: node
+  linkType: hard
+
 "@react-email/head@npm:0.0.10":
   version: 0.0.10
   resolution: "@react-email/head@npm:0.0.10"
@@ -5336,8 +5381,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@react-email/heading@npm:0.0.13":
-  version: 0.0.13
+"@react-email/head@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/head@npm:0.0.11"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/2531af0b6dd0d3c258c6f287015ba008ac24bc219f2b25b0c8e8793d50ce67014405ef238eda41b3589fc10329e7337d2dcda881f3e097919cc6cbb13ea98378
+  languageName: node
+  linkType: hard
+
+"@react-email/heading@npm:0.0.13":
+  version: 0.0.13
   resolution: "@react-email/heading@npm:0.0.13"
   dependencies:
     "@radix-ui/react-slot": "npm:1.1.0"
@@ -5347,6 +5401,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/heading@npm:0.0.14":
+  version: 0.0.14
+  resolution: "@react-email/heading@npm:0.0.14"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/57214a826655f288ba98ab580ad51981f2d708040238290301c7dc9f353f56d8f84b0c4cdb6fd4d50908c83275e9abdf52eeb61bb47772a32791209b0844c2e7
+  languageName: node
+  linkType: hard
+
+"@react-email/hr@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/hr@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/a460e56a3e9aea23c04c087f6e7c35481ad778077f468c3572f44e5631b4efb20404655e96b20978ece8f170315382d82a33ed01e38758345159b16fee39f12b
+  languageName: node
+  linkType: hard
+
 "@react-email/hr@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/hr@npm:0.0.9"
@@ -5356,6 +5428,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/html@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/html@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/e292bff395f0887625d8b9f708767666917cb3a4d4b2b5dcebd947437c53e25fd993e77c6ea5d61f8f751b3688f60972c7446060b610b0ace2d9230670df029e
+  languageName: node
+  linkType: hard
+
 "@react-email/html@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/html@npm:0.0.9"
@@ -5365,6 +5446,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/img@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/img@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/be07ad4107903b34b52c8e699c24e5991c7890be50cea294a2a5d89a92209427fdf03c0ab6407254eb872269a816370d0e0c214a02a7a4916ea394eefb8a5bcb
+  languageName: node
+  linkType: hard
+
 "@react-email/img@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/img@npm:0.0.9"
@@ -5374,6 +5464,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/link@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/link@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/153a0013622c8912418267f32c28a6194ea4ecdd12251057a5e221a45faccb11fdfcfef54949f84841fbcbe1e79e3ad7f45d8695172b5e0d250a7bd148c35e06
+  languageName: node
+  linkType: hard
+
 "@react-email/link@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/link@npm:0.0.9"
@@ -5394,6 +5493,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/markdown@npm:0.0.12":
+  version: 0.0.12
+  resolution: "@react-email/markdown@npm:0.0.12"
+  dependencies:
+    md-to-react-email: "npm:5.0.2"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/808241dd363af5aca30c8df28c3aa1d8e20056d1d264eb0726b5a1339c87b7e990cd19bb0c9c7935e291cc456f0df1c9b80f9a264507139d212392ff64fa1298
+  languageName: node
+  linkType: hard
+
 "@react-email/preview@npm:0.0.10":
   version: 0.0.10
   resolution: "@react-email/preview@npm:0.0.10"
@@ -5403,6 +5513,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/preview@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/preview@npm:0.0.11"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/661b1a5d2f68a224bcd58f2cab46141c14e964f1fced470a47cfa795cabe383568ac6c6ad3fc1b3f5191098ea236bd0559c541309e67c6b2e86af00c7a9ac565
+  languageName: node
+  linkType: hard
+
 "@react-email/render@npm:0.0.17":
   version: 0.0.17
   resolution: "@react-email/render@npm:0.0.17"
@@ -5417,6 +5536,29 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/render@npm:1.0.1":
+  version: 1.0.1
+  resolution: "@react-email/render@npm:1.0.1"
+  dependencies:
+    html-to-text: "npm:9.0.5"
+    js-beautify: "npm:^1.14.11"
+    react-promise-suspense: "npm:0.3.4"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+    react-dom: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/0ddc28a8a523802c34f4fe0e69de93c0d495ba05357a7061bee1ea34e29a4c17a4bba82569568b920006d17a2e67689b9b1cb41b106e684b826ed7017d49b4d2
+  languageName: node
+  linkType: hard
+
+"@react-email/row@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/row@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/23288ca36b992e30ff1bc34320da3bbf40782e84b4201f5e4c3dba6176f29c3225b7f0062122a5577bb52eacfadc5ca9fd18ee465b1ab67400c1277300dff111
+  languageName: node
+  linkType: hard
+
 "@react-email/row@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/row@npm:0.0.9"
@@ -5435,6 +5577,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/section@npm:0.0.14":
+  version: 0.0.14
+  resolution: "@react-email/section@npm:0.0.14"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/c18a1d47a4dadb49fd48ac004127f40247a0d28b523aea1e610c3d73439c2c00bd582b3c56aa6fec292acba97a431beea29292766950e58cbf0d13cb32d4bb2a
+  languageName: node
+  linkType: hard
+
 "@react-email/tailwind@npm:0.0.19":
   version: 0.0.19
   resolution: "@react-email/tailwind@npm:0.0.19"
@@ -5444,6 +5595,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-email/tailwind@npm:0.1.0":
+  version: 0.1.0
+  resolution: "@react-email/tailwind@npm:0.1.0"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/3e002b23e0c72d6e27b8be9c640e7b7392aec651b6bc50a8a92064d398e66615b1a1647f4641302f7e1949a7d7878c88c8f3b4b98f295b0882875b4a097f2122
+  languageName: node
+  linkType: hard
+
+"@react-email/text@npm:0.0.10":
+  version: 0.0.10
+  resolution: "@react-email/text@npm:0.0.10"
+  peerDependencies:
+    react: ^18.0 || ^19.0 || ^19.0.0-rc
+  checksum: 10/b4deb47df62b4c8ecb230debc478331cac650c684b819a11ef930fd4b9700c5a9d408bf4cca27d07638891bafd22c7c35730d434221f0d2d89dde1b01a5d2ad6
+  languageName: node
+  linkType: hard
+
 "@react-email/text@npm:0.0.9":
   version: 0.0.9
   resolution: "@react-email/text@npm:0.0.9"
@@ -5509,87 +5678,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@remix-run/dev@npm:2.10.3":
-  version: 2.10.3
-  resolution: "@remix-run/dev@npm:2.10.3"
-  dependencies:
-    "@babel/core": "npm:^7.21.8"
-    "@babel/generator": "npm:^7.21.5"
-    "@babel/parser": "npm:^7.21.8"
-    "@babel/plugin-syntax-decorators": "npm:^7.22.10"
-    "@babel/plugin-syntax-jsx": "npm:^7.21.4"
-    "@babel/preset-typescript": "npm:^7.21.5"
-    "@babel/traverse": "npm:^7.23.2"
-    "@babel/types": "npm:^7.22.5"
-    "@mdx-js/mdx": "npm:^2.3.0"
-    "@npmcli/package-json": "npm:^4.0.1"
-    "@remix-run/node": "npm:2.10.3"
-    "@remix-run/router": "npm:1.18.0"
-    "@remix-run/server-runtime": "npm:2.10.3"
-    "@types/mdx": "npm:^2.0.5"
-    "@vanilla-extract/integration": "npm:^6.2.0"
-    arg: "npm:^5.0.1"
-    cacache: "npm:^17.1.3"
-    chalk: "npm:^4.1.2"
-    chokidar: "npm:^3.5.1"
-    cross-spawn: "npm:^7.0.3"
-    dotenv: "npm:^16.0.0"
-    es-module-lexer: "npm:^1.3.1"
-    esbuild: "npm:0.17.6"
-    esbuild-plugins-node-modules-polyfill: "npm:^1.6.0"
-    execa: "npm:5.1.1"
-    exit-hook: "npm:2.2.1"
-    express: "npm:^4.19.2"
-    fs-extra: "npm:^10.0.0"
-    get-port: "npm:^5.1.1"
-    gunzip-maybe: "npm:^1.4.2"
-    jsesc: "npm:3.0.2"
-    json5: "npm:^2.2.2"
-    lodash: "npm:^4.17.21"
-    lodash.debounce: "npm:^4.0.8"
-    minimatch: "npm:^9.0.0"
-    ora: "npm:^5.4.1"
-    picocolors: "npm:^1.0.0"
-    picomatch: "npm:^2.3.1"
-    pidtree: "npm:^0.6.0"
-    postcss: "npm:^8.4.19"
-    postcss-discard-duplicates: "npm:^5.1.0"
-    postcss-load-config: "npm:^4.0.1"
-    postcss-modules: "npm:^6.0.0"
-    prettier: "npm:^2.7.1"
-    pretty-ms: "npm:^7.0.1"
-    react-refresh: "npm:^0.14.0"
-    remark-frontmatter: "npm:4.0.1"
-    remark-mdx-frontmatter: "npm:^1.0.1"
-    semver: "npm:^7.3.7"
-    set-cookie-parser: "npm:^2.6.0"
-    tar-fs: "npm:^2.1.1"
-    tsconfig-paths: "npm:^4.0.0"
-    ws: "npm:^7.4.5"
-  peerDependencies:
-    "@remix-run/react": ^2.10.3
-    "@remix-run/serve": ^2.10.3
-    typescript: ^5.1.0
-    vite: ^5.1.0
-    wrangler: ^3.28.2
-  peerDependenciesMeta:
-    "@remix-run/serve":
-      optional: true
-    typescript:
-      optional: true
-    vite:
-      optional: true
-    wrangler:
-      optional: true
-  bin:
-    remix: dist/cli.js
-  checksum: 10/44e5429ff2efd35a882d4050b4485b4b28b9bd2a74b394b7d2442ba2df164e2b2539af3db4501e628bba2888c08d39324bed600f5117e5d3184650faaf9a53a1
-  languageName: node
-  linkType: hard
-
-"@remix-run/dev@npm:2.11.2":
-  version: 2.11.2
-  resolution: "@remix-run/dev@npm:2.11.2"
+"@remix-run/dev@npm:2.13.1":
+  version: 2.13.1
+  resolution: "@remix-run/dev@npm:2.13.1"
   dependencies:
     "@babel/core": "npm:^7.21.8"
     "@babel/generator": "npm:^7.21.5"
@@ -5601,9 +5692,9 @@ __metadata:
     "@babel/types": "npm:^7.22.5"
     "@mdx-js/mdx": "npm:^2.3.0"
     "@npmcli/package-json": "npm:^4.0.1"
-    "@remix-run/node": "npm:2.11.2"
-    "@remix-run/router": "npm:1.19.1"
-    "@remix-run/server-runtime": "npm:2.11.2"
+    "@remix-run/node": "npm:2.13.1"
+    "@remix-run/router": "npm:1.20.0"
+    "@remix-run/server-runtime": "npm:2.13.1"
     "@types/mdx": "npm:^2.0.5"
     "@vanilla-extract/integration": "npm:^6.2.0"
     arg: "npm:^5.0.1"
@@ -5617,7 +5708,7 @@ __metadata:
     esbuild-plugins-node-modules-polyfill: "npm:^1.6.0"
     execa: "npm:5.1.1"
     exit-hook: "npm:2.2.1"
-    express: "npm:^4.19.2"
+    express: "npm:^4.20.0"
     fs-extra: "npm:^10.0.0"
     get-port: "npm:^5.1.1"
     gunzip-maybe: "npm:^1.4.2"
@@ -5643,10 +5734,10 @@ __metadata:
     set-cookie-parser: "npm:^2.6.0"
     tar-fs: "npm:^2.1.1"
     tsconfig-paths: "npm:^4.0.0"
-    ws: "npm:^7.4.5"
+    ws: "npm:^7.5.10"
   peerDependencies:
-    "@remix-run/react": ^2.11.2
-    "@remix-run/serve": ^2.11.2
+    "@remix-run/react": ^2.13.1
+    "@remix-run/serve": ^2.13.1
     typescript: ^5.1.0
     vite: ^5.1.0
     wrangler: ^3.28.2
@@ -5661,65 +5752,30 @@ __metadata:
       optional: true
   bin:
     remix: dist/cli.js
-  checksum: 10/e89b2fc46c730b17c8140eeb5b8251dd1b86f63d6518e764b236959e2c43d9b7e1b752e6a87c26db089f2820812f7b0623048519c06b872d36ec3b53b797881b
-  languageName: node
-  linkType: hard
-
-"@remix-run/express@npm:2.10.3":
-  version: 2.10.3
-  resolution: "@remix-run/express@npm:2.10.3"
-  dependencies:
-    "@remix-run/node": "npm:2.10.3"
-  peerDependencies:
-    express: ^4.19.2
-    typescript: ^5.1.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10/b635fffa1beee32cad4603427766962728106a9daca081caf8538f1831c670a83bbfdfe91b314655c762e88d6ab6987f8d9e544dbe2189922f41b235aa4828c7
-  languageName: node
-  linkType: hard
-
-"@remix-run/express@npm:2.11.2":
-  version: 2.11.2
-  resolution: "@remix-run/express@npm:2.11.2"
-  dependencies:
-    "@remix-run/node": "npm:2.11.2"
-  peerDependencies:
-    express: ^4.19.2
-    typescript: ^5.1.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10/97e49bffa3c83858fd6077c0b64a61659279f806346d45a389f2bdeed074b6b57fef0735c165296d8c76bab940259d1b83a35c7bec9fbf19e5fe3b02cdf66239
+  checksum: 10/73f3420fa753ca5baacec70f14f0dad4465415e918a5d3d7925d0a01bf8fcb87212add61ec7528eea6fc9eb4e62d7e992d7b4d5dfc4887fae8ef1850ead5eb15
   languageName: node
   linkType: hard
 
-"@remix-run/node@npm:2.10.3":
-  version: 2.10.3
-  resolution: "@remix-run/node@npm:2.10.3"
+"@remix-run/express@npm:2.13.1":
+  version: 2.13.1
+  resolution: "@remix-run/express@npm:2.13.1"
   dependencies:
-    "@remix-run/server-runtime": "npm:2.10.3"
-    "@remix-run/web-fetch": "npm:^4.4.2"
-    "@web3-storage/multipart-parser": "npm:^1.0.0"
-    cookie-signature: "npm:^1.1.0"
-    source-map-support: "npm:^0.5.21"
-    stream-slice: "npm:^0.1.2"
-    undici: "npm:^6.11.1"
+    "@remix-run/node": "npm:2.13.1"
   peerDependencies:
+    express: ^4.20.0
     typescript: ^5.1.0
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/70fa59c6ee56f64f89c93a844a18b8781386262d2f49edcd34c3e14f2ec6aeae90070fa8f35213133267e73ec91719efc35be38310b97ae0a0e5c738ac4718f7
+  checksum: 10/3f7f1e193ac52183ef6183eabbac76f9a6e5659f1ee70bb8cf284ffe1c1dd6409bd9deee71f6bbda136b407a5a34ec08de0fe0a286c368fa6aba3049de559b06
   languageName: node
   linkType: hard
 
-"@remix-run/node@npm:2.11.2":
-  version: 2.11.2
-  resolution: "@remix-run/node@npm:2.11.2"
+"@remix-run/node@npm:2.13.1":
+  version: 2.13.1
+  resolution: "@remix-run/node@npm:2.13.1"
   dependencies:
-    "@remix-run/server-runtime": "npm:2.11.2"
+    "@remix-run/server-runtime": "npm:2.13.1"
     "@remix-run/web-fetch": "npm:^4.4.2"
     "@web3-storage/multipart-parser": "npm:^1.0.0"
     cookie-signature: "npm:^1.1.0"
@@ -5731,39 +5787,19 @@ __metadata:
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/ebbb31bc22b6b44f8ff538e2e53f8527c7bbe0486e258dcb4271f5cb2dd13eae30008d415d969bed72713b1d50ed06b0399fc9f32aa7008b2272a04e431b28c5
-  languageName: node
-  linkType: hard
-
-"@remix-run/react@npm:2.10.3":
-  version: 2.10.3
-  resolution: "@remix-run/react@npm:2.10.3"
-  dependencies:
-    "@remix-run/router": "npm:1.18.0"
-    "@remix-run/server-runtime": "npm:2.10.3"
-    react-router: "npm:6.25.0"
-    react-router-dom: "npm:6.25.0"
-    turbo-stream: "npm:2.2.0"
-  peerDependencies:
-    react: ^18.0.0
-    react-dom: ^18.0.0
-    typescript: ^5.1.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10/0766017c3b9497f8d6ee51ab643a9fe85cc65dd50e6afa8c4601bfa4d72fc4dc5ca42711e46aff9f069ca002bd1a583a86d1278007d70144c16552568f5e7394
+  checksum: 10/f1abec9a776cb71f22e358e54db582e16feb0470b93a4b9dae27f0ae85821f4c7d4e9268c1bb881b0deec078498408a93b3c56e27fe3a26f095ec59f229bf5b8
   languageName: node
   linkType: hard
 
-"@remix-run/react@npm:2.11.2":
-  version: 2.11.2
-  resolution: "@remix-run/react@npm:2.11.2"
+"@remix-run/react@npm:2.13.1":
+  version: 2.13.1
+  resolution: "@remix-run/react@npm:2.13.1"
   dependencies:
-    "@remix-run/router": "npm:1.19.1"
-    "@remix-run/server-runtime": "npm:2.11.2"
-    react-router: "npm:6.26.1"
-    react-router-dom: "npm:6.26.1"
-    turbo-stream: "npm:2.3.0"
+    "@remix-run/router": "npm:1.20.0"
+    "@remix-run/server-runtime": "npm:2.13.1"
+    react-router: "npm:6.27.0"
+    react-router-dom: "npm:6.27.0"
+    turbo-stream: "npm:2.4.0"
   peerDependencies:
     react: ^18.0.0
     react-dom: ^18.0.0
@@ -5771,21 +5807,7 @@ __metadata:
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/d8abc2daa1578798b4cf193ace042013310097720f3017b04d4a9693fcd2d87c85db0c438673b4a238efabbf6f0ad87e83341da340783949340c2f9bfd4b6c8c
-  languageName: node
-  linkType: hard
-
-"@remix-run/router@npm:1.18.0":
-  version: 1.18.0
-  resolution: "@remix-run/router@npm:1.18.0"
-  checksum: 10/f878cf246b94368f431a51363f1d33dc35ad11cb910d930476d988825b024a152de87a7f74f0891c3e7182228f892c7f64f94409aae27084c320338dee82caa1
-  languageName: node
-  linkType: hard
-
-"@remix-run/router@npm:1.19.1":
-  version: 1.19.1
-  resolution: "@remix-run/router@npm:1.19.1"
-  checksum: 10/2800c2f6567a982fe942aacc4cb5b170e7cc89bd455960e3bea2424161ff7dac32d01886322d88dd19b88d1bea711f39566d17f02b73eeb74999affb471f8f52
+  checksum: 10/74d7463940114d5a7f1aa30250680b46ab0a1e55c9b3ec8e1281e6e73309873bf5a8479932fdc9b592383556e052ff2c1b8e6b45f4197e50dd87c77689e49c07
   languageName: node
   linkType: hard
 
@@ -5796,79 +5818,48 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@remix-run/serve@npm:2.10.3":
-  version: 2.10.3
-  resolution: "@remix-run/serve@npm:2.10.3"
-  dependencies:
-    "@remix-run/express": "npm:2.10.3"
-    "@remix-run/node": "npm:2.10.3"
-    chokidar: "npm:^3.5.3"
-    compression: "npm:^1.7.4"
-    express: "npm:^4.19.2"
-    get-port: "npm:5.1.1"
-    morgan: "npm:^1.10.0"
-    source-map-support: "npm:^0.5.21"
-  bin:
-    remix-serve: dist/cli.js
-  checksum: 10/05c179518f4c1fe853077d50759e9441434bab824e7b63a010d236bb411eba2f06aa50430938c91d2ab2270828b29023333e3b6083b038c08df44c334d3b51cf
+"@remix-run/router@npm:1.20.0":
+  version: 1.20.0
+  resolution: "@remix-run/router@npm:1.20.0"
+  checksum: 10/e1d2420db94a1855b97f1784898d0ae389cf3b77129b8f419e51d4833b77ca2c92ac09e2cb558015324d64580a138fd6faa31e52fcc3ba90e3cc382a1a324d4a
   languageName: node
   linkType: hard
 
-"@remix-run/serve@npm:2.11.2":
-  version: 2.11.2
-  resolution: "@remix-run/serve@npm:2.11.2"
+"@remix-run/serve@npm:2.13.1":
+  version: 2.13.1
+  resolution: "@remix-run/serve@npm:2.13.1"
   dependencies:
-    "@remix-run/express": "npm:2.11.2"
-    "@remix-run/node": "npm:2.11.2"
+    "@remix-run/express": "npm:2.13.1"
+    "@remix-run/node": "npm:2.13.1"
     chokidar: "npm:^3.5.3"
     compression: "npm:^1.7.4"
-    express: "npm:^4.19.2"
+    express: "npm:^4.20.0"
     get-port: "npm:5.1.1"
     morgan: "npm:^1.10.0"
     source-map-support: "npm:^0.5.21"
   bin:
     remix-serve: dist/cli.js
-  checksum: 10/c6a1d4682c4981456ecdcd1e47b28a034820c46526c1ee2bd728d9211cbd5c36d8134a02e30f48008e12ac0447c81b3d386ab2b117a488fa110194e6f387de95
+  checksum: 10/2b60e456073f35a398273f48c16da9764221e31d6430c69081f85f7d0c615ad96a3a0938f544512ad7f17399ec73d379a2d614c82cf571011dc2115c81f23dd6
   languageName: node
   linkType: hard
 
-"@remix-run/server-runtime@npm:2.10.3":
-  version: 2.10.3
-  resolution: "@remix-run/server-runtime@npm:2.10.3"
-  dependencies:
-    "@remix-run/router": "npm:1.18.0"
-    "@types/cookie": "npm:^0.6.0"
-    "@web3-storage/multipart-parser": "npm:^1.0.0"
-    cookie: "npm:^0.6.0"
-    set-cookie-parser: "npm:^2.4.8"
-    source-map: "npm:^0.7.3"
-    turbo-stream: "npm:2.2.0"
-  peerDependencies:
-    typescript: ^5.1.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10/7abcc23cb0585c591e2ed2e19fa87c18a0a572ba8cf1103268c4209beda047b574c22f4a76ec55c90af8522af1c7037b1588e33ba0538f3f136f547e399b6aa7
-  languageName: node
-  linkType: hard
-
-"@remix-run/server-runtime@npm:2.11.2":
-  version: 2.11.2
-  resolution: "@remix-run/server-runtime@npm:2.11.2"
+"@remix-run/server-runtime@npm:2.13.1":
+  version: 2.13.1
+  resolution: "@remix-run/server-runtime@npm:2.13.1"
   dependencies:
-    "@remix-run/router": "npm:1.19.1"
+    "@remix-run/router": "npm:1.20.0"
     "@types/cookie": "npm:^0.6.0"
     "@web3-storage/multipart-parser": "npm:^1.0.0"
     cookie: "npm:^0.6.0"
     set-cookie-parser: "npm:^2.4.8"
     source-map: "npm:^0.7.3"
-    turbo-stream: "npm:2.3.0"
+    turbo-stream: "npm:2.4.0"
   peerDependencies:
     typescript: ^5.1.0
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/50399f76369ff6f5e3eaa5e4ce5fc6fff2187212caacc5d60ff8af475a2c60815017e66a68c40d0e1f0bde42a122d90021ec1e415e6a1759e1ee0f425a4f6826
+  checksum: 10/f38fb874e0aae963f4106435d26f57142be060e33ec5d023c45909eb7885fb17dec30d8305ab3d7da057f4a7c67c084a0244f88f0b36d7c5d8afecf2ef389dd1
   languageName: node
   linkType: hard
 
@@ -6005,13 +5996,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-android-arm-eabi@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-android-arm-eabi@npm:4.19.0"
-  conditions: os=android & cpu=arm
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-android-arm-eabi@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-android-arm-eabi@npm:4.21.0"
@@ -6019,13 +6003,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-android-arm64@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-android-arm64@npm:4.19.0"
-  conditions: os=android & cpu=arm64
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-android-arm64@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-android-arm64@npm:4.21.0"
@@ -6033,13 +6010,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-darwin-arm64@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-darwin-arm64@npm:4.19.0"
-  conditions: os=darwin & cpu=arm64
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-darwin-arm64@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-darwin-arm64@npm:4.21.0"
@@ -6047,13 +6017,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-darwin-x64@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-darwin-x64@npm:4.19.0"
-  conditions: os=darwin & cpu=x64
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-darwin-x64@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-darwin-x64@npm:4.21.0"
@@ -6061,13 +6024,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-arm-gnueabihf@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.19.0"
-  conditions: os=linux & cpu=arm & libc=glibc
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.0"
@@ -6075,13 +6031,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-arm-musleabihf@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.19.0"
-  conditions: os=linux & cpu=arm & libc=musl
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-arm-musleabihf@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.21.0"
@@ -6089,13 +6038,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-arm64-gnu@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.19.0"
-  conditions: os=linux & cpu=arm64 & libc=glibc
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-arm64-gnu@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.21.0"
@@ -6103,13 +6045,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-arm64-musl@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-arm64-musl@npm:4.19.0"
-  conditions: os=linux & cpu=arm64 & libc=musl
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-arm64-musl@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-arm64-musl@npm:4.21.0"
@@ -6117,13 +6052,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.0"
-  conditions: os=linux & cpu=ppc64 & libc=glibc
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.0"
@@ -6131,13 +6059,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-riscv64-gnu@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.19.0"
-  conditions: os=linux & cpu=riscv64 & libc=glibc
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-riscv64-gnu@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.21.0"
@@ -6145,24 +6066,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-s390x-gnu@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.19.0"
-  conditions: os=linux & cpu=s390x & libc=glibc
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-s390x-gnu@npm:4.21.0":
-  version: 4.21.0
-  resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.0"
-  conditions: os=linux & cpu=s390x & libc=glibc
-  languageName: node
-  linkType: hard
-
-"@rollup/rollup-linux-x64-gnu@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-x64-gnu@npm:4.19.0"
-  conditions: os=linux & cpu=x64 & libc=glibc
+  version: 4.21.0
+  resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.0"
+  conditions: os=linux & cpu=s390x & libc=glibc
   languageName: node
   linkType: hard
 
@@ -6173,13 +6080,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-linux-x64-musl@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-linux-x64-musl@npm:4.19.0"
-  conditions: os=linux & cpu=x64 & libc=musl
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-linux-x64-musl@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-linux-x64-musl@npm:4.21.0"
@@ -6187,13 +6087,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-win32-arm64-msvc@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.19.0"
-  conditions: os=win32 & cpu=arm64
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-win32-arm64-msvc@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.21.0"
@@ -6201,13 +6094,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-win32-ia32-msvc@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.19.0"
-  conditions: os=win32 & cpu=ia32
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-win32-ia32-msvc@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.21.0"
@@ -6215,13 +6101,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rollup/rollup-win32-x64-msvc@npm:4.19.0":
-  version: 4.19.0
-  resolution: "@rollup/rollup-win32-x64-msvc@npm:4.19.0"
-  conditions: os=win32 & cpu=x64
-  languageName: node
-  linkType: hard
-
 "@rollup/rollup-win32-x64-msvc@npm:4.21.0":
   version: 4.21.0
   resolution: "@rollup/rollup-win32-x64-msvc@npm:4.21.0"
@@ -6234,77 +6113,77 @@ __metadata:
   resolution: "@ryot/frontend@workspace:apps/frontend"
   dependencies:
     "@bitprojects/umami-logger-typescript": "npm:1.0.10"
-    "@conform-to/react": "npm:1.1.5"
-    "@conform-to/zod": "npm:1.1.5"
+    "@conform-to/react": "npm:1.2.2"
+    "@conform-to/zod": "npm:1.2.2"
     "@formkit/auto-animate": "npm:0.8.2"
-    "@hello-pangea/dnd": "npm:16.6.0"
+    "@hello-pangea/dnd": "npm:17.0.0"
     "@lukemorales/query-key-factory": "npm:1.3.4"
-    "@mantine/carousel": "npm:7.12.1"
-    "@mantine/charts": "npm:7.12.1"
-    "@mantine/core": "npm:7.12.1"
-    "@mantine/dates": "npm:7.12.1"
-    "@mantine/form": "npm:7.12.1"
-    "@mantine/hooks": "npm:7.12.1"
-    "@mantine/notifications": "npm:7.12.1"
+    "@mantine/carousel": "npm:7.13.3"
+    "@mantine/charts": "npm:7.13.3"
+    "@mantine/core": "npm:7.13.3"
+    "@mantine/dates": "npm:7.13.3"
+    "@mantine/form": "npm:7.13.3"
+    "@mantine/hooks": "npm:7.13.3"
+    "@mantine/notifications": "npm:7.13.3"
     "@remix-pwa/dev": "npm:3.1.0"
     "@remix-pwa/sw": "npm:3.0.10"
     "@remix-pwa/worker-runtime": "npm:2.1.4"
-    "@remix-run/dev": "npm:2.11.2"
-    "@remix-run/node": "npm:2.11.2"
-    "@remix-run/react": "npm:2.11.2"
-    "@remix-run/serve": "npm:2.11.2"
+    "@remix-run/dev": "npm:2.13.1"
+    "@remix-run/node": "npm:2.13.1"
+    "@remix-run/react": "npm:2.13.1"
+    "@remix-run/serve": "npm:2.13.1"
     "@ryot/generated": "workspace:*"
     "@ryot/graphql": "workspace:*"
     "@ryot/ts-utils": "workspace:*"
-    "@tabler/icons-react": "npm:3.12.0"
-    "@tanstack/react-query": "npm:5.52.0"
-    "@tanstack/react-query-devtools": "npm:5.52.0"
+    "@tabler/icons-react": "npm:3.19.0"
+    "@tanstack/react-query": "npm:5.59.15"
+    "@tanstack/react-query-devtools": "npm:5.59.15"
     "@types/cookie": "npm:0.6.0"
     "@types/crypto-js": "npm:4.2.2"
-    "@types/howler": "npm:2.2.11"
+    "@types/howler": "npm:2.2.12"
     "@types/js-cookie": "npm:3.0.6"
-    "@types/react": "npm:18.3.4"
-    "@types/react-dom": "npm:18.3.0"
+    "@types/react": "npm:18.3.11"
+    "@types/react-dom": "npm:18.3.1"
     "@types/uuid": "npm:10.0.0"
     buffer: "npm:6.0.3"
     clsx: "npm:2.1.1"
-    cookie: "npm:0.6.0"
+    cookie: "npm:1.0.1"
     dayjs: "npm:1.11.13"
     dotenv: "npm:16.4.5"
     embla-carousel-react: "npm:7.1.0"
-    filesize: "npm:10.1.4"
+    filesize: "npm:10.1.6"
     graphql: "npm:16.9.0"
     graphql-request: "npm:7.1.0"
     howler: "npm:2.2.4"
     humanize-duration-ts: "npm:2.1.1"
     immer: "npm:10.1.1"
     isbot: "npm:5.1.17"
-    jotai: "npm:2.9.3"
+    jotai: "npm:2.10.1"
     js-cookie: "npm:3.0.5"
     jwt-decode: "npm:4.0.0"
-    mantine-datatable: "npm:7.11.3"
-    postcss: "npm:8.4.41"
+    mantine-datatable: "npm:7.12.4"
+    postcss: "npm:8.4.47"
     postcss-preset-mantine: "npm:1.17.0"
     postcss-simple-vars: "npm:7.0.1"
     react: "npm:18.3.1"
     react-confirm: "npm:0.3.0-7"
     react-dom: "npm:18.3.1"
-    react-virtuoso: "npm:4.10.1"
+    react-virtuoso: "npm:4.12.0"
     react-webcam: "npm:7.2.0"
-    recharts: "npm:2.12.7"
-    remix-development-tools: "npm:4.4.1"
+    recharts: "npm:2.13.0"
+    remix-development-tools: "npm:4.7.3"
     remix-routes: "npm:1.7.7"
-    remix-utils: "npm:7.6.0"
+    remix-utils: "npm:7.7.0"
     tiny-invariant: "npm:1.3.3"
     ts-essentials: "npm:10.0.2"
-    ts-pattern: "npm:5.3.1"
-    typescript: "npm:5.5.4"
+    ts-pattern: "npm:5.5.0"
+    typescript: "npm:5.6.3"
     typescript-plugin-css-modules: "npm:5.1.0"
     typescript-remix-routes-plugin: "npm:1.0.1"
     ufo: "npm:1.5.4"
     usehooks-ts: "npm:3.1.0"
     uuid: "npm:10.0.0"
-    vite: "npm:5.4.2"
+    vite: "npm:5.4.9"
     vite-tsconfig-paths: "npm:5.0.1"
     zod: "npm:3.23.8"
     zodix: "npm:0.4.4"
@@ -6315,8 +6194,8 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/generated@workspace:libs/generated"
   dependencies:
-    "@graphql-codegen/cli": "npm:5.0.2"
-    "@graphql-codegen/client-preset": "npm:4.3.3"
+    "@graphql-codegen/cli": "npm:5.0.3"
+    "@graphql-codegen/client-preset": "npm:4.4.0"
     "@graphql-typed-document-node/core": "npm:3.2.0"
     graphql: "npm:16.9.0"
   languageName: unknown
@@ -6335,9 +6214,9 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/root@workspace:."
   dependencies:
-    "@biomejs/biome": "npm:1.8.3"
+    "@biomejs/biome": "npm:1.9.4"
     tsconfig-moon: "npm:1.3.0"
-    typescript: "npm:5.5.4"
+    typescript: "npm:5.6.3"
   languageName: unknown
   linkType: soft
 
@@ -6347,6 +6226,9 @@ __metadata:
   dependencies:
     "@react-email/components": "npm:0.0.22"
     "@ryot/ts-utils": "workspace:*"
+    "@types/react": "npm:18.3.11"
+    "@types/react-dom": "npm:18.3.1"
+    react: "npm:18.3.1"
     react-dom: "npm:18.3.1"
     react-email: "npm:2.1.6"
   languageName: unknown
@@ -6356,12 +6238,13 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/ts-utils@workspace:libs/ts-utils"
   dependencies:
-    "@conform-to/zod": "npm:1.1.5"
+    "@conform-to/zod": "npm:1.2.2"
     "@ryot/generated": "workspace:*"
-    "@types/lodash": "npm:4.17.7"
+    "@types/lodash": "npm:4.17.12"
     dayjs: "npm:1.11.13"
     humanize-duration-ts: "npm:2.1.1"
     lodash: "npm:4.17.21"
+    tiny-invariant: "npm:1.3.3"
   languageName: unknown
   linkType: soft
 
@@ -6369,65 +6252,65 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/website@workspace:apps/website"
   dependencies:
-    "@conform-to/zod": "npm:1.1.5"
+    "@conform-to/zod": "npm:1.2.2"
     "@isaacs/ttlcache": "npm:1.4.1"
-    "@paddle/paddle-js": "npm:1.2.0"
-    "@paddle/paddle-node-sdk": "npm:1.4.1"
+    "@paddle/paddle-js": "npm:1.2.3"
+    "@paddle/paddle-node-sdk": "npm:1.9.1"
     "@radix-ui/react-label": "npm:2.1.0"
     "@radix-ui/react-slot": "npm:1.1.0"
-    "@radix-ui/react-tooltip": "npm:1.1.2"
-    "@react-email/components": "npm:0.0.22"
-    "@remix-run/dev": "npm:2.10.3"
-    "@remix-run/node": "npm:2.10.3"
-    "@remix-run/react": "npm:2.10.3"
-    "@remix-run/serve": "npm:2.10.3"
+    "@radix-ui/react-tooltip": "npm:1.1.3"
+    "@react-email/components": "npm:0.0.25"
+    "@remix-run/dev": "npm:2.13.1"
+    "@remix-run/node": "npm:2.13.1"
+    "@remix-run/react": "npm:2.13.1"
+    "@remix-run/serve": "npm:2.13.1"
     "@ryot/generated": "workspace:*"
     "@ryot/graphql": "workspace:*"
     "@ryot/transactional": "workspace:*"
     "@ryot/ts-utils": "workspace:*"
-    "@tabler/icons-react": "npm:3.11.0"
-    "@tailwindcss/typography": "npm:0.5.13"
-    "@types/crypto-js": "npm:^4"
-    "@types/lodash": "npm:4.17.7"
-    "@types/nodemailer": "npm:6"
-    "@types/react": "npm:18.2.20"
-    "@types/react-dom": "npm:18.2.7"
-    "@unkey/api": "npm:0.23.0"
-    autoprefixer: "npm:10.4.19"
+    "@tabler/icons-react": "npm:3.19.0"
+    "@tailwindcss/typography": "npm:0.5.15"
+    "@types/crypto-js": "npm:^4.2.2"
+    "@types/lodash": "npm:4.17.12"
+    "@types/nodemailer": "npm:6.4.16"
+    "@types/react": "npm:18.3.11"
+    "@types/react-dom": "npm:18.3.1"
+    "@unkey/api": "npm:0.26.2"
+    autoprefixer: "npm:10.4.20"
     class-variance-authority: "npm:0.7.0"
     clsx: "npm:2.1.1"
     crypto-js: "npm:4.2.0"
-    dayjs: "npm:1.11.12"
-    drizzle-kit: "npm:0.23.0"
-    drizzle-orm: "npm:0.32.1"
-    embla-carousel-autoplay: "npm:8.1.7"
-    embla-carousel-react: "npm:8.1.7"
+    dayjs: "npm:1.11.13"
+    drizzle-kit: "npm:0.26.2"
+    drizzle-orm: "npm:0.35.3"
+    embla-carousel-autoplay: "npm:8.3.0"
+    embla-carousel-react: "npm:8.3.0"
     graphql: "npm:16.9.0"
     graphql-request: "npm:7.1.0"
     humanize-duration-ts: "npm:2.1.1"
     input-otp: "npm:1.2.4"
-    isbot: "npm:4.1.0"
+    isbot: "npm:5.1.17"
     lodash: "npm:4.17.21"
-    lucide-react: "npm:0.414.0"
+    lucide-react: "npm:0.453.0"
     next-themes: "npm:0.3.0"
-    nodemailer: "npm:6.9.14"
+    nodemailer: "npm:6.9.15"
     openid-client: "npm:5.6.5"
-    postcss: "npm:8.4.38"
+    postcss: "npm:8.4.47"
     postgres: "npm:3.4.4"
-    react: "npm:18.2.0"
-    react-dom: "npm:18.2.0"
-    remix-routes: "npm:1.7.6"
-    remix-utils: "npm:7.6.0"
+    react: "npm:18.3.1"
+    react-dom: "npm:18.3.1"
+    remix-routes: "npm:1.7.7"
+    remix-utils: "npm:7.7.0"
     sonner: "npm:1.5.0"
-    tailwind-merge: "npm:2.4.0"
-    tailwindcss: "npm:3.4.4"
+    tailwind-merge: "npm:2.5.4"
+    tailwindcss: "npm:3.4.14"
     tailwindcss-animate: "npm:1.0.7"
-    ts-pattern: "npm:5.2.0"
-    typescript: "npm:5.1.6"
+    ts-pattern: "npm:5.5.0"
+    typescript: "npm:5.6.3"
     typescript-remix-routes-plugin: "npm:1.0.1"
     ufo: "npm:1.5.4"
-    vite: "npm:5.1.0"
-    vite-tsconfig-paths: "npm:4.2.1"
+    vite: "npm:5.4.9"
+    vite-tsconfig-paths: "npm:5.0.1"
     zod: "npm:3.23.8"
     zodix: "npm:0.4.4"
   languageName: unknown
@@ -6583,53 +6466,35 @@ __metadata:
   linkType: hard
 
 "@swc/types@npm:^0.1.5":
-  version: 0.1.12
-  resolution: "@swc/types@npm:0.1.12"
+  version: 0.1.13
+  resolution: "@swc/types@npm:0.1.13"
   dependencies:
     "@swc/counter": "npm:^0.1.3"
-  checksum: 10/92dbbc70cd068ea30fb6fbdc1ae8599d6c058a5d09b2923d6e4e24fab5ad7c86a19dd01f349a8e03e300a9321e06911a24df18303b40e307fbd4109372cef2ef
-  languageName: node
-  linkType: hard
-
-"@tabler/icons-react@npm:3.11.0":
-  version: 3.11.0
-  resolution: "@tabler/icons-react@npm:3.11.0"
-  dependencies:
-    "@tabler/icons": "npm:3.11.0"
-  peerDependencies:
-    react: ">= 16"
-  checksum: 10/1011b55f0a1efe327a863c6fdaf6522685d6492b3cddef0e8dfc2fa8326a88c661e13417f3b07a5710b4bbbde6c6830cb3c2309db81a7b20c0b2242f3e89a982
+  checksum: 10/d0a50432917048cc69e30c82d1266e052a8e8d05ab202c5d74a5666be3748da4d2f99aaff46d91c0e3d285cf8f55270f8391cd578066fdecc3865733f8d5e14a
   languageName: node
   linkType: hard
 
-"@tabler/icons-react@npm:3.12.0":
-  version: 3.12.0
-  resolution: "@tabler/icons-react@npm:3.12.0"
+"@tabler/icons-react@npm:3.19.0":
+  version: 3.19.0
+  resolution: "@tabler/icons-react@npm:3.19.0"
   dependencies:
-    "@tabler/icons": "npm:3.12.0"
+    "@tabler/icons": "npm:3.19.0"
   peerDependencies:
     react: ">= 16"
-  checksum: 10/505444ad6040196516da2c876b427c9d70389b486037faafeea5f471e6f793a4471b5338b99eecb755647cfa4e0c327fe1d356cdfe5ce2548947b0372270e46b
-  languageName: node
-  linkType: hard
-
-"@tabler/icons@npm:3.11.0":
-  version: 3.11.0
-  resolution: "@tabler/icons@npm:3.11.0"
-  checksum: 10/1815b5ba0b1016db4902248f5fbc79dff217b9f41ece950bfa0222cfd92d75740bc9d08ab227d7b9fee93e972df57c0c4d8da8b9769a92e065067aa7d8370a13
+  checksum: 10/0b3396519665cdef8e2722b0280f625fb996552ce3f1fde986628929b841f0f7eac036c4f495a16c99f330ae662ab3c527cfc80aa7e0293974d829532021daf1
   languageName: node
   linkType: hard
 
-"@tabler/icons@npm:3.12.0":
-  version: 3.12.0
-  resolution: "@tabler/icons@npm:3.12.0"
-  checksum: 10/1ab2ebcb3b7a4d5272a58f67091b39881838c04664cb170d729993bc22cf8a65b96d822e76963aa7310f34e7496928dfb3a1fe7d45940bea7fad038a873ac844
+"@tabler/icons@npm:3.19.0":
+  version: 3.19.0
+  resolution: "@tabler/icons@npm:3.19.0"
+  checksum: 10/f69b346c868bf0092d3650da3febacb5006423aa191f1e1fcb1487b3c5c56dfb4a8876f58286c2f0476acfe4c612c096ca83fdde896562be27405948c1d9556a
   languageName: node
   linkType: hard
 
-"@tailwindcss/typography@npm:0.5.13":
-  version: 0.5.13
-  resolution: "@tailwindcss/typography@npm:0.5.13"
+"@tailwindcss/typography@npm:0.5.15":
+  version: 0.5.15
+  resolution: "@tailwindcss/typography@npm:0.5.15"
   dependencies:
     lodash.castarray: "npm:^4.4.0"
     lodash.isplainobject: "npm:^4.0.6"
@@ -6637,44 +6502,44 @@ __metadata:
     postcss-selector-parser: "npm:6.0.10"
   peerDependencies:
     tailwindcss: "*"
-  checksum: 10/ffa5fee725d390591f1654fee8275b025248ef0e25a54b1749db23ec82d6117729b933e2326b6d52348ff2844839755f89d5d82fe5900d91b399c9380041b4e9
+  checksum: 10/8c677e4de25a5362d757c3c0b9d594b63c789ab287cad846a082ca227e0d3435145793301ced8f32d5215d5c21537195e7d87ca08a6e7a8facf47a11e7f07d22
   languageName: node
   linkType: hard
 
-"@tanstack/query-core@npm:5.52.0":
-  version: 5.52.0
-  resolution: "@tanstack/query-core@npm:5.52.0"
-  checksum: 10/cd21e87ad7a0bbb262dea21704352eb1bbaafc26776ae1602b4be9a2d0d1f16a89cc4b5951f69083e26f970d75386431240d5a573ed9bce5a37ba2dc862e376a
+"@tanstack/query-core@npm:5.59.13":
+  version: 5.59.13
+  resolution: "@tanstack/query-core@npm:5.59.13"
+  checksum: 10/dc28f8a667095a98e22411a0f067f0c03701c9c92355d1cb74e9b160c49acdf0e5e362cc027ece6a70e4faec721170da2ee38e71f07814c664a2c4cbd494d736
   languageName: node
   linkType: hard
 
-"@tanstack/query-devtools@npm:5.51.16":
-  version: 5.51.16
-  resolution: "@tanstack/query-devtools@npm:5.51.16"
-  checksum: 10/b0e8c1f86890a515d4ddbab4743387aecd882271f7be2cbc36f69d05ba42b803ae2e9bbfd53a03450ca4827c94f6b5d7d6fa5e013bfabe6ee0aa9a7b34a223d3
+"@tanstack/query-devtools@npm:5.58.0":
+  version: 5.58.0
+  resolution: "@tanstack/query-devtools@npm:5.58.0"
+  checksum: 10/ca16c47c943ea392dfddc301f7e09ecdb0c8b905fb684b8f26b908a244e2e897679efb0ead5fa8e728711017341fdd91d8c51ebb19f746819e26ade5549f539e
   languageName: node
   linkType: hard
 
-"@tanstack/react-query-devtools@npm:5.52.0":
-  version: 5.52.0
-  resolution: "@tanstack/react-query-devtools@npm:5.52.0"
+"@tanstack/react-query-devtools@npm:5.59.15":
+  version: 5.59.15
+  resolution: "@tanstack/react-query-devtools@npm:5.59.15"
   dependencies:
-    "@tanstack/query-devtools": "npm:5.51.16"
+    "@tanstack/query-devtools": "npm:5.58.0"
   peerDependencies:
-    "@tanstack/react-query": ^5.52.0
+    "@tanstack/react-query": ^5.59.15
     react: ^18 || ^19
-  checksum: 10/67fff9fe45a54e6823800bd15de5d5712b65bda9bb567907765481025775817b03c56925add47dd51fe19de1135b57b290570e5cb00b5a071ca3f7d409c39805
+  checksum: 10/89702ee06d09154c8c845dcefc1f39fe6ae507fba7219d41224da35c55d0f4e7c76fc819fc85604d761395ae6638f5864ddd0be41a82b368f55dc5998ce85b90
   languageName: node
   linkType: hard
 
-"@tanstack/react-query@npm:5.52.0":
-  version: 5.52.0
-  resolution: "@tanstack/react-query@npm:5.52.0"
+"@tanstack/react-query@npm:5.59.15":
+  version: 5.59.15
+  resolution: "@tanstack/react-query@npm:5.59.15"
   dependencies:
-    "@tanstack/query-core": "npm:5.52.0"
+    "@tanstack/query-core": "npm:5.59.13"
   peerDependencies:
-    react: ^18.0.0
-  checksum: 10/6976d309d306f0dd70f25e0de820812c47bfc39b654294e1512d4bb5320d0abad1bc3d6de16a25b7ae6766eb9b928c5e396e996d7d6689d33a1c1436de68cf7b
+    react: ^18 || ^19
+  checksum: 10/a55168650140c6e0a3a20adad9e1ef2088a6834a26d7f3455e90177c7dcff2b9ba92fdc330f4d2b1afe246233330b27f71dec003e883ed6aa195490fcf43e803
   languageName: node
   linkType: hard
 
@@ -6717,7 +6582,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/crypto-js@npm:4.2.2, @types/crypto-js@npm:^4":
+"@types/crypto-js@npm:4.2.2, @types/crypto-js@npm:^4.2.2":
   version: 4.2.2
   resolution: "@types/crypto-js@npm:4.2.2"
   checksum: 10/a40fc5a9219fd33f54ba3e094c5e5ab2904d3106681a76f1029bb038976591e9c8959099963bf4474fde21c2d8d00c1f896445206a3a58f85588f9cb1bd96a9a
@@ -6745,6 +6610,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/d3-hierarchy@npm:^1.1.8":
+  version: 1.1.11
+  resolution: "@types/d3-hierarchy@npm:1.1.11"
+  checksum: 10/057948069a5c1a366462bc08d597aaaf8b6f7eb96d785273d20ab56456deb01958c8500de60b491c1bc4cb08c7bf05fdcb6a94ffb91b3548d6b9c1b365840ccc
+  languageName: node
+  linkType: hard
+
 "@types/d3-interpolate@npm:^3.0.1":
   version: 3.0.1
   resolution: "@types/d3-interpolate@npm:3.0.1"
@@ -6802,26 +6674,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/eslint-scope@npm:^3.7.3":
-  version: 3.7.7
-  resolution: "@types/eslint-scope@npm:3.7.7"
-  dependencies:
-    "@types/eslint": "npm:*"
-    "@types/estree": "npm:*"
-  checksum: 10/e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e
-  languageName: node
-  linkType: hard
-
-"@types/eslint@npm:*":
-  version: 9.6.0
-  resolution: "@types/eslint@npm:9.6.0"
-  dependencies:
-    "@types/estree": "npm:*"
-    "@types/json-schema": "npm:*"
-  checksum: 10/39fc797c671ec9c9184802b4974748cf45ee1b11d7aaaaede44426abcafd07ec7c18eb090e8f5b3387b51637ce3fdf54499472d8dd58a928f0d005cbacb573b4
-  languageName: node
-  linkType: hard
-
 "@types/estree-jsx@npm:^1.0.0":
   version: 1.0.3
   resolution: "@types/estree-jsx@npm:1.0.3"
@@ -6838,13 +6690,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.5":
+"@types/estree@npm:1.0.5":
   version: 1.0.5
   resolution: "@types/estree@npm:1.0.5"
   checksum: 10/7de6d928dd4010b0e20c6919e1a6c27b61f8d4567befa89252055fad503d587ecb9a1e3eab1b1901f923964d7019796db810b7fd6430acb26c32866d126fd408
   languageName: node
   linkType: hard
 
+"@types/estree@npm:^1.0.5":
+  version: 1.0.6
+  resolution: "@types/estree@npm:1.0.6"
+  checksum: 10/9d35d475095199c23e05b431bcdd1f6fec7380612aed068b14b2a08aa70494de8a9026765a5a91b1073f636fb0368f6d8973f518a31391d519e20c59388ed88d
+  languageName: node
+  linkType: hard
+
 "@types/hast@npm:^2.0.0":
   version: 2.3.8
   resolution: "@types/hast@npm:2.3.8"
@@ -6854,20 +6713,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/hoist-non-react-statics@npm:^3.3.1":
-  version: 3.3.1
-  resolution: "@types/hoist-non-react-statics@npm:3.3.1"
-  dependencies:
-    "@types/react": "npm:*"
-    hoist-non-react-statics: "npm:^3.3.0"
-  checksum: 10/071e6d75a0ed9aa0e9ca2cc529a8c15bf7ac3e4a37aac279772ea6036fd0bf969b67fb627b65cfce65adeab31fec1e9e95b4dcdefeab075b580c0c7174206f63
-  languageName: node
-  linkType: hard
-
-"@types/howler@npm:2.2.11":
-  version: 2.2.11
-  resolution: "@types/howler@npm:2.2.11"
-  checksum: 10/2875810bc1c076cfe7117adaecb88bdd12126b674329488d2b35d4906d15375f606c91cba8ee671fb09bbe2728fd5d396616e9b5144c7a8fca0bdd49e2d410e7
+"@types/howler@npm:2.2.12":
+  version: 2.2.12
+  resolution: "@types/howler@npm:2.2.12"
+  checksum: 10/94597cf283cdacb4edecd161161ca78b585d3abf342e57f86093ad0eec86d94b7de644fb902a11a6cd1d1bf5225ffd07c336a04a5ba9bdc1cc14c26ff57cb678
   languageName: node
   linkType: hard
 
@@ -6885,7 +6734,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8":
+"@types/json-schema@npm:^7.0.8":
   version: 7.0.15
   resolution: "@types/json-schema@npm:7.0.15"
   checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7
@@ -6899,10 +6748,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/lodash@npm:4.17.7":
-  version: 4.17.7
-  resolution: "@types/lodash@npm:4.17.7"
-  checksum: 10/b8177f19cf962414a66989837481b13f546afc2e98e8d465bec59e6ac03a59c584eb7053ce511cde3a09c5f3096d22a5ae22cfb56b23f3b0da75b0743b6b1a44
+"@types/lodash@npm:4.17.12":
+  version: 4.17.12
+  resolution: "@types/lodash@npm:4.17.12"
+  checksum: 10/3d829f97c0ea713135e6eb48e983feffb315f4cf228d03654ca8a328e3fecd03005bf7dd30bf411f4306967c610d96738d441588283a41456815f2ce1fd7044c
   languageName: node
   linkType: hard
 
@@ -6952,12 +6801,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/nodemailer@npm:6":
-  version: 6.4.15
-  resolution: "@types/nodemailer@npm:6.4.15"
+"@types/nodemailer@npm:6.4.16":
+  version: 6.4.16
+  resolution: "@types/nodemailer@npm:6.4.16"
   dependencies:
     "@types/node": "npm:*"
-  checksum: 10/1b400b694ed1bc51c242a719de44b484911481596c288407fff18b81ee76945d2d0c6e411371dfa4293747ebf4146a1f0df0e188ff7b9d2adaccd1183dfa1c2c
+  checksum: 10/38e612fef3963c7ae7563f039a8b26d8d4371a7feeedaf38a534f889094f6727fb0d52f565902b01099cfe0b4be7956125ebdae312b9ab3905929df5c815bd95
   languageName: node
   linkType: hard
 
@@ -7007,21 +6856,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/react-dom@npm:18.2.7":
-  version: 18.2.7
-  resolution: "@types/react-dom@npm:18.2.7"
-  dependencies:
-    "@types/react": "npm:*"
-  checksum: 10/9b70ef66cbe2d2898ea37eb79ee3697e0e4ad3d950e769a601f79be94097d43b8ef45b98a0b29528203c7d731c81666f637b2b7032deeced99214b4bc0662614
-  languageName: node
-  linkType: hard
-
-"@types/react-dom@npm:18.3.0, @types/react-dom@npm:^18.2.0":
-  version: 18.3.0
-  resolution: "@types/react-dom@npm:18.3.0"
+"@types/react-dom@npm:18.3.1, @types/react-dom@npm:^18.2.0":
+  version: 18.3.1
+  resolution: "@types/react-dom@npm:18.3.1"
   dependencies:
     "@types/react": "npm:*"
-  checksum: 10/6ff53f5a7b7fba952a68e114d3b542ebdc1e87a794234785ebab0bcd9bde7fb4885f21ebaf93d26dc0a1b5b93287f42cad68b78ae04dddf6b20da7aceff0beaf
+  checksum: 10/33f9ba79b26641ddf00a8699c30066b7e3573ab254e97475bf08f82fab83a6d3ce8d4ebad86afeb49bb8df3374390a9ba93125cece33badc4b3e8f7eac3c84d8
   languageName: node
   linkType: hard
 
@@ -7083,12 +6923,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@unkey/api@npm:0.23.0":
-  version: 0.23.0
-  resolution: "@unkey/api@npm:0.23.0"
+"@unkey/api@npm:0.26.2":
+  version: 0.26.2
+  resolution: "@unkey/api@npm:0.26.2"
   dependencies:
     "@unkey/rbac": "npm:^0.3.1"
-  checksum: 10/7f3d519c54fbd5097086f61ddd0c0ee5d1771711ea508a45b35ba8cc7df697dd6f62f2baf9e4225663b77dce5411e565a89a3eca3620a7d20dbf357d4ee6d62e
+  checksum: 10/8fc64b504759ed296e0b79013d2283f3136399991425e91d1bcf5cd5bf42515edd715e094e7f6240462eca0ab0f9f4de6feb5469c2e14be4962dfdd592402f8c
   languageName: node
   linkType: hard
 
@@ -7325,13 +7165,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@whatwg-node/events@npm:^0.0.2":
-  version: 0.0.2
-  resolution: "@whatwg-node/events@npm:0.0.2"
-  checksum: 10/6d491801d36967d5d62443cca6ef39d41cf882d575839d2e9d1a5f394722cd24ef84fe9e897bb72c01bd198871fda9ff0e8b1ac5aa6f3f814f87d92b7f28fdcc
-  languageName: node
-  linkType: hard
-
 "@whatwg-node/events@npm:^0.1.0":
   version: 0.1.1
   resolution: "@whatwg-node/events@npm:0.1.1"
@@ -7339,19 +7172,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@whatwg-node/fetch@npm:^0.8.0":
-  version: 0.8.5
-  resolution: "@whatwg-node/fetch@npm:0.8.5"
-  dependencies:
-    "@peculiar/webcrypto": "npm:^1.4.0"
-    "@whatwg-node/node-fetch": "npm:^0.3.3"
-    busboy: "npm:^1.6.0"
-    urlpattern-polyfill: "npm:^7.0.0"
-    web-streams-polyfill: "npm:^3.2.1"
-  checksum: 10/1cd426f10a5f33c6d6ff27b8b557d8d083d2b32890d6af56e1d868f4beabf18218d9ac6b43a1d9b4d1dbd7c3b24c9c63b9f8e21bc4d178e46dfd7c15c35b6f71
-  languageName: node
-  linkType: hard
-
 "@whatwg-node/fetch@npm:^0.9.0":
   version: 0.9.9
   resolution: "@whatwg-node/fetch@npm:0.9.9"
@@ -7362,16 +7182,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@whatwg-node/node-fetch@npm:^0.3.3":
-  version: 0.3.5
-  resolution: "@whatwg-node/node-fetch@npm:0.3.5"
+"@whatwg-node/fetch@npm:^0.9.20":
+  version: 0.9.22
+  resolution: "@whatwg-node/fetch@npm:0.9.22"
   dependencies:
-    "@whatwg-node/events": "npm:^0.0.2"
-    busboy: "npm:^1.6.0"
-    fast-querystring: "npm:^1.1.1"
-    fast-url-parser: "npm:^1.1.3"
-    tslib: "npm:^2.3.1"
-  checksum: 10/e75399d20ba3ab634fbd5b92481e93b151760c3fa9734881d3b78c422fa90f2dab1f32b624b709bfde27d924c439dd9cbaad675aa54d3f5f4a44862ce9e0e42d
+    "@whatwg-node/node-fetch": "npm:^0.5.27"
+    urlpattern-polyfill: "npm:^10.0.0"
+  checksum: 10/de6ced46d78dd5b662edd862f15b404ff3ea1aa3a68d92a4b79d920054dfcad6b2532137e7d48448e6301548342992cb25405251be4cf449ed5cdd3ba667a697
   languageName: node
   linkType: hard
 
@@ -7388,6 +7205,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@whatwg-node/node-fetch@npm:^0.5.27":
+  version: 0.5.27
+  resolution: "@whatwg-node/node-fetch@npm:0.5.27"
+  dependencies:
+    "@kamilkisiela/fast-url-parser": "npm:^1.1.4"
+    busboy: "npm:^1.6.0"
+    fast-querystring: "npm:^1.1.1"
+    tslib: "npm:^2.6.3"
+  checksum: 10/b38abdefaccedb268913aefb66798f386b197dc65a3b9a73ee77438508f06e6c62889c261fff07f001f9e38800bd07b81c2c2667c3ffb5df42890bcf336e7829
+  languageName: node
+  linkType: hard
+
 "@xtuc/ieee754@npm:^1.2.0":
   version: 1.2.0
   resolution: "@xtuc/ieee754@npm:1.2.0"
@@ -7479,11 +7308,11 @@ __metadata:
   linkType: hard
 
 "acorn@npm:^8.7.1, acorn@npm:^8.8.2":
-  version: 8.12.1
-  resolution: "acorn@npm:8.12.1"
+  version: 8.13.0
+  resolution: "acorn@npm:8.13.0"
   bin:
     acorn: bin/acorn
-  checksum: 10/d08c2d122bba32d0861e0aa840b2ee25946c286d5dc5990abca991baf8cdbfbe199b05aacb221b979411a2fea36f83e26b5ac4f6b4e0ce49038c62316c1848f0
+  checksum: 10/33e3a03114b02b3bc5009463b3d9549b31a90ee38ebccd5e66515830a02acf62a90edcc12abfb6c9fb3837b6c17a3ec9b72b3bf52ac31d8ad8248a4af871e0f5
   languageName: node
   linkType: hard
 
@@ -7699,17 +7528,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"asn1js@npm:^3.0.1, asn1js@npm:^3.0.5":
-  version: 3.0.5
-  resolution: "asn1js@npm:3.0.5"
-  dependencies:
-    pvtsutils: "npm:^1.3.2"
-    pvutils: "npm:^1.1.3"
-    tslib: "npm:^2.4.0"
-  checksum: 10/17fb0302432186631550de9606a4622ec366646d072cde9cdf4bcafa47bd2425e157eeb7b1377ee6520f8b46687b4ecaee31cf0ad2fa494361a1938b2ed53194
-  languageName: node
-  linkType: hard
-
 "astral-regex@npm:^2.0.0":
   version: 2.0.0
   resolution: "astral-regex@npm:2.0.0"
@@ -7765,21 +7583,21 @@ __metadata:
   languageName: node
   linkType: hard
 
-"autoprefixer@npm:10.4.19":
-  version: 10.4.19
-  resolution: "autoprefixer@npm:10.4.19"
+"autoprefixer@npm:10.4.20":
+  version: 10.4.20
+  resolution: "autoprefixer@npm:10.4.20"
   dependencies:
-    browserslist: "npm:^4.23.0"
-    caniuse-lite: "npm:^1.0.30001599"
+    browserslist: "npm:^4.23.3"
+    caniuse-lite: "npm:^1.0.30001646"
     fraction.js: "npm:^4.3.7"
     normalize-range: "npm:^0.1.2"
-    picocolors: "npm:^1.0.0"
+    picocolors: "npm:^1.0.1"
     postcss-value-parser: "npm:^4.2.0"
   peerDependencies:
     postcss: ^8.1.0
   bin:
     autoprefixer: bin/autoprefixer
-  checksum: 10/98378eae37b8bf0f1515e4c91b4c9c1ce69ede311d4dea7e934f5afe147d23712c577f112c4019a4c40461c585d82d474d08044f8eb6cb8a063c3d5b7aca52d2
+  checksum: 10/d3c4b562fc4af2393623a0207cc336f5b9f94c4264ae1c316376904c279702ce2b12dc3f27205f491195d1e29bb52ffc269970ceb0f271f035fadee128a273f7
   languageName: node
   linkType: hard
 
@@ -7924,9 +7742,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"body-parser@npm:1.20.2":
-  version: 1.20.2
-  resolution: "body-parser@npm:1.20.2"
+"body-parser@npm:1.20.3":
+  version: 1.20.3
+  resolution: "body-parser@npm:1.20.3"
   dependencies:
     bytes: "npm:3.1.2"
     content-type: "npm:~1.0.5"
@@ -7936,11 +7754,11 @@ __metadata:
     http-errors: "npm:2.0.0"
     iconv-lite: "npm:0.4.24"
     on-finished: "npm:2.4.1"
-    qs: "npm:6.11.0"
+    qs: "npm:6.13.0"
     raw-body: "npm:2.5.2"
     type-is: "npm:~1.6.18"
     unpipe: "npm:1.0.0"
-  checksum: 10/3cf171b82190cf91495c262b073e425fc0d9e25cc2bf4540d43f7e7bbca27d6a9eae65ca367b6ef3993eea261159d9d2ab37ce444e8979323952e12eb3df319a
+  checksum: 10/8723e3d7a672eb50854327453bed85ac48d045f4958e81e7d470c56bf111f835b97e5b73ae9f6393d0011cc9e252771f46fd281bbabc57d33d3986edf1e6aeca
   languageName: node
   linkType: hard
 
@@ -7981,17 +7799,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"browserslist@npm:^4.21.10, browserslist@npm:^4.21.5, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1":
-  version: 4.23.2
-  resolution: "browserslist@npm:4.23.2"
+"browserslist@npm:^4.21.10, browserslist@npm:^4.21.5, browserslist@npm:^4.23.3":
+  version: 4.24.2
+  resolution: "browserslist@npm:4.24.2"
   dependencies:
-    caniuse-lite: "npm:^1.0.30001640"
-    electron-to-chromium: "npm:^1.4.820"
-    node-releases: "npm:^2.0.14"
-    update-browserslist-db: "npm:^1.1.0"
+    caniuse-lite: "npm:^1.0.30001669"
+    electron-to-chromium: "npm:^1.5.41"
+    node-releases: "npm:^2.0.18"
+    update-browserslist-db: "npm:^1.1.1"
   bin:
     browserslist: cli.js
-  checksum: 10/326a98b1c39bcc9a99b197f15790dc28e122b1aead3257c837421899377ac96239123f26868698085b3d9be916d72540602738e1f857e86a387e810af3fda6e5
+  checksum: 10/f8a9d78bbabe466c57ffd5c50a9e5582a5df9aa68f43078ca62a9f6d0d6c70ba72eca72d0a574dbf177cf55cdca85a46f7eb474917a47ae5398c66f8b76f7d1c
   languageName: node
   linkType: hard
 
@@ -8023,6 +7841,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"browserslist@npm:^4.23.1":
+  version: 4.23.2
+  resolution: "browserslist@npm:4.23.2"
+  dependencies:
+    caniuse-lite: "npm:^1.0.30001640"
+    electron-to-chromium: "npm:^1.4.820"
+    node-releases: "npm:^2.0.14"
+    update-browserslist-db: "npm:^1.1.0"
+  bin:
+    browserslist: cli.js
+  checksum: 10/326a98b1c39bcc9a99b197f15790dc28e122b1aead3257c837421899377ac96239123f26868698085b3d9be916d72540602738e1f857e86a387e810af3fda6e5
+  languageName: node
+  linkType: hard
+
 "bser@npm:2.1.1":
   version: 2.1.1
   resolution: "bser@npm:2.1.1"
@@ -8144,7 +7976,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2":
+"call-bind@npm:^1.0.2":
   version: 1.0.2
   resolution: "call-bind@npm:1.0.2"
   dependencies:
@@ -8165,6 +7997,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"call-bind@npm:^1.0.7":
+  version: 1.0.7
+  resolution: "call-bind@npm:1.0.7"
+  dependencies:
+    es-define-property: "npm:^1.0.0"
+    es-errors: "npm:^1.3.0"
+    function-bind: "npm:^1.1.2"
+    get-intrinsic: "npm:^1.2.4"
+    set-function-length: "npm:^1.2.1"
+  checksum: 10/cd6fe658e007af80985da5185bff7b55e12ef4c2b6f41829a26ed1eef254b1f1c12e3dfd5b2b068c6ba8b86aba62390842d81752e67dcbaec4f6f76e7113b6b7
+  languageName: node
+  linkType: hard
+
 "callsites@npm:^3.0.0":
   version: 3.1.0
   resolution: "callsites@npm:3.1.0"
@@ -8214,10 +8059,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"caniuse-lite@npm:^1.0.30001464, caniuse-lite@npm:^1.0.30001579":
-  version: 1.0.30001643
-  resolution: "caniuse-lite@npm:1.0.30001643"
-  checksum: 10/dddbda29fa24fbc435873309c71070461cbfc915d9bce3216180524c20c5637b2bee1a14b45972e9ac19e1fdf63fba3f63608b9e7d68de32f5ee1953c8c69e05
+"caniuse-lite@npm:^1.0.30001464, caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001669":
+  version: 1.0.30001669
+  resolution: "caniuse-lite@npm:1.0.30001669"
+  checksum: 10/cd0b481bb997703cb7651e55666b4aa4e7b4ecf9784796e2393179a15e55c71a6abc6ff865c922bbd3bbfa4a4bf0530d8da13989b97ff8c7850c8a5bd4e00491
   languageName: node
   linkType: hard
 
@@ -8228,7 +8073,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001640":
+"caniuse-lite@npm:^1.0.30001579":
+  version: 1.0.30001643
+  resolution: "caniuse-lite@npm:1.0.30001643"
+  checksum: 10/dddbda29fa24fbc435873309c71070461cbfc915d9bce3216180524c20c5637b2bee1a14b45972e9ac19e1fdf63fba3f63608b9e7d68de32f5ee1953c8c69e05
+  languageName: node
+  linkType: hard
+
+"caniuse-lite@npm:^1.0.30001640":
   version: 1.0.30001642
   resolution: "caniuse-lite@npm:1.0.30001642"
   checksum: 10/8d80ea82be453ae0fdfea8766d82740a4945c1b99189650f29bfc458d4e235d7e99027a8f8bc5a4228d8c4457ba896315284b0703f300353ad5f09d8e693de10
@@ -8253,6 +8105,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"chain-function@npm:^1.0.0":
+  version: 1.0.1
+  resolution: "chain-function@npm:1.0.1"
+  checksum: 10/11b2c4aaea5b37a74717181197312bc95ddfe61a6de7c2b275b34ebb8c2d1e72ce3f20b828dc84982c0b2defc5fc0814bbd3e50f294420f252392ec04de5c806
+  languageName: node
+  linkType: hard
+
 "chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2":
   version: 4.1.2
   resolution: "chalk@npm:4.1.2"
@@ -8505,7 +8364,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"clone@npm:^2.1.2":
+"clone@npm:^2.1.1":
   version: 2.1.2
   resolution: "clone@npm:2.1.2"
   checksum: 10/d9c79efba655f0bf601ab299c57eb54cbaa9860fb011aee9d89ed5ac0d12df1660ab7642fddaabb9a26b7eff0e117d4520512cb70798319ff5d30a111b5310c2
@@ -8761,7 +8620,21 @@ __metadata:
   languageName: node
   linkType: hard
 
-"cookie@npm:0.6.0, cookie@npm:^0.6.0":
+"cookie@npm:0.7.1":
+  version: 0.7.1
+  resolution: "cookie@npm:0.7.1"
+  checksum: 10/aec6a6aa0781761bf55d60447d6be08861d381136a0fe94aa084fddd4f0300faa2b064df490c6798adfa1ebaef9e0af9b08a189c823e0811b8b313b3d9a03380
+  languageName: node
+  linkType: hard
+
+"cookie@npm:1.0.1":
+  version: 1.0.1
+  resolution: "cookie@npm:1.0.1"
+  checksum: 10/4b24d4fad5ba94ab76d74a8fc33ae1dcdb5dc02013e03e9577b26f019d9dfe396ffb9b3711ba1726bcfa1b93c33d117db0f31e187838aed7753dee1abc691688
+  languageName: node
+  linkType: hard
+
+"cookie@npm:^0.6.0":
   version: 0.6.0
   resolution: "cookie@npm:0.6.0"
   checksum: 10/c1f8f2ea7d443b9331680598b0ae4e6af18a618c37606d1bbdc75bec8361cce09fe93e727059a673f2ba24467131a9fb5a4eec76bb1b149c1b3e1ccb268dc583
@@ -8941,10 +8814,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"d3-hierarchy@npm:^3.1.2":
-  version: 3.1.2
-  resolution: "d3-hierarchy@npm:3.1.2"
-  checksum: 10/497b79dc6c35e28b21e8a7b94db92876abd1d4ec082d9803a07ea8964e55b0e71c511a21489363a36f1456f069adb8ff7d33c633678730d6ae961ed350b27733
+"d3-hierarchy@npm:^1.1.9":
+  version: 1.1.9
+  resolution: "d3-hierarchy@npm:1.1.9"
+  checksum: 10/d2a4e518c3e35de15e0ac7f0a420300a658435850fc32eaf15b7811697e34565b89012e07e60a53babb723dc43df8981102079b4097b790b42ac21c8d23c0424
   languageName: node
   linkType: hard
 
@@ -8957,6 +8830,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"d3-path@npm:1":
+  version: 1.0.9
+  resolution: "d3-path@npm:1.0.9"
+  checksum: 10/6ce1747837ea2a449d9ea32e169a382978ab09a4805eb408feb6bbc12cb5f5f6ce29aefc252dd9a815d420f4813d672f75578b78b3bbaf7811f54d8c7f93fd11
+  languageName: node
+  linkType: hard
+
 "d3-path@npm:^3.1.0":
   version: 3.1.0
   resolution: "d3-path@npm:3.1.0"
@@ -8984,7 +8864,16 @@ __metadata:
   languageName: node
   linkType: hard
 
-"d3-shape@npm:^3.1.0, d3-shape@npm:^3.2.0":
+"d3-shape@npm:^1.3.7":
+  version: 1.3.7
+  resolution: "d3-shape@npm:1.3.7"
+  dependencies:
+    d3-path: "npm:1"
+  checksum: 10/1e40fdcfdc8edc9c53a77a6aaea2dbf31bf06df12ebd66cc8d91f76bbde753049ad21dfee0577f7dc5d0a4468554ede4783f6df7d809e291745334dba977c09e
+  languageName: node
+  linkType: hard
+
+"d3-shape@npm:^3.1.0":
   version: 3.2.0
   resolution: "d3-shape@npm:3.2.0"
   dependencies:
@@ -9060,19 +8949,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"date-fns@npm:^2.30.0":
-  version: 2.30.0
-  resolution: "date-fns@npm:2.30.0"
-  dependencies:
-    "@babel/runtime": "npm:^7.21.0"
-  checksum: 10/70b3e8ea7aaaaeaa2cd80bd889622a4bcb5d8028b4de9162cbcda359db06e16ff6e9309e54eead5341e71031818497f19aaf9839c87d1aba1e27bb4796e758a9
-  languageName: node
-  linkType: hard
-
-"dayjs@npm:1.11.12":
-  version: 1.11.12
-  resolution: "dayjs@npm:1.11.12"
-  checksum: 10/8ee7c1e14961fd08d40b788d0c0e930dc6288b3d32911bb911b2fb31bb703c262788164fbe678ee9e50e2a35268d667b8c8ba43fd1806771c1f404c300a2b428
+"date-fns@npm:^4.1.0":
+  version: 4.1.0
+  resolution: "date-fns@npm:4.1.0"
+  checksum: 10/d5f6e9de5bbc52310f786099e18609289ed5e30af60a71e0646784c8185ddd1d0eebcf7c96b7faaaefc4a8366f3a3a4244d099b6d0866ee2bec80d1361e64342
   languageName: node
   linkType: hard
 
@@ -9206,6 +9086,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"define-data-property@npm:^1.1.4":
+  version: 1.1.4
+  resolution: "define-data-property@npm:1.1.4"
+  dependencies:
+    es-define-property: "npm:^1.0.0"
+    es-errors: "npm:^1.3.0"
+    gopd: "npm:^1.0.1"
+  checksum: 10/abdcb2505d80a53524ba871273e5da75e77e52af9e15b3aa65d8aad82b8a3a424dad7aee2cc0b71470ac7acf501e08defac362e8b6a73cdb4309f028061df4ae
+  languageName: node
+  linkType: hard
+
 "delayed-stream@npm:~1.0.0":
   version: 1.0.0
   resolution: "delayed-stream@npm:1.0.0"
@@ -9234,7 +9125,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"dequal@npm:^2.0.0":
+"dequal@npm:^2.0.0, dequal@npm:^2.0.2":
   version: 2.0.3
   resolution: "dequal@npm:2.0.3"
   checksum: 10/6ff05a7561f33603df87c45e389c9ac0a95e3c056be3da1a0c4702149e3a7f6fe5ffbb294478687ba51a9e95f3a60e8b6b9005993acd79c292c7d15f71964b6b
@@ -9299,6 +9190,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"dom-helpers@npm:^3.3.1":
+  version: 3.4.0
+  resolution: "dom-helpers@npm:3.4.0"
+  dependencies:
+    "@babel/runtime": "npm:^7.1.2"
+  checksum: 10/63ce62469be8cb481c41811f6b463a1b071dce8a7cd73a39cd4fdaa1e570201af6e7a895a502f4db414e2cc0533e44096ad95c13f303e5a3c3a036f0a43798e4
+  languageName: node
+  linkType: hard
+
 "dom-helpers@npm:^5.0.1":
   version: 5.2.1
   resolution: "dom-helpers@npm:5.2.1"
@@ -9378,27 +9278,29 @@ __metadata:
   languageName: node
   linkType: hard
 
-"drizzle-kit@npm:0.23.0":
-  version: 0.23.0
-  resolution: "drizzle-kit@npm:0.23.0"
+"drizzle-kit@npm:0.26.2":
+  version: 0.26.2
+  resolution: "drizzle-kit@npm:0.26.2"
   dependencies:
+    "@drizzle-team/brocli": "npm:^0.10.1"
     "@esbuild-kit/esm-loader": "npm:^2.5.5"
     esbuild: "npm:^0.19.7"
     esbuild-register: "npm:^3.5.0"
   bin:
     drizzle-kit: bin.cjs
-  checksum: 10/e1e2403e1deed9dd9bfd96e100860c658b44d11ea50659e21c328640f22e6ec89a05345a09af8d94913a526a23693aa244d0964bdd30658bdb2ec6ec4998128d
+  checksum: 10/6d499a9a55761efa42c37e1416905a8c53602784ee92526af226060fc96736dc758c56cc2327432e7892278015f342d1da0eb814a0336f0f4ea85a836ab6c82e
   languageName: node
   linkType: hard
 
-"drizzle-orm@npm:0.32.1":
-  version: 0.32.1
-  resolution: "drizzle-orm@npm:0.32.1"
+"drizzle-orm@npm:0.35.3":
+  version: 0.35.3
+  resolution: "drizzle-orm@npm:0.35.3"
   peerDependencies:
     "@aws-sdk/client-rds-data": ">=3"
     "@cloudflare/workers-types": ">=3"
     "@electric-sql/pglite": ">=0.1.1"
-    "@libsql/client": "*"
+    "@libsql/client": ">=0.10.0"
+    "@libsql/client-wasm": ">=0.10.0"
     "@neondatabase/serverless": ">=0.1"
     "@op-engineering/op-sqlite": ">=2"
     "@opentelemetry/api": ^1.4.1
@@ -9479,7 +9381,7 @@ __metadata:
       optional: true
     sqlite3:
       optional: true
-  checksum: 10/a17a0bf757b67606d44fd938e89670b1595a75258a7685e642e428a64a0e6c867322da327312ff68b1f2439b1a4059cff8588108b369328309738910cc2c7697
+  checksum: 10/d91b5bb85983508e838e12f75b5463ca87b4dad31e55026dfeeeba9153226d86eb7743b8cedc0a7c8e609e2930f4b7ffff426416c4a939dd0f0e6b6c581b39f0
   languageName: node
   linkType: hard
 
@@ -9541,17 +9443,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ejs@npm:^3.1.9":
-  version: 3.1.9
-  resolution: "ejs@npm:3.1.9"
-  dependencies:
-    jake: "npm:^10.8.5"
-  bin:
-    ejs: bin/cli.js
-  checksum: 10/71f56d37540d2c2d71701f0116710c676f75314a3e997ef8b83515d5d4d2b111c5a72725377caeecb928671bacb84a0d38135f345904812e989847057d59f21a
-  languageName: node
-  linkType: hard
-
 "electron-to-chromium@npm:^1.4.284":
   version: 1.4.356
   resolution: "electron-to-chromium@npm:1.4.356"
@@ -9573,12 +9464,19 @@ __metadata:
   languageName: node
   linkType: hard
 
-"embla-carousel-autoplay@npm:8.1.7":
-  version: 8.1.7
-  resolution: "embla-carousel-autoplay@npm:8.1.7"
+"electron-to-chromium@npm:^1.5.41":
+  version: 1.5.42
+  resolution: "electron-to-chromium@npm:1.5.42"
+  checksum: 10/869d4813723980a3566b45b0550a99cf46467db3464e9b45b3aad116989faf36bf62c8e73cf25956435fb814e9f8b6bcace4c17f6ecc315df87fbfb5518664ff
+  languageName: node
+  linkType: hard
+
+"embla-carousel-autoplay@npm:8.3.0":
+  version: 8.3.0
+  resolution: "embla-carousel-autoplay@npm:8.3.0"
   peerDependencies:
-    embla-carousel: 8.1.7
-  checksum: 10/8abc97d3405a55377813f247ccc040db94e6994a36b69a0a4c8017973376b4ed056d5501f085af0b051b815b0d55e7f9a360e7fa53e21c2919eba194976f6833
+    embla-carousel: 8.3.0
+  checksum: 10/1e891db0d341bbe31d7315d53b3e96d970a1df632db98d9bcaf5b44829311d592fb742afa09f9ece1a7fd898f440a8da13161e0974451a344bbeb265a8e80f05
   languageName: node
   linkType: hard
 
@@ -9593,24 +9491,24 @@ __metadata:
   languageName: node
   linkType: hard
 
-"embla-carousel-react@npm:8.1.7":
-  version: 8.1.7
-  resolution: "embla-carousel-react@npm:8.1.7"
+"embla-carousel-react@npm:8.3.0":
+  version: 8.3.0
+  resolution: "embla-carousel-react@npm:8.3.0"
   dependencies:
-    embla-carousel: "npm:8.1.7"
-    embla-carousel-reactive-utils: "npm:8.1.7"
+    embla-carousel: "npm:8.3.0"
+    embla-carousel-reactive-utils: "npm:8.3.0"
   peerDependencies:
     react: ^16.8.0 || ^17.0.1 || ^18.0.0
-  checksum: 10/623a903e04d4707dabb8e7ce18b326be610b0394eb9237a78c738477c945f32fee4b5a3681289227080ace467a037ea5fabdae2f36cc8c02a9e5afc0e421308a
+  checksum: 10/48b71504208741d051f09d26ec7925c4470c0c7b9dce1275079d1ab1a32f7892456b953df2836f1be7555061aaac595509d78ab12c0e1370dd7f77df682639f6
   languageName: node
   linkType: hard
 
-"embla-carousel-reactive-utils@npm:8.1.7":
-  version: 8.1.7
-  resolution: "embla-carousel-reactive-utils@npm:8.1.7"
+"embla-carousel-reactive-utils@npm:8.3.0":
+  version: 8.3.0
+  resolution: "embla-carousel-reactive-utils@npm:8.3.0"
   peerDependencies:
-    embla-carousel: 8.1.7
-  checksum: 10/ad6b24dca37e7918dae32cc490e2fdcfa2ccbbc9f3059c7c76fd3b0ce8e5e2d6e9aa5fcd5338a20cf6908ae7dffeeec7547c798acda8bb7011ddfbb148a6f965
+    embla-carousel: 8.3.0
+  checksum: 10/8d1db9a65455dbbee4de7e163c470b2a7552dee13153e95c8754d37389e29dbd03b9a7b11018c84ed59b4db616846cf39de8ea828f50945ab9ea1e99fe9e7289
   languageName: node
   linkType: hard
 
@@ -9621,10 +9519,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"embla-carousel@npm:8.1.7":
-  version: 8.1.7
-  resolution: "embla-carousel@npm:8.1.7"
-  checksum: 10/9721d494c35a576254eb69d3d6cc6dea320a335ffd45dd1c71be54af29e5d2db616777d3808affad209cc4c5a1e488a0f2fc641ef8df83ea0f28c9720bf2c464
+"embla-carousel@npm:8.3.0":
+  version: 8.3.0
+  resolution: "embla-carousel@npm:8.3.0"
+  checksum: 10/939db8fbe604f1d46a1c3150bf8be7f5a566924e426a02762c0c97dcf474973fdb128a113d96a57e953753657fb69298f31247e7dfbc4d820fbb691c54502249
   languageName: node
   linkType: hard
 
@@ -9649,6 +9547,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"encodeurl@npm:~2.0.0":
+  version: 2.0.0
+  resolution: "encodeurl@npm:2.0.0"
+  checksum: 10/abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe
+  languageName: node
+  linkType: hard
+
 "encoding@npm:^0.1.13":
   version: 0.1.13
   resolution: "encoding@npm:0.1.13"
@@ -9705,7 +9610,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"enhanced-resolve@npm:^5.17.0":
+"enhanced-resolve@npm:^5.17.1":
   version: 5.17.1
   resolution: "enhanced-resolve@npm:5.17.1"
   dependencies:
@@ -9756,6 +9661,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"es-define-property@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "es-define-property@npm:1.0.0"
+  dependencies:
+    get-intrinsic: "npm:^1.2.4"
+  checksum: 10/f66ece0a887b6dca71848fa71f70461357c0e4e7249696f81bad0a1f347eed7b31262af4a29f5d726dc026426f085483b6b90301855e647aa8e21936f07293c6
+  languageName: node
+  linkType: hard
+
+"es-errors@npm:^1.3.0":
+  version: 1.3.0
+  resolution: "es-errors@npm:1.3.0"
+  checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5
+  languageName: node
+  linkType: hard
+
 "es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.4.1":
   version: 1.5.4
   resolution: "es-module-lexer@npm:1.5.4"
@@ -10028,7 +9949,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"esbuild@npm:^0.19.3, esbuild@npm:^0.19.7":
+"esbuild@npm:^0.19.7":
   version: 0.19.12
   resolution: "esbuild@npm:0.19.12"
   dependencies:
@@ -10202,6 +10123,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"escalade@npm:^3.2.0":
+  version: 3.2.0
+  resolution: "escalade@npm:3.2.0"
+  checksum: 10/9d7169e3965b2f9ae46971afa392f6e5a25545ea30f2e2dd99c9b0a95a3f52b5653681a84f5b2911a413ddad2d7a93d3514165072f349b5ffc59c75a899970d6
+  languageName: node
+  linkType: hard
+
 "escape-html@npm:~1.0.3":
   version: 1.0.3
   resolution: "escape-html@npm:1.0.3"
@@ -10431,42 +10359,42 @@ __metadata:
   languageName: node
   linkType: hard
 
-"express@npm:^4.19.2":
-  version: 4.19.2
-  resolution: "express@npm:4.19.2"
+"express@npm:^4.20.0":
+  version: 4.21.1
+  resolution: "express@npm:4.21.1"
   dependencies:
     accepts: "npm:~1.3.8"
     array-flatten: "npm:1.1.1"
-    body-parser: "npm:1.20.2"
+    body-parser: "npm:1.20.3"
     content-disposition: "npm:0.5.4"
     content-type: "npm:~1.0.4"
-    cookie: "npm:0.6.0"
+    cookie: "npm:0.7.1"
     cookie-signature: "npm:1.0.6"
     debug: "npm:2.6.9"
     depd: "npm:2.0.0"
-    encodeurl: "npm:~1.0.2"
+    encodeurl: "npm:~2.0.0"
     escape-html: "npm:~1.0.3"
     etag: "npm:~1.8.1"
-    finalhandler: "npm:1.2.0"
+    finalhandler: "npm:1.3.1"
     fresh: "npm:0.5.2"
     http-errors: "npm:2.0.0"
-    merge-descriptors: "npm:1.0.1"
+    merge-descriptors: "npm:1.0.3"
     methods: "npm:~1.1.2"
     on-finished: "npm:2.4.1"
     parseurl: "npm:~1.3.3"
-    path-to-regexp: "npm:0.1.7"
+    path-to-regexp: "npm:0.1.10"
     proxy-addr: "npm:~2.0.7"
-    qs: "npm:6.11.0"
+    qs: "npm:6.13.0"
     range-parser: "npm:~1.2.1"
     safe-buffer: "npm:5.2.1"
-    send: "npm:0.18.0"
-    serve-static: "npm:1.15.0"
+    send: "npm:0.19.0"
+    serve-static: "npm:1.16.2"
     setprototypeof: "npm:1.2.0"
     statuses: "npm:2.0.1"
     type-is: "npm:~1.6.18"
     utils-merge: "npm:1.0.1"
     vary: "npm:~1.1.2"
-  checksum: 10/3fcd792536f802c059789ef48db3851b87e78fba103423e524144d79af37da7952a2b8d4e1a007f423329c7377d686d9476ac42e7d9ea413b80345d495e30a3a
+  checksum: 10/5d4a36dd03c1d1cce93172e9b185b5cd13a978d29ee03adc51cd278be7b4a514ae2b63e2fdaec0c00fdc95c6cfb396d9dd1da147917ffd337d6cd0778e08c9bc
   languageName: node
   linkType: hard
 
@@ -10654,10 +10582,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"filesize@npm:10.1.4":
-  version: 10.1.4
-  resolution: "filesize@npm:10.1.4"
-  checksum: 10/ac2b95f4ee8d42ad4b12f8f918baeb1127065dcb319abca30c0d9ef115b602e31a06c8150953b13dc52e52ebb1238e18e6001ab5fca14a10957e788bd6012f1c
+"filesize@npm:10.1.6":
+  version: 10.1.6
+  resolution: "filesize@npm:10.1.6"
+  checksum: 10/e800837c4fc02303f1944d5a4c7b706df1c5cd95d745181852604fb00a1c2d55d2d3921252722bd2f0c86b59c94edaba23fa224776bbf977455d4034e7be1f45
   languageName: node
   linkType: hard
 
@@ -10670,18 +10598,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"finalhandler@npm:1.2.0":
-  version: 1.2.0
-  resolution: "finalhandler@npm:1.2.0"
+"finalhandler@npm:1.3.1":
+  version: 1.3.1
+  resolution: "finalhandler@npm:1.3.1"
   dependencies:
     debug: "npm:2.6.9"
-    encodeurl: "npm:~1.0.2"
+    encodeurl: "npm:~2.0.0"
     escape-html: "npm:~1.0.3"
     on-finished: "npm:2.4.1"
     parseurl: "npm:~1.3.3"
     statuses: "npm:2.0.1"
     unpipe: "npm:~1.0.0"
-  checksum: 10/635718cb203c6d18e6b48dfbb6c54ccb08ea470e4f474ddcef38c47edcf3227feec316f886dd701235997d8af35240cae49856721ce18f539ad038665ebbf163
+  checksum: 10/4babe72969b7373b5842bc9f75c3a641a4d0f8eb53af6b89fa714d4460ce03fb92b28de751d12ba415e96e7e02870c436d67412120555e2b382640535697305b
   languageName: node
   linkType: hard
 
@@ -10970,6 +10898,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"get-intrinsic@npm:^1.2.4":
+  version: 1.2.4
+  resolution: "get-intrinsic@npm:1.2.4"
+  dependencies:
+    es-errors: "npm:^1.3.0"
+    function-bind: "npm:^1.1.2"
+    has-proto: "npm:^1.0.1"
+    has-symbols: "npm:^1.0.3"
+    hasown: "npm:^2.0.0"
+  checksum: 10/85bbf4b234c3940edf8a41f4ecbd4e25ce78e5e6ad4e24ca2f77037d983b9ef943fd72f00f3ee97a49ec622a506b67db49c36246150377efcda1c9eb03e5f06d
+  languageName: node
+  linkType: hard
+
 "get-nonce@npm:^1.0.0":
   version: 1.0.1
   resolution: "get-nonce@npm:1.0.1"
@@ -11151,9 +11092,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"graphql-config@npm:^5.0.2":
-  version: 5.0.2
-  resolution: "graphql-config@npm:5.0.2"
+"graphql-config@npm:^5.1.1":
+  version: 5.1.3
+  resolution: "graphql-config@npm:5.1.3"
   dependencies:
     "@graphql-tools/graphql-file-loader": "npm:^8.0.0"
     "@graphql-tools/json-file-loader": "npm:^8.0.0"
@@ -11162,8 +11103,8 @@ __metadata:
     "@graphql-tools/url-loader": "npm:^8.0.0"
     "@graphql-tools/utils": "npm:^10.0.0"
     cosmiconfig: "npm:^8.1.0"
-    jiti: "npm:^1.18.2"
-    minimatch: "npm:^4.2.3"
+    jiti: "npm:^2.0.0"
+    minimatch: "npm:^9.0.5"
     string-env-interpolation: "npm:^1.0.1"
     tslib: "npm:^2.4.0"
   peerDependencies:
@@ -11172,7 +11113,7 @@ __metadata:
   peerDependenciesMeta:
     cosmiconfig-toml-loader:
       optional: true
-  checksum: 10/668f177e8c36e482b246ec84e10e0e9d7f29e51b519828d22866c724500cf2cc075a07e4d5d5f057df9eaa189550d485072d3814b15ff0b383367e0fa92f8bbf
+  checksum: 10/9d37f5d424f302808102d118988878be5e4841ba1a06a865cdb9052b24e26eaa9923fb18163bf4f32102d87b3895c53e2ffcdebc1d651f04b56f93f5c38b83c3
   languageName: node
   linkType: hard
 
@@ -11286,6 +11227,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"has-property-descriptors@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "has-property-descriptors@npm:1.0.2"
+  dependencies:
+    es-define-property: "npm:^1.0.0"
+  checksum: 10/2d8c9ab8cebb572e3362f7d06139a4592105983d4317e68f7adba320fe6ddfc8874581e0971e899e633fd5f72e262830edce36d5a0bc863dad17ad20572484b2
+  languageName: node
+  linkType: hard
+
 "has-proto@npm:^1.0.1":
   version: 1.0.1
   resolution: "has-proto@npm:1.0.1"
@@ -11374,15 +11324,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
-  version: 3.3.2
-  resolution: "hoist-non-react-statics@npm:3.3.2"
-  dependencies:
-    react-is: "npm:^16.7.0"
-  checksum: 10/1acbe85f33e5a39f90c822ad4d28b24daeb60f71c545279431dc98c312cd28a54f8d64788e477fe21dc502b0e3cf58589ebe5c1ad22af27245370391c2d24ea6
-  languageName: node
-  linkType: hard
-
 "hosted-git-info@npm:^2.1.4":
   version: 2.8.9
   resolution: "hosted-git-info@npm:2.8.9"
@@ -12028,13 +11969,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"isbot@npm:4.1.0":
-  version: 4.1.0
-  resolution: "isbot@npm:4.1.0"
-  checksum: 10/ee76a2c937e701dbc47043fe71ab887dd3e7a5ba7c61f12bb7053b5634c1e3d413325f1adee3bc615cb8b75b9a95e9c79f36c66f11809cc4108488cb8e21d1dc
-  languageName: node
-  linkType: hard
-
 "isbot@npm:5.1.17":
   version: 5.1.17
   resolution: "isbot@npm:5.1.17"
@@ -12125,15 +12059,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jiti@npm:^1.18.2":
-  version: 1.19.1
-  resolution: "jiti@npm:1.19.1"
-  bin:
-    jiti: bin/jiti.js
-  checksum: 10/88f11b406a877db348b956eb9cecf42d6cb6edcd6317aa75c032130fc88c1150baf8def5511e808af10b11336db2639a295ffe1ed455d65216dcef45a0a424b5
-  languageName: node
-  linkType: hard
-
 "jiti@npm:^1.19.1, jiti@npm:^1.21.0":
   version: 1.21.6
   resolution: "jiti@npm:1.21.6"
@@ -12143,6 +12068,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jiti@npm:^2.0.0":
+  version: 2.3.3
+  resolution: "jiti@npm:2.3.3"
+  bin:
+    jiti: lib/jiti-cli.mjs
+  checksum: 10/21d1e89d909101c702769537e75ad87b433f980bc6938a6f64a90d1fbe7cb1510a0d4b82d4020b3093b47cebff5568af5e39d883e26aa4413564ced43b8cfd84
+  languageName: node
+  linkType: hard
+
 "jose@npm:^4.11.4":
   version: 4.14.0
   resolution: "jose@npm:4.14.0"
@@ -12157,9 +12091,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jotai@npm:2.9.3":
-  version: 2.9.3
-  resolution: "jotai@npm:2.9.3"
+"jotai@npm:2.10.1":
+  version: 2.10.1
+  resolution: "jotai@npm:2.10.1"
   peerDependencies:
     "@types/react": ">=17.0.0"
     react: ">=17.0.0"
@@ -12168,7 +12102,7 @@ __metadata:
       optional: true
     react:
       optional: true
-  checksum: 10/4366a50be8edd39f1430ac655677e559cfbb253101e4f2e750bca8ebd81e75ea09ea3828bce727482b15305ffe924dda75bd9a055e903e0cc804b6600478dc36
+  checksum: 10/6d01b3daa88d9edbce06776fb70921f7341c5bd5787cd3d3dd91a820760838ebe5abafe7563f0d04957a1d863b557999260ad33d225ce1d54e5fdd9a1a93af0e
   languageName: node
   linkType: hard
 
@@ -12544,7 +12478,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
+"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0":
   version: 1.4.0
   resolution: "loose-envify@npm:1.4.0"
   dependencies:
@@ -12612,12 +12546,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lucide-react@npm:0.414.0":
-  version: 0.414.0
-  resolution: "lucide-react@npm:0.414.0"
+"lucide-react@npm:0.453.0":
+  version: 0.453.0
+  resolution: "lucide-react@npm:0.453.0"
   peerDependencies:
-    react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
-  checksum: 10/d2524481f14f690e15add2a517d5bcda1432029ae212e82fa8e8624bec0501f8131514d0a03519a60d9d543252f33d8ab0e7a592dee365ec9cc49fe403d5ff1b
+    react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
+  checksum: 10/b49c486ce04ebe3650d756aab0584eb96d4b3c2381ab1185ebedbd00333cfafcfe71766f918bc5ea7538bab0339804363afa5503acb416ee2208af9d31a9fac1
   languageName: node
   linkType: hard
 
@@ -12664,15 +12598,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"mantine-datatable@npm:7.11.3":
-  version: 7.11.3
-  resolution: "mantine-datatable@npm:7.11.3"
+"mantine-datatable@npm:7.12.4":
+  version: 7.12.4
+  resolution: "mantine-datatable@npm:7.12.4"
   peerDependencies:
-    "@mantine/core": ">=7.8"
-    "@mantine/hooks": ">=7.8"
+    "@mantine/core": ">=7.11"
+    "@mantine/hooks": ">=7.11"
     clsx: ">=2"
     react: ">=18.2"
-  checksum: 10/8fe7c5745ed3ec8278ca52d19b0826d5437d32e5973d2f11d5fb05152e1a8fe47decf8010a9ba3ef1e47f8784742044bd06b4059c8ddb787f141b98ecfbea104
+  checksum: 10/01ea1fb8425ee36eee35db73ec41a8b1f2ecb57e5ff039e1fdc0f03d8cd2d7ccd047b9a37ec3821d5b8c4b3787f6b8599788a61cba06109cccdf1126b1213950
   languageName: node
   linkType: hard
 
@@ -12919,10 +12853,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"merge-descriptors@npm:1.0.1":
-  version: 1.0.1
-  resolution: "merge-descriptors@npm:1.0.1"
-  checksum: 10/5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26
+"merge-descriptors@npm:1.0.3":
+  version: 1.0.3
+  resolution: "merge-descriptors@npm:1.0.3"
+  checksum: 10/52117adbe0313d5defa771c9993fe081e2d2df9b840597e966aadafde04ae8d0e3da46bac7ca4efc37d4d2b839436582659cd49c6a43eacb3fe3050896a105d1
   languageName: node
   linkType: hard
 
@@ -13373,21 +13307,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
-  version: 3.1.2
-  resolution: "minimatch@npm:3.1.2"
-  dependencies:
-    brace-expansion: "npm:^1.1.7"
-  checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634
-  languageName: node
-  linkType: hard
-
-"minimatch@npm:^4.2.3":
-  version: 4.2.3
-  resolution: "minimatch@npm:4.2.3"
+"minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
+  version: 3.1.2
+  resolution: "minimatch@npm:3.1.2"
   dependencies:
     brace-expansion: "npm:^1.1.7"
-  checksum: 10/02bacb187448c6aeeed5a794a64799fb1d1dd0274694da97272a9d3b919c7dfba6987d2089f6465191450d36d767c47fd7958227610b919121ccaed7de7f8924
+  checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634
   languageName: node
   linkType: hard
 
@@ -13409,7 +13334,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"minimatch@npm:^9.0.4":
+"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5":
   version: 9.0.5
   resolution: "minimatch@npm:9.0.5"
   dependencies:
@@ -13824,6 +13749,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"node-releases@npm:^2.0.18":
+  version: 2.0.18
+  resolution: "node-releases@npm:2.0.18"
+  checksum: 10/241e5fa9556f1c12bafb83c6c3e94f8cf3d8f2f8f904906ecef6e10bcaa1d59aa61212d4651bec70052015fc54bd3fdcdbe7fc0f638a17e6685aa586c076ec4e
+  languageName: node
+  linkType: hard
+
 "node-releases@npm:^2.0.8":
   version: 2.0.10
   resolution: "node-releases@npm:2.0.10"
@@ -13831,10 +13763,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"nodemailer@npm:6.9.14":
-  version: 6.9.14
-  resolution: "nodemailer@npm:6.9.14"
-  checksum: 10/749d1a3ef440d6147c37ad850f5be065d55d87cd46a4470372d4e443593838a5ddd78c69623817a804ddede3212fa28ac069b5b8266e892b27e1dcff75103def
+"nodemailer@npm:6.9.15":
+  version: 6.9.15
+  resolution: "nodemailer@npm:6.9.15"
+  checksum: 10/16079406fe603f6e4debb8ac34bdb919688ad169ddf61e7b10e56abef85b537762eb3a19a15ce32bb9446d68e2f93a1ba4e24836ba86c7363fc42145aa36a291
   languageName: node
   linkType: hard
 
@@ -14008,10 +13940,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"object-inspect@npm:^1.9.0":
-  version: 1.12.3
-  resolution: "object-inspect@npm:1.12.3"
-  checksum: 10/532b0036f0472f561180fac0d04fe328ee01f57637624c83fb054f81b5bfe966cdf4200612a499ed391a7ca3c46b20a0bc3a55fc8241d944abe687c556a32b39
+"object-inspect@npm:^1.13.1":
+  version: 1.13.2
+  resolution: "object-inspect@npm:1.13.2"
+  checksum: 10/7ef65583b6397570a17c56f0c1841e0920e83900f2c94638927abb7b81ac08a19c7aae135bd9dcca96208cac0c7332b4650fb927f027b0cf92d71df2990d0561
   languageName: node
   linkType: hard
 
@@ -14354,10 +14286,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"path-to-regexp@npm:0.1.7":
-  version: 0.1.7
-  resolution: "path-to-regexp@npm:0.1.7"
-  checksum: 10/701c99e1f08e3400bea4d701cf6f03517474bb1b608da71c78b1eb261415b645c5670dfae49808c89e12cea2dccd113b069f040a80de012da0400191c6dbd1c8
+"path-to-regexp@npm:0.1.10":
+  version: 0.1.10
+  resolution: "path-to-regexp@npm:0.1.10"
+  checksum: 10/894e31f1b20e592732a87db61fff5b95c892a3fe430f9ab18455ebe69ee88ef86f8eb49912e261f9926fc53da9f93b46521523e33aefd9cb0a7b0d85d7096006
   languageName: node
   linkType: hard
 
@@ -14425,6 +14357,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"picocolors@npm:^1.1.0":
+  version: 1.1.1
+  resolution: "picocolors@npm:1.1.1"
+  checksum: 10/e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045
+  languageName: node
+  linkType: hard
+
 "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1":
   version: 2.3.1
   resolution: "picomatch@npm:2.3.1"
@@ -14723,14 +14662,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss@npm:8.4.41, postcss@npm:^8.4.41":
-  version: 8.4.41
-  resolution: "postcss@npm:8.4.41"
+"postcss@npm:8.4.47, postcss@npm:^8.4.43":
+  version: 8.4.47
+  resolution: "postcss@npm:8.4.47"
   dependencies:
     nanoid: "npm:^3.3.7"
-    picocolors: "npm:^1.0.1"
-    source-map-js: "npm:^1.2.0"
-  checksum: 10/6e6176c2407eff60493ca60a706c6b7def20a722c3adda94ea1ece38345eb99964191336fd62b62652279cec6938e79e0b1e1d477142c8d3516e7a725a74ee37
+    picocolors: "npm:^1.1.0"
+    source-map-js: "npm:^1.2.1"
+  checksum: 10/f2b50ba9b6fcb795232b6bb20de7cdc538c0025989a8ed9c4438d1960196ba3b7eaff41fdb1a5c701b3504651ea87aeb685577707f0ae4d6ce6f3eae5df79a81
   languageName: node
   linkType: hard
 
@@ -14840,7 +14779,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
+"prop-types@npm:^15.5.6, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
   version: 15.8.1
   resolution: "prop-types@npm:15.8.1"
   dependencies:
@@ -14934,28 +14873,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"pvtsutils@npm:^1.3.2":
-  version: 1.3.2
-  resolution: "pvtsutils@npm:1.3.2"
-  dependencies:
-    tslib: "npm:^2.4.0"
-  checksum: 10/3e89fea1836dd9027446d65923f7240372a1040b777b2e6adfc319bfeb3cacfd56dccb708652651e85ad6a5c87f61728b697226c105d441140b648f3e4167872
-  languageName: node
-  linkType: hard
-
-"pvutils@npm:^1.1.3":
-  version: 1.1.3
-  resolution: "pvutils@npm:1.1.3"
-  checksum: 10/e5201b8f78ece68eae414a938c844bc45fb3f0de298178eed1775a217eedfd897c4346e5e54f410bb4d7466e09ceb262e85f20fd64239b8bb2595f14c52fa95e
-  languageName: node
-  linkType: hard
-
-"qs@npm:6.11.0":
-  version: 6.11.0
-  resolution: "qs@npm:6.11.0"
+"qs@npm:6.13.0":
+  version: 6.13.0
+  resolution: "qs@npm:6.13.0"
   dependencies:
-    side-channel: "npm:^1.0.4"
-  checksum: 10/5a3bfea3e2f359ede1bfa5d2f0dbe54001aa55e40e27dc3e60fab814362d83a9b30758db057c2011b6f53a2d4e4e5150194b5bac45372652aecb3e3c0d4b256e
+    side-channel: "npm:^1.0.6"
+  checksum: 10/f548b376e685553d12e461409f0d6e5c59ec7c7d76f308e2a888fd9db3e0c5e89902bedd0754db3a9038eda5f27da2331a6f019c8517dc5e0a16b3c9a6e9cef8
   languageName: node
   linkType: hard
 
@@ -15018,6 +14941,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-d3-tree@npm:^3.6.2":
+  version: 3.6.2
+  resolution: "react-d3-tree@npm:3.6.2"
+  dependencies:
+    "@bkrem/react-transition-group": "npm:^1.3.3"
+    "@types/d3-hierarchy": "npm:^1.1.8"
+    clone: "npm:^2.1.1"
+    d3-hierarchy: "npm:^1.1.9"
+    d3-selection: "npm:^3.0.0"
+    d3-shape: "npm:^1.3.7"
+    d3-zoom: "npm:^3.0.0"
+    dequal: "npm:^2.0.2"
+    uuid: "npm:^8.3.1"
+  peerDependencies:
+    react: 16.x || 17.x || 18.x
+    react-dom: 16.x || 17.x || 18.x
+  checksum: 10/cb8b9e8ea4a17561c867733c4dbc3ee0d51593a7b9777f2100ef9574ddeaebc7ea2b29f17ab69d7b699883dc443f7d93e2b42af62c739f750c679e8f1322750d
+  languageName: node
+  linkType: hard
+
 "react-diff-viewer-continued@npm:^3.3.1":
   version: 3.4.0
   resolution: "react-diff-viewer-continued@npm:3.4.0"
@@ -15034,18 +14977,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-dom@npm:18.2.0":
-  version: 18.2.0
-  resolution: "react-dom@npm:18.2.0"
-  dependencies:
-    loose-envify: "npm:^1.1.0"
-    scheduler: "npm:^0.23.0"
-  peerDependencies:
-    react: ^18.2.0
-  checksum: 10/ca5e7762ec8c17a472a3605b6f111895c9f87ac7d43a610ab7024f68cd833d08eda0625ce02ec7178cc1f3c957cf0b9273cdc17aa2cd02da87544331c43b1d21
-  languageName: node
-  linkType: hard
-
 "react-dom@npm:18.3.1, react-dom@npm:^18.2.0":
   version: 18.3.1
   resolution: "react-dom@npm:18.3.1"
@@ -15118,17 +15049,24 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-is@npm:^16.10.2, react-is@npm:^16.13.1, react-is@npm:^16.7.0":
+"react-is@npm:^16.13.1":
   version: 16.13.1
   resolution: "react-is@npm:16.13.1"
   checksum: 10/5aa564a1cde7d391ac980bedee21202fc90bdea3b399952117f54fb71a932af1e5902020144fb354b4690b2414a0c7aafe798eb617b76a3d441d956db7726fdf
   languageName: node
   linkType: hard
 
-"react-is@npm:^18.0.0":
-  version: 18.2.0
-  resolution: "react-is@npm:18.2.0"
-  checksum: 10/200cd65bf2e0be7ba6055f647091b725a45dd2a6abef03bf2380ce701fd5edccee40b49b9d15edab7ac08a762bf83cb4081e31ec2673a5bfb549a36ba21570df
+"react-is@npm:^18.3.1":
+  version: 18.3.1
+  resolution: "react-is@npm:18.3.1"
+  checksum: 10/d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22
+  languageName: node
+  linkType: hard
+
+"react-lifecycles-compat@npm:^3.0.4":
+  version: 3.0.4
+  resolution: "react-lifecycles-compat@npm:3.0.4"
+  checksum: 10/c66b9c98c15cd6b0d0a4402df5f665e8cc7562fb7033c34508865bea51fd7b623f7139b5b7e708515d3cd665f264a6a9403e1fa7e6d61a05759066f5e9f07783
   languageName: node
   linkType: hard
 
@@ -15153,35 +15091,22 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-redux@npm:^8.1.3":
-  version: 8.1.3
-  resolution: "react-redux@npm:8.1.3"
+"react-redux@npm:^9.1.2":
+  version: 9.1.2
+  resolution: "react-redux@npm:9.1.2"
   dependencies:
-    "@babel/runtime": "npm:^7.12.1"
-    "@types/hoist-non-react-statics": "npm:^3.3.1"
     "@types/use-sync-external-store": "npm:^0.0.3"
-    hoist-non-react-statics: "npm:^3.3.2"
-    react-is: "npm:^18.0.0"
     use-sync-external-store: "npm:^1.0.0"
   peerDependencies:
-    "@types/react": ^16.8 || ^17.0 || ^18.0
-    "@types/react-dom": ^16.8 || ^17.0 || ^18.0
-    react: ^16.8 || ^17.0 || ^18.0
-    react-dom: ^16.8 || ^17.0 || ^18.0
-    react-native: ">=0.59"
-    redux: ^4 || ^5.0.0-beta.0
+    "@types/react": ^18.2.25
+    react: ^18.0
+    redux: ^5.0.0
   peerDependenciesMeta:
     "@types/react":
       optional: true
-    "@types/react-dom":
-      optional: true
-    react-dom:
-      optional: true
-    react-native:
-      optional: true
     redux:
       optional: true
-  checksum: 10/c4c7586cff3abeb784e73598d330f5301116a4e9942fd36895f2bccd8990001709c6c3ea1817edb75ee477470d6c67c9113e05a7f86b2b68a3950c9c29fe20cb
+  checksum: 10/319b3286f538da7e609ca90fc6762ffae007c5cf75e525a25237ac2feaee63d9cf76fe766817de1fc8f27e7bde825ca409c463037d26dd8e57c435d383f80c50
   languageName: node
   linkType: hard
 
@@ -15262,51 +15187,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-router-dom@npm:6.25.0":
-  version: 6.25.0
-  resolution: "react-router-dom@npm:6.25.0"
-  dependencies:
-    "@remix-run/router": "npm:1.18.0"
-    react-router: "npm:6.25.0"
-  peerDependencies:
-    react: ">=16.8"
-    react-dom: ">=16.8"
-  checksum: 10/4f74050c64980a5acd37b62dad2e877b0e757a641b310cb233e8772277e45d60c2e44a75d14572079fee60d5469002b98b46c569761a0b9b347d083804cb3b7b
-  languageName: node
-  linkType: hard
-
-"react-router-dom@npm:6.26.1":
-  version: 6.26.1
-  resolution: "react-router-dom@npm:6.26.1"
+"react-router-dom@npm:6.27.0":
+  version: 6.27.0
+  resolution: "react-router-dom@npm:6.27.0"
   dependencies:
-    "@remix-run/router": "npm:1.19.1"
-    react-router: "npm:6.26.1"
+    "@remix-run/router": "npm:1.20.0"
+    react-router: "npm:6.27.0"
   peerDependencies:
     react: ">=16.8"
     react-dom: ">=16.8"
-  checksum: 10/1bd255d1ff88f477699c72656e7c07702a907e644388a1bea1c648f2df0c3c86db2e90bea945b1d43eaf84ebab194f3868f3788502965ad5f20c508c6874f1fe
-  languageName: node
-  linkType: hard
-
-"react-router@npm:6.25.0":
-  version: 6.25.0
-  resolution: "react-router@npm:6.25.0"
-  dependencies:
-    "@remix-run/router": "npm:1.18.0"
-  peerDependencies:
-    react: ">=16.8"
-  checksum: 10/7885b90e3abb574b2843b3efefe481ed87c1dba2a17ad301dc0df5e6e93c08c56ccbafc24ee2fc43650e11c1d7d0304762e7c98d43f35e756af76f2587b95b80
+  checksum: 10/cfbcbc1d387d3341a335e3a075e487cc09dcbb62f1b83bc827fc3eec937523d5647a2c4488c804dc61581e65561823d0166d17b5dbc8579998c25b5a0bcabad6
   languageName: node
   linkType: hard
 
-"react-router@npm:6.26.1":
-  version: 6.26.1
-  resolution: "react-router@npm:6.26.1"
+"react-router@npm:6.27.0":
+  version: 6.27.0
+  resolution: "react-router@npm:6.27.0"
   dependencies:
-    "@remix-run/router": "npm:1.19.1"
+    "@remix-run/router": "npm:1.20.0"
   peerDependencies:
     react: ">=16.8"
-  checksum: 10/b3761515c75da65a1678f005d08a6285ceccd9df7237ae6fdd9ab2ab816ef328435b75610f705ecd9ecd41c6878fd22eb9b44c5391cdef2e1ed99ddbc78de8a4
+  checksum: 10/352e3af2075cdccf9d114b7e06d94a1b46a2147ba9d6e8643787a92464f5fd9ead950252a98d551f99f21860288bcf3a4f088cb5f46b28d1274a4e2ba24cc0f9
   languageName: node
   linkType: hard
 
@@ -15369,13 +15270,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-virtuoso@npm:4.10.1":
-  version: 4.10.1
-  resolution: "react-virtuoso@npm:4.10.1"
+"react-use-websocket@npm:^4.8.1":
+  version: 4.10.0
+  resolution: "react-use-websocket@npm:4.10.0"
+  checksum: 10/3f03ae19501961efdcefa3ab81e0f80d868f1b5cfe215ad0e35da589ecb4d9747f20504c7f824ab65c80b5231af959b9994785d2284a6024ed205300a930d277
+  languageName: node
+  linkType: hard
+
+"react-virtuoso@npm:4.12.0":
+  version: 4.12.0
+  resolution: "react-virtuoso@npm:4.12.0"
   peerDependencies:
     react: ">=16 || >=17 || >= 18"
     react-dom: ">=16 || >=17 || >= 18"
-  checksum: 10/63d77e85c84ce1ecb014ba65e0f353bf1fc108df9f2d071431a088a4964b616f2ceb8dac9458db8bcd7015e93817254a587ccaa5f3a0ebf59606cce526c3adf8
+  checksum: 10/f9d899dc7747e475efb793b849de9bac4f71209f3a74238a4d58d7c0c81543cca35179d64029f014fb6ebad2eab18d9a852444a49c8fe7c5177a18f09b9924d4
   languageName: node
   linkType: hard
 
@@ -15389,15 +15297,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react@npm:18.2.0":
-  version: 18.2.0
-  resolution: "react@npm:18.2.0"
-  dependencies:
-    loose-envify: "npm:^1.1.0"
-  checksum: 10/b9214a9bd79e99d08de55f8bef2b7fc8c39630be97c4e29d7be173d14a9a10670b5325e94485f74cd8bff4966ef3c78ee53c79a7b0b9b70cba20aa8973acc694
-  languageName: node
-  linkType: hard
-
 "react@npm:18.3.1, react@npm:^18.2.0":
   version: 18.3.1
   resolution: "react@npm:18.3.1"
@@ -15490,14 +15389,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"recharts@npm:2.12.7":
-  version: 2.12.7
-  resolution: "recharts@npm:2.12.7"
+"recharts@npm:2.13.0":
+  version: 2.13.0
+  resolution: "recharts@npm:2.13.0"
   dependencies:
     clsx: "npm:^2.0.0"
     eventemitter3: "npm:^4.0.1"
     lodash: "npm:^4.17.21"
-    react-is: "npm:^16.10.2"
+    react-is: "npm:^18.3.1"
     react-smooth: "npm:^4.0.0"
     recharts-scale: "npm:^0.4.4"
     tiny-invariant: "npm:^1.3.1"
@@ -15505,7 +15404,7 @@ __metadata:
   peerDependencies:
     react: ^16.0.0 || ^17.0.0 || ^18.0.0
     react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
-  checksum: 10/dd2806978d025abf37ba6eda36fad9361968257c5224286f2881a9f79b18b6045bab520c9c44fd1dc5bb7e35e7ab51d5cde1669e44cb32610e7cb9c49fdd490f
+  checksum: 10/d9adf066282c06b1f1b1fbdc69e119060d4b5738b59abba61702afade366023884086d6223c4530279ac3a34f3d81b4626fb5fec099fdeacd16614cb03a861c9
   languageName: node
   linkType: hard
 
@@ -15519,12 +15418,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"redux@npm:^4.2.1":
-  version: 4.2.1
-  resolution: "redux@npm:4.2.1"
-  dependencies:
-    "@babel/runtime": "npm:^7.9.2"
-  checksum: 10/371e4833b671193303a7dea7803c8fdc8e0d566740c78f580e0a3b77b4161da25037626900a2205a5d616117fa6ad09a4232e5a110bd437186b5c6355a041750
+"redux@npm:^5.0.1":
+  version: 5.0.1
+  resolution: "redux@npm:5.0.1"
+  checksum: 10/a373f9ed65693ead58bea5ef61c1d6bef39da9f2706db3be6f84815f3a1283230ecd1184efb1b3daa7f807d8211b0181564ca8f336fc6ee0b1e2fa0ba06737c2
   languageName: node
   linkType: hard
 
@@ -15624,49 +15521,28 @@ __metadata:
   languageName: node
   linkType: hard
 
-"remix-development-tools@npm:4.4.1":
-  version: 4.4.1
-  resolution: "remix-development-tools@npm:4.4.1"
+"remix-development-tools@npm:4.7.3":
+  version: 4.7.3
+  resolution: "remix-development-tools@npm:4.7.3"
   dependencies:
     "@radix-ui/react-accordion": "npm:^1.1.2"
     "@radix-ui/react-select": "npm:^1.2.2"
     beautify: "npm:^0.0.8"
     chalk: "npm:^5.3.0"
-    clone: "npm:^2.1.2"
     clsx: "npm:^2.0.0"
-    d3-hierarchy: "npm:^3.1.2"
-    d3-selection: "npm:^3.0.0"
-    d3-shape: "npm:^3.2.0"
-    d3-zoom: "npm:^3.0.0"
-    date-fns: "npm:^2.30.0"
+    date-fns: "npm:^4.1.0"
     es-module-lexer: "npm:^1.4.1"
+    react-d3-tree: "npm:^3.6.2"
     react-diff-viewer-continued: "npm:^3.3.1"
     react-hotkeys-hook: "npm:^4.5.0"
+    react-use-websocket: "npm:^4.8.1"
     tailwind-merge: "npm:^1.14.0"
-    uuid: "npm:^9.0.1"
-    zod: "npm:^3.22.4"
   peerDependencies:
     "@remix-run/react": ">=1.15"
     react: ">=17"
     react-dom: ">=17"
     vite: ">=5.0.0"
-  checksum: 10/cd9f8a51d675edefb58ac3651fbcb6636cb317c1466f6c75d1ce6e641e847b2232e1d051cdaf5b66bda2e3736002764318659c4e2cd8534edcd6b0347decfe21
-  languageName: node
-  linkType: hard
-
-"remix-routes@npm:1.7.6":
-  version: 1.7.6
-  resolution: "remix-routes@npm:1.7.6"
-  dependencies:
-    chokidar: "npm:^3.5.2"
-    ejs: "npm:^3.1.9"
-    meow: "npm:9.0.0"
-    mkdirp: "npm:^1.0.4"
-    slash: "npm:3"
-    typescript-remix-routes-plugin: "npm:1.0.1"
-  bin:
-    remix-routes: lib/cli.js
-  checksum: 10/115203419f84a158063b09b07ae80c567b7f6a164cad7a573c3c5f50bd5151e5ce4199175a0fbd50c86a31d1847b5607d43c66e35e7ec3ad337f47f7f1bd12af
+  checksum: 10/c99e9b2072891db09b24a83e59a379b27f65edfd94c80c04f0da8594ce7778b4ea5cccb0ad1c13fc033b45e62fca45ef4dad6e45ff312b255a265548c3c3740c
   languageName: node
   linkType: hard
 
@@ -15685,14 +15561,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"remix-utils@npm:7.6.0":
-  version: 7.6.0
-  resolution: "remix-utils@npm:7.6.0"
+"remix-utils@npm:7.7.0":
+  version: 7.7.0
+  resolution: "remix-utils@npm:7.7.0"
   dependencies:
-    type-fest: "npm:^4.3.3"
+    type-fest: "npm:^4.18.1"
   peerDependencies:
     "@remix-run/cloudflare": ^2.0.0
-    "@remix-run/deno": ^2.0.0
     "@remix-run/node": ^2.0.0
     "@remix-run/react": ^2.0.0
     "@remix-run/router": ^1.7.2
@@ -15704,8 +15579,6 @@ __metadata:
   peerDependenciesMeta:
     "@remix-run/cloudflare":
       optional: true
-    "@remix-run/deno":
-      optional: true
     "@remix-run/node":
       optional: true
     "@remix-run/react":
@@ -15722,7 +15595,7 @@ __metadata:
       optional: true
     zod:
       optional: true
-  checksum: 10/2654ad7e49bc3e9517f05f6cca60a864127748a2e0394323075a84f89a180dee6adb7b68d6c89380b9ed11cf38a2e24b409b1457689f1ed1119b6548f3731a2b
+  checksum: 10/eba6331e68dc5440222f803f6b70e05068a9141f57f5a733b34d2451f1e14f03199dc4d376e46dc2a71e742ad11c4e3283b856ad09a6ea453ca0ce7506453384
   languageName: node
   linkType: hard
 
@@ -15893,69 +15766,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"rollup@npm:^4.2.0":
-  version: 4.19.0
-  resolution: "rollup@npm:4.19.0"
-  dependencies:
-    "@rollup/rollup-android-arm-eabi": "npm:4.19.0"
-    "@rollup/rollup-android-arm64": "npm:4.19.0"
-    "@rollup/rollup-darwin-arm64": "npm:4.19.0"
-    "@rollup/rollup-darwin-x64": "npm:4.19.0"
-    "@rollup/rollup-linux-arm-gnueabihf": "npm:4.19.0"
-    "@rollup/rollup-linux-arm-musleabihf": "npm:4.19.0"
-    "@rollup/rollup-linux-arm64-gnu": "npm:4.19.0"
-    "@rollup/rollup-linux-arm64-musl": "npm:4.19.0"
-    "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.19.0"
-    "@rollup/rollup-linux-riscv64-gnu": "npm:4.19.0"
-    "@rollup/rollup-linux-s390x-gnu": "npm:4.19.0"
-    "@rollup/rollup-linux-x64-gnu": "npm:4.19.0"
-    "@rollup/rollup-linux-x64-musl": "npm:4.19.0"
-    "@rollup/rollup-win32-arm64-msvc": "npm:4.19.0"
-    "@rollup/rollup-win32-ia32-msvc": "npm:4.19.0"
-    "@rollup/rollup-win32-x64-msvc": "npm:4.19.0"
-    "@types/estree": "npm:1.0.5"
-    fsevents: "npm:~2.3.2"
-  dependenciesMeta:
-    "@rollup/rollup-android-arm-eabi":
-      optional: true
-    "@rollup/rollup-android-arm64":
-      optional: true
-    "@rollup/rollup-darwin-arm64":
-      optional: true
-    "@rollup/rollup-darwin-x64":
-      optional: true
-    "@rollup/rollup-linux-arm-gnueabihf":
-      optional: true
-    "@rollup/rollup-linux-arm-musleabihf":
-      optional: true
-    "@rollup/rollup-linux-arm64-gnu":
-      optional: true
-    "@rollup/rollup-linux-arm64-musl":
-      optional: true
-    "@rollup/rollup-linux-powerpc64le-gnu":
-      optional: true
-    "@rollup/rollup-linux-riscv64-gnu":
-      optional: true
-    "@rollup/rollup-linux-s390x-gnu":
-      optional: true
-    "@rollup/rollup-linux-x64-gnu":
-      optional: true
-    "@rollup/rollup-linux-x64-musl":
-      optional: true
-    "@rollup/rollup-win32-arm64-msvc":
-      optional: true
-    "@rollup/rollup-win32-ia32-msvc":
-      optional: true
-    "@rollup/rollup-win32-x64-msvc":
-      optional: true
-    fsevents:
-      optional: true
-  bin:
-    rollup: dist/bin/rollup
-  checksum: 10/a5f56e60d160e727f372fb0b0adbab03c1e5b858df7af62e626459687e6510d5b9685e4badef50bb6ffd916eaf53c1684a8e12ae959dacb8e6930c77a00a0f19
-  languageName: node
-  linkType: hard
-
 "rollup@npm:^4.20.0":
   version: 4.21.0
   resolution: "rollup@npm:4.21.0"
@@ -16101,7 +15911,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"scheduler@npm:^0.23.0, scheduler@npm:^0.23.2":
+"scheduler@npm:^0.23.2":
   version: 0.23.2
   resolution: "scheduler@npm:0.23.2"
   dependencies:
@@ -16186,9 +15996,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"send@npm:0.18.0":
-  version: 0.18.0
-  resolution: "send@npm:0.18.0"
+"send@npm:0.19.0":
+  version: 0.19.0
+  resolution: "send@npm:0.19.0"
   dependencies:
     debug: "npm:2.6.9"
     depd: "npm:2.0.0"
@@ -16203,7 +16013,7 @@ __metadata:
     on-finished: "npm:2.4.1"
     range-parser: "npm:~1.2.1"
     statuses: "npm:2.0.1"
-  checksum: 10/ec66c0ad109680ad8141d507677cfd8b4e40b9559de23191871803ed241718e99026faa46c398dcfb9250676076573bd6bfe5d0ec347f88f4b7b8533d1d391cb
+  checksum: 10/1f6064dea0ae4cbe4878437aedc9270c33f2a6650a77b56a16b62d057527f2766d96ee282997dd53ec0339082f2aad935bc7d989b46b48c82fc610800dc3a1d0
   languageName: node
   linkType: hard
 
@@ -16227,15 +16037,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"serve-static@npm:1.15.0":
-  version: 1.15.0
-  resolution: "serve-static@npm:1.15.0"
+"serve-static@npm:1.16.2":
+  version: 1.16.2
+  resolution: "serve-static@npm:1.16.2"
   dependencies:
-    encodeurl: "npm:~1.0.2"
+    encodeurl: "npm:~2.0.0"
     escape-html: "npm:~1.0.3"
     parseurl: "npm:~1.3.3"
-    send: "npm:0.18.0"
-  checksum: 10/699b2d4c29807a51d9b5e0f24955346911437aebb0178b3c4833ad30d3eca93385ff9927254f5c16da345903cad39d9cd4a532198c95a5129cc4ed43911b15a4
+    send: "npm:0.19.0"
+  checksum: 10/7fa9d9c68090f6289976b34fc13c50ac8cd7f16ae6bce08d16459300f7fc61fbc2d7ebfa02884c073ec9d6ab9e7e704c89561882bbe338e99fcacb2912fde737
   languageName: node
   linkType: hard
 
@@ -16265,6 +16075,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"set-function-length@npm:^1.2.1":
+  version: 1.2.2
+  resolution: "set-function-length@npm:1.2.2"
+  dependencies:
+    define-data-property: "npm:^1.1.4"
+    es-errors: "npm:^1.3.0"
+    function-bind: "npm:^1.1.2"
+    get-intrinsic: "npm:^1.2.4"
+    gopd: "npm:^1.0.1"
+    has-property-descriptors: "npm:^1.0.2"
+  checksum: 10/505d62b8e088468917ca4e3f8f39d0e29f9a563b97dbebf92f4bd2c3172ccfb3c5b8e4566d5fcd00784a00433900e7cb8fbc404e2dbd8c3818ba05bb9d4a8a6d
+  languageName: node
+  linkType: hard
+
 "setimmediate@npm:^1.0.5":
   version: 1.0.5
   resolution: "setimmediate@npm:1.0.5"
@@ -16302,14 +16126,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"side-channel@npm:^1.0.4":
-  version: 1.0.4
-  resolution: "side-channel@npm:1.0.4"
+"side-channel@npm:^1.0.6":
+  version: 1.0.6
+  resolution: "side-channel@npm:1.0.6"
   dependencies:
-    call-bind: "npm:^1.0.0"
-    get-intrinsic: "npm:^1.0.2"
-    object-inspect: "npm:^1.9.0"
-  checksum: 10/c4998d9fc530b0e75a7fd791ad868fdc42846f072734f9080ff55cc8dc7d3899abcda24fd896aa6648c3ab7021b4bb478073eb4f44dfd55bce9714bc1a7c5d45
+    call-bind: "npm:^1.0.7"
+    es-errors: "npm:^1.3.0"
+    get-intrinsic: "npm:^1.2.4"
+    object-inspect: "npm:^1.13.1"
+  checksum: 10/eb10944f38cebad8ad643dd02657592fa41273ce15b8bfa928d3291aff2d30c20ff777cfe908f76ccc4551ace2d1245822fdc576657cce40e9066c638ca8fa4d
   languageName: node
   linkType: hard
 
@@ -16482,6 +16307,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"source-map-js@npm:^1.2.1":
+  version: 1.2.1
+  resolution: "source-map-js@npm:1.2.1"
+  checksum: 10/ff9d8c8bf096d534a5b7707e0382ef827b4dd360a577d3f34d2b9f48e12c9d230b5747974ee7c607f0df65113732711bb701fe9ece3c7edbd43cb2294d707df3
+  languageName: node
+  linkType: hard
+
 "source-map-support@npm:^0.5.21, source-map-support@npm:~0.5.20":
   version: 0.5.21
   resolution: "source-map-support@npm:0.5.21"
@@ -16865,10 +16697,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tailwind-merge@npm:2.4.0":
-  version: 2.4.0
-  resolution: "tailwind-merge@npm:2.4.0"
-  checksum: 10/dda331a95146c9943dd18f152156fe7a5daf450a686d0562fb521ebc027273bd0c8f9503742668b3e90d27e887aa794ea5e79c38faa51216e19e9d0f1ec55925
+"tailwind-merge@npm:2.5.4":
+  version: 2.5.4
+  resolution: "tailwind-merge@npm:2.5.4"
+  checksum: 10/2bf6585a30c0ab2e4e8c4bfe0d0a14edbf8e1d88bb8ce68c79f7185e8a7e410893903a98f5819cf8843f1afbe87639e1989af28456cbb0240ddec3468f303727
   languageName: node
   linkType: hard
 
@@ -16921,9 +16753,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tailwindcss@npm:3.4.4":
-  version: 3.4.4
-  resolution: "tailwindcss@npm:3.4.4"
+"tailwindcss@npm:3.4.14":
+  version: 3.4.14
+  resolution: "tailwindcss@npm:3.4.14"
   dependencies:
     "@alloc/quick-lru": "npm:^5.2.0"
     arg: "npm:^5.0.2"
@@ -16950,7 +16782,7 @@ __metadata:
   bin:
     tailwind: lib/cli.js
     tailwindcss: lib/cli.js
-  checksum: 10/ab120014a68517c079fbeecba06c404ac94088a959b5b5e631214af4d87b332b6e4b28d8453f65eac9d94759a030ca581b1330f7d73cbf497883c4e2de083432
+  checksum: 10/2b75b697d4859ce813947b043edf19ed61f80321914743a00ba883f327016e4f7f9414823b6ccffeb1359524335c47933d970da5ce2158329f43e9a89d934eb0
   languageName: node
   linkType: hard
 
@@ -17023,8 +16855,8 @@ __metadata:
   linkType: hard
 
 "terser@npm:^5.26.0":
-  version: 5.31.3
-  resolution: "terser@npm:5.31.3"
+  version: 5.36.0
+  resolution: "terser@npm:5.36.0"
   dependencies:
     "@jridgewell/source-map": "npm:^0.3.3"
     acorn: "npm:^8.8.2"
@@ -17032,7 +16864,7 @@ __metadata:
     source-map-support: "npm:~0.5.20"
   bin:
     terser: bin/terser
-  checksum: 10/7f66d93a1157f66f5eda16515ed45e6eb485d3c4acbc46e78a5e62922f5b4643d9212abc586f791021fafc71563a93475a986c52f4270a5e0b3ee50a70507d9e
+  checksum: 10/52e641419f79d7ccdecd136b9a8e0b03f93cfe3b53cce556253aaabc347d3f2af1745419b9e622abc95d592084dc76e57774b8f9e68d29d543f4dd11c044daf4
   languageName: node
   linkType: hard
 
@@ -17187,17 +17019,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ts-pattern@npm:5.2.0":
-  version: 5.2.0
-  resolution: "ts-pattern@npm:5.2.0"
-  checksum: 10/d85607629a46d93a6245b3b17238648fda49d3ab01366df4d049dd5b293876de599f0a810c9a62de13adad38028ffb336539f612bc2686a1f879cd1f3f671a0c
-  languageName: node
-  linkType: hard
-
-"ts-pattern@npm:5.3.1":
-  version: 5.3.1
-  resolution: "ts-pattern@npm:5.3.1"
-  checksum: 10/e20be66468abb92617d909146737c7d96a4eda55c0e6ef38fece2a266a6242805daabe98adc57c6081593a11cb970b0f026fee086e337c20d145962465c74e4c
+"ts-pattern@npm:5.5.0":
+  version: 5.5.0
+  resolution: "ts-pattern@npm:5.5.0"
+  checksum: 10/d07b22cc65ea4601be588f9ac6e9aeafdc36e13bb3779bcb2ca298e9010b14eca34305c61ad89c0c68e52c5ea5725584fab5e500f5efdf5922289b36b398b684
   languageName: node
   linkType: hard
 
@@ -17208,20 +17033,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tsconfck@npm:^2.1.0":
-  version: 2.1.2
-  resolution: "tsconfck@npm:2.1.2"
-  peerDependencies:
-    typescript: ^4.3.5 || ^5.0.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  bin:
-    tsconfck: bin/tsconfck.js
-  checksum: 10/61df3b03b334a25eabb0a52e67a0c8d85770c631f2739db7703af8fdd102a2ebd598f1c851cc5fc6d6a59f2497a26c845be71c934ea16d838a3ff95a885034fb
-  languageName: node
-  linkType: hard
-
 "tsconfck@npm:^3.0.3":
   version: 3.0.3
   resolution: "tsconfck@npm:3.0.3"
@@ -17268,17 +17079,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"turbo-stream@npm:2.2.0":
-  version: 2.2.0
-  resolution: "turbo-stream@npm:2.2.0"
-  checksum: 10/1a61be95585d6c901c7f626497765e28c38332f6562f4206e8a295dca87297d044f2413f369764365a6cbb13aae3c21fee2aace4525ff32c1e787e418916914e
-  languageName: node
-  linkType: hard
-
-"turbo-stream@npm:2.3.0":
-  version: 2.3.0
-  resolution: "turbo-stream@npm:2.3.0"
-  checksum: 10/68fd6d4b39a865a82e484563f4134c061b25c784ef80f4d8a8f909074a37162bd9b4466b5bd6b929ea9261fca09b7481d1a50b282383458d928c8bdbf5be7d18
+"tslib@npm:^2.6.3":
+  version: 2.8.0
+  resolution: "tslib@npm:2.8.0"
+  checksum: 10/1bc7c43937477059b4d26f2dbde7e49ef0fb4f38f3014e0603eaea76d6a885742c8b1762af45949145e5e7408a736d20ded949da99dabc8ccba1fc5531d2d927
   languageName: node
   linkType: hard
 
@@ -17331,6 +17135,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"type-fest@npm:^4.18.1":
+  version: 4.26.1
+  resolution: "type-fest@npm:4.26.1"
+  checksum: 10/b82676194f80af228cb852e320d2ea8381c89d667d2e4d9f2bdfc8f254bccc039c7741a90c53617a4de0c9fdca8265ed18eb0888cd628f391c5c381c33a9f94b
+  languageName: node
+  linkType: hard
+
 "type-fest@npm:^4.3.1":
   version: 4.19.0
   resolution: "type-fest@npm:4.19.0"
@@ -17338,13 +17149,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"type-fest@npm:^4.3.3":
-  version: 4.7.0
-  resolution: "type-fest@npm:4.7.0"
-  checksum: 10/eebb9297b3f4a929db98ef56d459e1ca1cccac5cb53696fb2d6264b33a3df9857727fa7951796b29092d9d271ad7fc033970b23e3275ed26a61647988fe91fb4
-  languageName: node
-  linkType: hard
-
 "type-is@npm:~1.6.18":
   version: 1.6.18
   resolution: "type-is@npm:1.6.18"
@@ -17409,13 +17213,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@npm:5.5.4":
-  version: 5.5.4
-  resolution: "typescript@npm:5.5.4"
+"typescript@npm:5.6.3":
+  version: 5.6.3
+  resolution: "typescript@npm:5.6.3"
   bin:
     tsc: bin/tsc
     tsserver: bin/tsserver
-  checksum: 10/1689ccafef894825481fc3d856b4834ba3cc185a9c2878f3c76a9a1ef81af04194849840f3c69e7961e2312771471bb3b460ca92561e1d87599b26c37d0ffb6f
+  checksum: 10/c328e418e124b500908781d9f7b9b93cf08b66bf5936d94332b463822eea2f4e62973bfb3b8a745fdc038785cb66cf59d1092bac3ec2ac6a3e5854687f7833f1
   languageName: node
   linkType: hard
 
@@ -17439,13 +17243,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>":
-  version: 5.5.4
-  resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=5adc0c"
+"typescript@patch:typescript@npm%3A5.6.3#optional!builtin<compat/typescript>":
+  version: 5.6.3
+  resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin<compat/typescript>::version=5.6.3&hash=5adc0c"
   bin:
     tsc: bin/tsc
     tsserver: bin/tsserver
-  checksum: 10/2c065f0ef81855eac25c9b658a3c9da65ffc005260c12854c2286f40f3667e1b1ecf8bdbdd37b59aa0397920378ce7900bff8cb32e0f1c7af6fd86efc676718c
+  checksum: 10/dc4bec403cd33a204b655b1152a096a08e7bad2c931cb59ef8ff26b6f2aa541bf98f09fc157958a60c921b1983a8dde9a85b692f9de60fa8f574fd131e3ae4dd
   languageName: node
   linkType: hard
 
@@ -17691,6 +17495,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"update-browserslist-db@npm:^1.1.1":
+  version: 1.1.1
+  resolution: "update-browserslist-db@npm:1.1.1"
+  dependencies:
+    escalade: "npm:^3.2.0"
+    picocolors: "npm:^1.1.0"
+  peerDependencies:
+    browserslist: ">= 4.21.0"
+  bin:
+    update-browserslist-db: cli.js
+  checksum: 10/7678dd8609750588d01aa7460e8eddf2ff9d16c2a52fb1811190e0d056390f1fdffd94db3cf8fb209cf634ab4fa9407886338711c71cc6ccade5eeb22b093734
+  languageName: node
+  linkType: hard
+
 "upper-case-first@npm:^2.0.2":
   version: 2.0.2
   resolution: "upper-case-first@npm:2.0.2"
@@ -17718,12 +17536,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"urlpattern-polyfill@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "urlpattern-polyfill@npm:7.0.0"
-  dependencies:
-    braces: "npm:^3.0.2"
-  checksum: 10/fdcf1fa889a32f68e9a0144e78e8f4887f54ab0b6dba2be507a2342b192530f08d225e031df6fe64a630e0989eb277def0bf58e01d9eb44e9a10c89bfa5a8696
+"urlpattern-polyfill@npm:^10.0.0":
+  version: 10.0.0
+  resolution: "urlpattern-polyfill@npm:10.0.0"
+  checksum: 10/346819dbe718e929988298d02a988b8ddfa601d08daaa7e69b1148eab699c86c0f0f933d68d8c8cf913166fe64156ed28904e673200d18ef7e9ed6b58cea3fc7
   languageName: node
   linkType: hard
 
@@ -17865,12 +17681,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"uuid@npm:^9.0.1":
-  version: 9.0.1
-  resolution: "uuid@npm:9.0.1"
+"uuid@npm:^8.3.1":
+  version: 8.3.2
+  resolution: "uuid@npm:8.3.2"
   bin:
     uuid: dist/bin/uuid
-  checksum: 10/9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2
+  checksum: 10/9a5f7aa1d6f56dd1e8d5f2478f855f25c645e64e26e347a98e98d95781d5ed20062d6cca2eecb58ba7c84bc3910be95c0451ef4161906abaab44f9cb68ffbdd1
   languageName: node
   linkType: hard
 
@@ -17983,22 +17799,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"vite-tsconfig-paths@npm:4.2.1":
-  version: 4.2.1
-  resolution: "vite-tsconfig-paths@npm:4.2.1"
-  dependencies:
-    debug: "npm:^4.1.1"
-    globrex: "npm:^0.1.2"
-    tsconfck: "npm:^2.1.0"
-  peerDependencies:
-    vite: "*"
-  peerDependenciesMeta:
-    vite:
-      optional: true
-  checksum: 10/fd21e99a9bcd4ddb995727a9cf7b87ca95308b0b67e69bee2b836024aabe962b2206fbb62e66645638a1298fd171f8a2b0b55202da7cc801a31b3efa41a3fb2f
-  languageName: node
-  linkType: hard
-
 "vite-tsconfig-paths@npm:5.0.1":
   version: 5.0.1
   resolution: "vite-tsconfig-paths@npm:5.0.1"
@@ -18015,53 +17815,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"vite@npm:5.1.0":
-  version: 5.1.0
-  resolution: "vite@npm:5.1.0"
-  dependencies:
-    esbuild: "npm:^0.19.3"
-    fsevents: "npm:~2.3.3"
-    postcss: "npm:^8.4.35"
-    rollup: "npm:^4.2.0"
-  peerDependencies:
-    "@types/node": ^18.0.0 || >=20.0.0
-    less: "*"
-    lightningcss: ^1.21.0
-    sass: "*"
-    stylus: "*"
-    sugarss: "*"
-    terser: ^5.4.0
-  dependenciesMeta:
-    fsevents:
-      optional: true
-  peerDependenciesMeta:
-    "@types/node":
-      optional: true
-    less:
-      optional: true
-    lightningcss:
-      optional: true
-    sass:
-      optional: true
-    stylus:
-      optional: true
-    sugarss:
-      optional: true
-    terser:
-      optional: true
-  bin:
-    vite: bin/vite.js
-  checksum: 10/14d136f2e71d657cb55bec2a9330951e27e572ed79c4e79e3edc24decfa87f95664b8206614bfcf6a61db933667e554a8eed389291ad8af49de0784548a83a4c
-  languageName: node
-  linkType: hard
-
-"vite@npm:5.4.2":
-  version: 5.4.2
-  resolution: "vite@npm:5.4.2"
+"vite@npm:5.4.9":
+  version: 5.4.9
+  resolution: "vite@npm:5.4.9"
   dependencies:
     esbuild: "npm:^0.21.3"
     fsevents: "npm:~2.3.3"
-    postcss: "npm:^8.4.41"
+    postcss: "npm:^8.4.43"
     rollup: "npm:^4.20.0"
   peerDependencies:
     "@types/node": ^18.0.0 || >=20.0.0
@@ -18094,7 +17854,7 @@ __metadata:
       optional: true
   bin:
     vite: bin/vite.js
-  checksum: 10/c449f9295060fa88c9b5cc1bd17c2de198deae596b7da1250db9662fb15e7b21e0dbe9a40a9106fb7438b1231494b169457e809ecbb65e732648d87dd284ab23
+  checksum: 10/60dfb3912ba6367d2d128e798d899caae3f4ec58990657b9f679c4d9de21ddec7eba5f6ad3d4fa0e8ea31771d477521b8e757a622ecc54829d73cb7f7c146bc4
   languageName: node
   linkType: hard
 
@@ -18138,13 +17898,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"warning@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "warning@npm:3.0.0"
+  dependencies:
+    loose-envify: "npm:^1.0.0"
+  checksum: 10/c9f99a12803aab81b29858e7dc3415bf98b41baee3a4c3acdeb680d98c47b6e17490f1087dccc54432deed5711a5ce0ebcda2b27e9b5eb054c32ae50acb4419c
+  languageName: node
+  linkType: hard
+
 "watchpack@npm:^2.4.1":
-  version: 2.4.1
-  resolution: "watchpack@npm:2.4.1"
+  version: 2.4.2
+  resolution: "watchpack@npm:2.4.2"
   dependencies:
     glob-to-regexp: "npm:^0.4.1"
     graceful-fs: "npm:^4.1.2"
-  checksum: 10/0736ebd20b75d3931f9b6175c819a66dee29297c1b389b2e178bc53396a6f867ecc2fd5d87a713ae92dcb73e487daec4905beee20ca00a9e27f1184a7c2bca5e
+  checksum: 10/6bd4c051d9af189a6c781c3158dcb3069f432a0c144159eeb0a44117412105c61b2b683a5c9eebc4324625e0e9b76536387d0ba354594fa6cbbdf1ef60bee4c3
   languageName: node
   linkType: hard
 
@@ -18170,26 +17939,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"web-streams-polyfill@npm:^3.1.1, web-streams-polyfill@npm:^3.2.1":
+"web-streams-polyfill@npm:^3.1.1":
   version: 3.2.1
   resolution: "web-streams-polyfill@npm:3.2.1"
   checksum: 10/08fcf97b7883c1511dd3da794f50e9bde75a660884783baaddb2163643c21a94086f394dc4bd20dff0f55c98d98d60c4bea05a5809ef5005bdf835b63ada8900
   languageName: node
   linkType: hard
 
-"webcrypto-core@npm:^1.7.7":
-  version: 1.7.7
-  resolution: "webcrypto-core@npm:1.7.7"
-  dependencies:
-    "@peculiar/asn1-schema": "npm:^2.3.6"
-    "@peculiar/json-schema": "npm:^1.1.12"
-    asn1js: "npm:^3.0.1"
-    pvtsutils: "npm:^1.3.2"
-    tslib: "npm:^2.4.0"
-  checksum: 10/e87ac59d7d05c2aa96117c8f589e99ec9556dfc9ff3cd7fe9464de32e60ed6ff237cdfd35ed53c93546dd0d548bab67b244be381e97b162fe87b6d826e8765ae
-  languageName: node
-  linkType: hard
-
 "webidl-conversions@npm:^3.0.0":
   version: 3.0.1
   resolution: "webidl-conversions@npm:3.0.1"
@@ -18205,10 +17961,9 @@ __metadata:
   linkType: hard
 
 "webpack@npm:^5":
-  version: 5.93.0
-  resolution: "webpack@npm:5.93.0"
+  version: 5.95.0
+  resolution: "webpack@npm:5.95.0"
   dependencies:
-    "@types/eslint-scope": "npm:^3.7.3"
     "@types/estree": "npm:^1.0.5"
     "@webassemblyjs/ast": "npm:^1.12.1"
     "@webassemblyjs/wasm-edit": "npm:^1.12.1"
@@ -18217,7 +17972,7 @@ __metadata:
     acorn-import-attributes: "npm:^1.9.5"
     browserslist: "npm:^4.21.10"
     chrome-trace-event: "npm:^1.0.2"
-    enhanced-resolve: "npm:^5.17.0"
+    enhanced-resolve: "npm:^5.17.1"
     es-module-lexer: "npm:^1.2.1"
     eslint-scope: "npm:5.1.1"
     events: "npm:^3.2.0"
@@ -18237,7 +17992,7 @@ __metadata:
       optional: true
   bin:
     webpack: bin/webpack.js
-  checksum: 10/a48bef7a511d826db7f9ebee2c84317214923ac40cb2aabe6a649546c54a76a55fc3b91ff03c05fed22a13a176891c47bbff7fcc644c53bcbe5091555863641b
+  checksum: 10/0377ad3a550b041f26237c96fb55754625b0ce6bae83c1c2447e3262ad056b0b0ad770dcbb92b59f188e9a2bd56155ce910add17dcf023cfbe78bdec774380c1
   languageName: node
   linkType: hard
 
@@ -18357,9 +18112,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ws@npm:^7.4.5":
-  version: 7.5.9
-  resolution: "ws@npm:7.5.9"
+"ws@npm:^7.5.10":
+  version: 7.5.10
+  resolution: "ws@npm:7.5.10"
   peerDependencies:
     bufferutil: ^4.0.1
     utf-8-validate: ^5.0.2
@@ -18368,7 +18123,7 @@ __metadata:
       optional: true
     utf-8-validate:
       optional: true
-  checksum: 10/171e35012934bd8788150a7f46f963e50bac43a4dc524ee714c20f258693ac4d3ba2abadb00838fdac42a47af9e958c7ae7e6f4bc56db047ba897b8a2268cf7c
+  checksum: 10/9c796b84ba80ffc2c2adcdfc9c8e9a219ba99caa435c9a8d45f9ac593bba325563b3f83edc5eb067cc6d21b9a6bf2c930adf76dd40af5f58a5ca6859e81858f0
   languageName: node
   linkType: hard
 
@@ -18522,7 +18277,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"zod@npm:3.23.8, zod@npm:^3.17.3, zod@npm:^3.22.4, zod@npm:^3.23.5, zod@npm:^3.23.8":
+"zod@npm:3.23.8, zod@npm:^3.17.3, zod@npm:^3.23.5, zod@npm:^3.23.8":
   version: 3.23.8
   resolution: "zod@npm:3.23.8"
   checksum: 10/846fd73e1af0def79c19d510ea9e4a795544a67d5b34b7e1c4d0425bf6bfd1c719446d94cdfa1721c1987d891321d61f779e8236fde517dc0e524aa851a6eff1


Test patch:
diff --git a/libs/graphql/src/backend/queries/LatestUserSummary.gql b/libs/graphql/src/backend/queries/LatestUserSummary.gql
index f3bdb50439..23fc3cab31 100644
--- a/libs/graphql/src/backend/queries/LatestUserSummary.gql
+++ b/libs/graphql/src/backend/queries/LatestUserSummary.gql
@@ -1,35 +1,36 @@
 query LatestUserSummary {
-  latestUserSummary {
-    totalMetadataReviewCount
-    totalCollectionReviewCount
-    totalMetadataGroupReviewCount
-    totalPersonReviewCount
-    measurementCount
-    workoutCount
-    totalWorkoutDuration
-    audioBookCount
-    totalAudioBookDuration
-    animeCount
-    bookCount
-    totalBookPages
-    podcastCount
-    totalPodcastDuration
-    mangaCount
-    movieCount
-    totalMovieDuration
-    showCount
-    totalShowDuration
-    videoGameCount
-    visualNovelCount
-    totalVisualNovelDuration
-    totalWorkoutPersonalBests
-    totalWorkoutWeight
-    totalWorkoutReps
-    totalWorkoutDistance
-    totalWorkoutRestTime
-    totalMetadataCount
-    totalReviewCount
-    totalCount
-    totalDuration
-  }
+	latestUserSummary {
+		totalMetadataReviewCount
+		totalCollectionReviewCount
+		totalMetadataGroupReviewCount
+		totalPersonReviewCount
+		measurementCount
+		workoutCount
+		totalWorkoutDuration
+		audioBookCount
+		totalAudioBookDuration
+		animeCount
+		bookCount
+		totalBookPages
+		podcastCount
+		totalPodcastDuration
+		mangaCount
+		movieCount
+		totalMovieDuration
+		showCount
+		totalShowDuration
+		videoGameCount
+		totalVideoGameDuration
+		visualNovelCount
+		totalVisualNovelDuration
+		totalWorkoutPersonalBests
+		totalWorkoutWeight
+		totalWorkoutReps
+		totalWorkoutDistance
+		totalWorkoutRestTime
+		totalMetadataCount
+		totalReviewCount
+		totalCount
+		totalDuration
+	}
 }