mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Add zod to validate types
This commit is contained in:
parent
090d613266
commit
dfe6fa7cda
@ -24,6 +24,16 @@
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
experimental: {
|
||||
swcPlugins: [
|
||||
[
|
||||
"next-superjson-plugin",
|
||||
{
|
||||
excluded: [],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
env: {
|
||||
KYOO_URL: process.env.KYOO_URL ?? "http://localhost:5000",
|
||||
},
|
||||
|
@ -26,10 +26,12 @@
|
||||
"@mui/icons-material": "^5.8.4",
|
||||
"@mui/material": "^5.8.7",
|
||||
"next": "12.2.2",
|
||||
"next-superjson-plugin": "^0.3.0",
|
||||
"next-translate": "^1.5.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-query": "^4.0.0-beta.23"
|
||||
"react-query": "^4.0.0-beta.23",
|
||||
"zod": "^3.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.0.3",
|
||||
|
@ -29,13 +29,14 @@ import {
|
||||
Tooltip,
|
||||
Box,
|
||||
Skeleton,
|
||||
AppBarProps,
|
||||
} from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import logo from "../../public/icons/icon.svg";
|
||||
import useTranslation from "next-translate/useTranslation";
|
||||
import Image from "next/image";
|
||||
import { ButtonLink } from "~/utils/link";
|
||||
import { Library, Page } from "~/models";
|
||||
import { LibraryP, Paged } from "~/models";
|
||||
import { useFetch } from "~/utils/query";
|
||||
import { ErrorSnackbar } from "./error-snackbar";
|
||||
|
||||
@ -63,6 +64,7 @@ export const KyooTitle = (props: { sx: SxProps<Theme> }) => {
|
||||
mr: 2,
|
||||
fontFamily: "monospace",
|
||||
fontWeight: 700,
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Kyoo
|
||||
@ -72,9 +74,14 @@ export const KyooTitle = (props: { sx: SxProps<Theme> }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Navbar = () => {
|
||||
export const NavbarQuery = {
|
||||
parser: Paged(LibraryP),
|
||||
path: ["libraries"],
|
||||
};
|
||||
|
||||
export const Navbar = (barProps: AppBarProps) => {
|
||||
const { t } = useTranslation("common");
|
||||
const { data, error, isSuccess, isError } = useFetch<Page<Library>>("libraries");
|
||||
const { data, error, isSuccess, isError } = useFetch(NavbarQuery);
|
||||
|
||||
return (
|
||||
<AppBar position="sticky">
|
||||
@ -111,7 +118,7 @@ export const Navbar = () => {
|
||||
</Box>
|
||||
<Tooltip title={t("navbar.login")}>
|
||||
<IconButton sx={{ p: 0 }} href="/auth/login">
|
||||
<Avatar alt="Account" />
|
||||
<Avatar alt={t("navbar.login")} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Toolbar>
|
||||
|
@ -18,9 +18,8 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Box, NoSsr, Skeleton, styled } from "@mui/material";
|
||||
import { Box, Skeleton, styled } from "@mui/material";
|
||||
import {
|
||||
MutableRefObject,
|
||||
SyntheticEvent,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
|
@ -18,7 +18,7 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type { Page } from "./page";
|
||||
export type { KyooErrors } from "./kyoo-errors";
|
||||
export * from "./page";
|
||||
export * from "./kyoo-errors";
|
||||
export * from "./traits";
|
||||
export * from "./resources";
|
||||
|
@ -18,6 +18,8 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* A page of resource that contains information about the pagination of resources.
|
||||
*/
|
||||
@ -53,3 +55,12 @@ export interface Page<T> {
|
||||
*/
|
||||
items: T[];
|
||||
}
|
||||
|
||||
export const Paged = <Item>(item: z.ZodType<Item>): z.ZodSchema<Page<Item>> =>
|
||||
z.object({
|
||||
this: z.string(),
|
||||
first: z.string(),
|
||||
next: z.string().optional(),
|
||||
count: z.number(),
|
||||
items: z.array(item),
|
||||
});
|
||||
|
34
front/src/models/resources/genre.ts
Normal file
34
front/src/models/resources/genre.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
import { ResourceP } from "../traits/resource";
|
||||
|
||||
export const GenreP = ResourceP.extend({
|
||||
/**
|
||||
* The name of this genre.
|
||||
*/
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* A genre that allow one to specify categories for shows.
|
||||
*/
|
||||
export type Genre = z.infer<typeof GenreP>;
|
@ -18,19 +18,22 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Resource } from "../traits/resource";
|
||||
import { z } from "zod";
|
||||
import { ResourceP } from "../traits/resource";
|
||||
|
||||
/**
|
||||
* The library that will contain Shows, Collections...
|
||||
*/
|
||||
export interface Library extends Resource {
|
||||
export const LibraryP = ResourceP.extend({
|
||||
/**
|
||||
* The name of this library.
|
||||
*/
|
||||
name: string;
|
||||
name: z.string(),
|
||||
|
||||
/**
|
||||
* The list of paths that this library is responsible for. This is mainly used by the Scan task.
|
||||
*/
|
||||
paths: string[];
|
||||
}
|
||||
paths: z.array(z.string()),
|
||||
});
|
||||
|
||||
export type Library = z.infer<typeof LibraryP>;
|
||||
|
@ -18,42 +18,10 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Resource, Images } from "../traits";
|
||||
|
||||
/**
|
||||
* A series or a movie.
|
||||
*/
|
||||
export interface Show extends Resource, Images {
|
||||
/**
|
||||
* The title of this show.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The list of alternative titles of this show.
|
||||
*/
|
||||
aliases: string[];
|
||||
|
||||
/**
|
||||
* The summary of this show.
|
||||
*/
|
||||
overview: string;
|
||||
|
||||
/**
|
||||
* Is this show airing, not aired yet or finished?
|
||||
*/
|
||||
status: Status;
|
||||
|
||||
/**
|
||||
* The date this show started airing. It can be null if this is unknown.
|
||||
*/
|
||||
startAir: Date | null;
|
||||
|
||||
/**
|
||||
* The date this show finished airing. It can also be null if this is unknown.
|
||||
*/
|
||||
endAir: Date | null;
|
||||
}
|
||||
import { z } from "zod";
|
||||
import { zdate } from "~/utils/zod";
|
||||
import { ImagesP, ResourceP } from "../traits";
|
||||
import { GenreP } from "./genre";
|
||||
|
||||
/**
|
||||
* The enum containing show's status.
|
||||
@ -64,3 +32,45 @@ export enum Status {
|
||||
Airing = 2,
|
||||
Planned = 3,
|
||||
}
|
||||
|
||||
export const ShowP = z.preprocess(
|
||||
(x: any) => {
|
||||
x.name = x.title;
|
||||
return x;
|
||||
},
|
||||
ResourceP.merge(ImagesP).extend({
|
||||
/**
|
||||
* The title of this show.
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* The list of alternative titles of this show.
|
||||
*/
|
||||
aliases: z.array(z.string()),
|
||||
/**
|
||||
* The summary of this show.
|
||||
*/
|
||||
overview: z.string(),
|
||||
/**
|
||||
* Is this show airing, not aired yet or finished?
|
||||
*/
|
||||
status: z.nativeEnum(Status),
|
||||
/**
|
||||
* The date this show started airing. It can be null if this is unknown.
|
||||
*/
|
||||
startAir: zdate().optional(),
|
||||
/**
|
||||
* The date this show finished airing. It can also be null if this is unknown.
|
||||
*/
|
||||
endAir: zdate().nullable(),
|
||||
/**
|
||||
* The list of genres (themes) this show has.
|
||||
*/
|
||||
genres: z.array(GenreP).optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* A tv serie or an anime.
|
||||
*/
|
||||
export type Show = z.infer<typeof ShowP>;
|
||||
|
@ -18,38 +18,43 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base traits for items that has image resources.
|
||||
*/
|
||||
export interface Images {
|
||||
import { z } from "zod";
|
||||
|
||||
const imageFn = (url: string) => (url.startsWith("/api") ? url : `/api${url}`);
|
||||
|
||||
export const ImagesP = z.object({
|
||||
/**
|
||||
* An url to the poster of this resource. If this resource does not have an image, the link will
|
||||
* be null. If the kyoo's instance is not capable of handling this kind of image for the specific
|
||||
* resource, this field won't be present.
|
||||
*/
|
||||
poster?: string;
|
||||
poster: z.string().transform(imageFn).optional(),
|
||||
|
||||
/**
|
||||
* An url to the thumbnail of this resource. If this resource does not have an image, the link
|
||||
* will be null. If the kyoo's instance is not capable of handling this kind of image for the
|
||||
* specific resource, this field won't be present.
|
||||
*/
|
||||
thumbnail?: string;
|
||||
thumbnail: z.string().transform(imageFn).optional(),
|
||||
|
||||
/**
|
||||
* An url to the logo of this resource. If this resource does not have an image, the link will be
|
||||
* null. If the kyoo's instance is not capable of handling this kind of image for the specific
|
||||
* resource, this field won't be present.
|
||||
*/
|
||||
logo?: string;
|
||||
logo: z.string().transform(imageFn).optional(),
|
||||
|
||||
/**
|
||||
* An url to the thumbnail of this resource. If this resource does not have an image, the link
|
||||
* will be null. If the kyoo's instance is not capable of handling this kind of image for the
|
||||
* specific resource, this field won't be present.
|
||||
*/
|
||||
trailer: z.string().transform(imageFn).optional(),
|
||||
});
|
||||
|
||||
trailer?: string;
|
||||
};
|
||||
/**
|
||||
* Base traits for items that has image resources.
|
||||
*/
|
||||
export type Images = z.infer<typeof ImagesP>;
|
||||
|
||||
export const imageList = ["poster", "thumbnail", "logo", "trailer"];
|
||||
|
@ -18,18 +18,22 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The base trait used to represent identifiable resources.
|
||||
*/
|
||||
export interface Resource {
|
||||
import { z } from "zod";
|
||||
|
||||
export const ResourceP = z.object({
|
||||
/**
|
||||
* A unique ID for this type of resource. This can't be changed and duplicates are not allowed.
|
||||
*/
|
||||
id: number;
|
||||
id: z.number(),
|
||||
|
||||
/**
|
||||
* A human-readable identifier that can be used instead of an ID. A slug must be unique for a type
|
||||
* of resource but it can be changed.
|
||||
*/
|
||||
slug: string;
|
||||
}
|
||||
slug: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* The base trait used to represent identifiable resources.
|
||||
*/
|
||||
export type Resource = z.infer<typeof ResourceP>;
|
||||
|
@ -24,9 +24,9 @@ import { ThemeProvider } from "@mui/material";
|
||||
import NextApp, { AppContext } from "next/app";
|
||||
import type { AppProps } from "next/app";
|
||||
import { Hydrate, QueryClientProvider } from "react-query";
|
||||
import { createQueryClient, fetchQuery } from "~/utils/query";
|
||||
import { createQueryClient, fetchQuery, QueryIdentifier, QueryPage } from "~/utils/query";
|
||||
import { defaultTheme } from "~/utils/themes/default-theme";
|
||||
import { Navbar } from "~/components/navbar";
|
||||
import { Navbar, NavbarQuery } from "~/components/navbar";
|
||||
import "../global.css";
|
||||
import { Box } from "@mui/system";
|
||||
|
||||
@ -35,9 +35,7 @@ const AppWithNavbar = ({ children }: { children: JSX.Element }) => {
|
||||
<>
|
||||
<Navbar/>
|
||||
{/* TODO: add an option to disable the navbar in the component */}
|
||||
<Box >
|
||||
{children}
|
||||
</Box>
|
||||
<Box>{children}</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -45,6 +43,8 @@ const AppWithNavbar = ({ children }: { children: JSX.Element }) => {
|
||||
const App = ({ Component, pageProps }: AppProps) => {
|
||||
const [queryClient] = useState(() => createQueryClient());
|
||||
const { queryState, ...props } = pageProps;
|
||||
|
||||
// TODO: tranform date string to date instances in the queryState
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Hydrate state={queryState}>
|
||||
@ -61,10 +61,10 @@ const App = ({ Component, pageProps }: AppProps) => {
|
||||
App.getInitialProps = async (ctx: AppContext) => {
|
||||
const appProps = await NextApp.getInitialProps(ctx);
|
||||
|
||||
const getUrl = (ctx.Component as any).getFetchUrls;
|
||||
const urls: string[][] = getUrl ? getUrl(ctx.router.query) : [];
|
||||
const getUrl = (ctx.Component as QueryPage).getFetchUrls;
|
||||
const urls: QueryIdentifier[] = getUrl ? getUrl(ctx.router.query as any) : [];
|
||||
// TODO: check if the navbar is needed for this
|
||||
urls.push(["libraries"]);
|
||||
urls.push(NavbarQuery);
|
||||
appProps.pageProps.queryState = await fetchQuery(urls);
|
||||
|
||||
return appProps;
|
||||
|
@ -20,8 +20,8 @@
|
||||
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { Image, Poster } from "~/components/poster";
|
||||
import { Show } from "~/models";
|
||||
import { QueryPage, useFetch } from "~/utils/query";
|
||||
import { Show, ShowP } from "~/models";
|
||||
import { QueryIdentifier, QueryPage, useFetch } from "~/utils/query";
|
||||
import { withRoute } from "~/utils/router";
|
||||
|
||||
const ShowHeader = (data: Show) => {
|
||||
@ -36,8 +36,17 @@ const ShowHeader = (data: Show) => {
|
||||
);
|
||||
};
|
||||
|
||||
const query = (slug: string): QueryIdentifier<Show> => ({
|
||||
parser: ShowP,
|
||||
path: ["shows", slug],
|
||||
params: {
|
||||
fields: ["genres"],
|
||||
},
|
||||
});
|
||||
|
||||
const ShowDetails: QueryPage<{ slug: string }> = ({ slug }) => {
|
||||
const { data } = useFetch<Show>("shows", slug);
|
||||
const { data, error } = useFetch(query(slug));
|
||||
console.log("error", data);
|
||||
|
||||
if (!data) return <p>oups</p>;
|
||||
|
||||
@ -48,6 +57,6 @@ const ShowDetails: QueryPage<{ slug: string }> = ({ slug }) => {
|
||||
);
|
||||
};
|
||||
|
||||
ShowDetails.getFetchUrls = ({ slug }) => [["shows", slug]];
|
||||
ShowDetails.getFetchUrls = ({ slug }) => [query(slug)];
|
||||
|
||||
export default withRoute(ShowDetails);
|
||||
|
@ -26,22 +26,37 @@ import {
|
||||
useInfiniteQuery,
|
||||
useQuery,
|
||||
} from "react-query";
|
||||
import { z } from "zod";
|
||||
import { imageList, KyooErrors, Page } from "~/models";
|
||||
import { Paged } from "~/models/page";
|
||||
|
||||
const queryFn = async <T>(context: QueryFunctionContext): Promise<T> => {
|
||||
const queryFn = async <Data>(
|
||||
type: z.ZodType<Data>,
|
||||
context: QueryFunctionContext,
|
||||
): Promise<Data> => {
|
||||
try {
|
||||
const resp = await fetch(
|
||||
[typeof window === "undefined" ? process.env.KYOO_URL : "/api"]
|
||||
.concat(context.pageParam ? [context.pageParam] : (context.queryKey as string[]))
|
||||
.join("/"),
|
||||
.concat(
|
||||
context.pageParam ? [context.pageParam] : (context.queryKey.filter((x) => x) as string[]),
|
||||
)
|
||||
.join("/")
|
||||
.replace("/?", "?"),
|
||||
);
|
||||
if (!resp.ok) {
|
||||
throw await resp.json();
|
||||
}
|
||||
return await resp.json();
|
||||
|
||||
const data = await resp.json();
|
||||
const parsed = await type.safeParseAsync(data);
|
||||
if (!parsed.success) {
|
||||
console.log("Parse error: ", parsed.error);
|
||||
throw { errors: parsed.error.errors.map((x) => x.message) } as KyooErrors;
|
||||
}
|
||||
return parsed.data;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw { errors: ["Could not reach Kyoo's server."] }; // as KyooErrors;
|
||||
console.error("Fetch error: ", e);
|
||||
throw { errors: ["Could not reach Kyoo's server."] } as KyooErrors;
|
||||
}
|
||||
};
|
||||
|
||||
@ -53,52 +68,57 @@ export const createQueryClient = () =>
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
retry: false,
|
||||
queryFn: queryFn,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export type QueryIdentifier<T = unknown> = {
|
||||
parser: z.ZodType<T>;
|
||||
path: string[];
|
||||
params?: { [query: string]: boolean | number | string | string[] };
|
||||
};
|
||||
|
||||
export type QueryPage<Props = {}> = ComponentType<Props> & {
|
||||
getFetchUrls?: (route: { [key: string]: string }) => string[][];
|
||||
getFetchUrls?: (route: { [key: string]: string }) => QueryIdentifier[];
|
||||
};
|
||||
|
||||
const imageSelector = <T>(obj: T): T => {
|
||||
// TODO: remove this
|
||||
// @ts-ignore
|
||||
if ("title" in obj) obj.name = obj.title;
|
||||
|
||||
for (const img of imageList) {
|
||||
// @ts-ignore
|
||||
if (img in obj && obj[img] && !obj[img].startsWith("/api")) {
|
||||
// @ts-ignore
|
||||
obj[img] = `/api${obj[img]}`;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
const toQuery = (params?: { [query: string]: boolean | number | string | string[] }) => {
|
||||
if (!params) return undefined;
|
||||
return (
|
||||
"?" +
|
||||
Object.entries(params)
|
||||
.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`)
|
||||
.join("&")
|
||||
);
|
||||
};
|
||||
|
||||
export const useFetch = <Data>(...params: string[]) => {
|
||||
return useQuery<Data, KyooErrors>(params, {
|
||||
select: imageSelector,
|
||||
export const useFetch = <Data>(query: QueryIdentifier<Data>) => {
|
||||
return useQuery<Data, KyooErrors>({
|
||||
queryKey: [...query.path, toQuery(query.params)],
|
||||
queryFn: (ctx) => queryFn(query.parser, ctx),
|
||||
});
|
||||
};
|
||||
|
||||
export const useInfiniteFetch = <Data>(...params: string[]) => {
|
||||
return useInfiniteQuery<Page<Data>, KyooErrors>(params, {
|
||||
select: (pages) => {
|
||||
pages.pages.map((x) => x.items.map(imageSelector));
|
||||
return pages;
|
||||
},
|
||||
export const useInfiniteFetch = <Data>(query: QueryIdentifier<Data>) => {
|
||||
return useInfiniteQuery<Page<Data>, KyooErrors>({
|
||||
queryKey: [...query.path, toQuery(query.params)],
|
||||
queryFn: (ctx) => queryFn(Paged(query.parser), ctx),
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchQuery = async (queries: string[][]) => {
|
||||
export const fetchQuery = async (queries: QueryIdentifier[]) => {
|
||||
// we can't put this check in a function because we want build time optimizations
|
||||
// see https://github.com/vercel/next.js/issues/5354 for details
|
||||
if (typeof window !== "undefined") return {};
|
||||
console.log(queries)
|
||||
|
||||
const client = createQueryClient();
|
||||
await Promise.all(queries.map((x) => client.prefetchQuery(x)));
|
||||
await Promise.all(
|
||||
queries.map((query) =>
|
||||
client.prefetchQuery({
|
||||
queryKey: [...query.path, toQuery(query.params)],
|
||||
queryFn: (ctx) => queryFn(query.parser, ctx),
|
||||
}),
|
||||
),
|
||||
);
|
||||
return dehydrate(client);
|
||||
};
|
||||
|
33
front/src/utils/zod.ts
Normal file
33
front/src/utils/zod.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
export const zdate = () => {
|
||||
return z.preprocess((arg) => {
|
||||
if (arg instanceof Date) return arg;
|
||||
|
||||
if (typeof arg === "string" && /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/.test(arg)) {
|
||||
return new Date(arg);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, z.date());
|
||||
};
|
@ -1385,7 +1385,7 @@ has@^1.0.3:
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hoist-non-react-statics@^3.3.1:
|
||||
hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
@ -1938,6 +1938,13 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
next-superjson-plugin@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/next-superjson-plugin/-/next-superjson-plugin-0.3.0.tgz#81145f275c1e555be68867c104cc21113f96c675"
|
||||
integrity sha512-M0Soj1P2t9peCyzNndEJiS48O2m88X9UGsCXDy8WHyGwWw1S7eCOEg9MiMqR+X1GD5C3hsdtHKMsKqrZOzr+SQ==
|
||||
dependencies:
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
next-translate@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/next-translate/-/next-translate-1.5.0.tgz#b1e5c4a8e55e31b3ed1b9428529f27c289c6b7bc"
|
||||
@ -2583,3 +2590,8 @@ yaml@^1.7.2:
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
zod@^3.18.0:
|
||||
version "3.18.0"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.18.0.tgz#2eed58b3cafb8d9a67aa2fee69279702f584f3bc"
|
||||
integrity sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==
|
||||
|
Loading…
x
Reference in New Issue
Block a user