mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
289 lines
7.5 KiB
TypeScript
289 lines
7.5 KiB
TypeScript
import { z, ZodNumber, ZodOptional } from 'zod';
|
|
// import zodToJsonSchema from 'zod-to-json-schema';
|
|
|
|
export const BoardGameSearch = z.object({
|
|
minAge: z.number(),
|
|
maxAge: z.number(),
|
|
minPlayers: z.number(),
|
|
maxPlayers: z.number()
|
|
});
|
|
|
|
export const saved_game_schema = z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
thumb_url: z.string(),
|
|
players: z.string(),
|
|
playtime: IntegerString(z.number())
|
|
});
|
|
|
|
export const list_game_request_schema = z.object({
|
|
id: z.string(),
|
|
externalId: z.string()
|
|
});
|
|
|
|
export type ListGameSchema = typeof list_game_request_schema;
|
|
|
|
// https://github.com/colinhacks/zod/discussions/330
|
|
function IntegerString<schema extends ZodNumber | ZodOptional<ZodNumber>>(schema: schema) {
|
|
return z.preprocess(
|
|
(value) =>
|
|
typeof value === 'string'
|
|
? parseInt(value, 10)
|
|
: typeof value === 'number'
|
|
? value
|
|
: undefined,
|
|
schema
|
|
);
|
|
}
|
|
|
|
const Search = z.object({
|
|
q: z.string().trim().optional().default(''),
|
|
minAge: IntegerString(z.number().min(1).max(120).optional()),
|
|
minPlayers: IntegerString(z.number().min(1).max(50).optional()),
|
|
maxPlayers: IntegerString(z.number().min(1).max(50).optional()),
|
|
exactMinAge: z.preprocess((a) => Boolean(a), z.boolean().optional()),
|
|
exactMinPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()),
|
|
exactMaxPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()),
|
|
sort: z.enum(['asc', 'desc']).optional(),
|
|
sortBy: z.enum(['name', 'min_players', 'max_players', 'min_age', 'times_played']).optional(),
|
|
limit: z.number().min(10).max(100).default(10),
|
|
skip: z.number().min(0).default(0)
|
|
});
|
|
|
|
// minAge: z
|
|
// .string()
|
|
// .min(1)
|
|
// .transform((v) => +v)
|
|
// .refine((minAge) => !isNaN(minAge), { message: 'Must be a number' })
|
|
// .refine((minAge) => minAge >= 1 && minAge <= 120, { message: 'Must be between 1 and 120' })
|
|
// .optional(),
|
|
|
|
export const search_schema = z
|
|
.object({
|
|
q: z.string().trim().optional().default(''),
|
|
exact: z.preprocess((a) => Boolean(a), z.boolean().default(true)),
|
|
minAge: IntegerString(z.number().min(1).max(120).optional()),
|
|
minPlayers: IntegerString(z.number().min(1).max(50).optional()),
|
|
maxPlayers: IntegerString(z.number().min(1).max(50).optional()),
|
|
exactMinAge: z.preprocess((a) => Boolean(a), z.boolean().optional()),
|
|
exactMinPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()),
|
|
exactMaxPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()),
|
|
sort: z.enum(['asc', 'desc']).optional(),
|
|
order: z.enum(['name', 'min_players', 'max_players', 'min_age', 'times_played']).optional(),
|
|
limit: z.number().min(10).max(100).default(10),
|
|
skip: z.number().min(0).default(0)
|
|
})
|
|
.superRefine(
|
|
({ minPlayers, maxPlayers, minAge, exactMinAge, exactMinPlayers, exactMaxPlayers }, ctx) => {
|
|
console.log({ minPlayers, maxPlayers });
|
|
if (minPlayers && maxPlayers && minPlayers > maxPlayers) {
|
|
ctx.addIssue({
|
|
code: 'custom',
|
|
message: 'Min Players must be smaller than Max Players',
|
|
path: ['minPlayers']
|
|
});
|
|
ctx.addIssue({
|
|
code: 'custom',
|
|
message: 'Min Players must be smaller than Max Players',
|
|
path: ['maxPlayers']
|
|
});
|
|
}
|
|
|
|
if (exactMinAge && !minAge) {
|
|
ctx.addIssue({
|
|
code: 'custom',
|
|
message: 'Min Age required when searching for exact min age',
|
|
path: ['minAge']
|
|
});
|
|
}
|
|
|
|
if (exactMinPlayers && !minPlayers) {
|
|
ctx.addIssue({
|
|
code: 'custom',
|
|
message: 'Min Players required when searching for exact min players',
|
|
path: ['minPlayers']
|
|
});
|
|
}
|
|
|
|
if (exactMaxPlayers && !maxPlayers) {
|
|
ctx.addIssue({
|
|
code: 'custom',
|
|
message: 'Max Players required when searching for exact max players',
|
|
path: ['maxPlayers']
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
export type SearchSchema = typeof search_schema;
|
|
|
|
export const BggForm = z.object({
|
|
link: z.string().trim().startsWith('https://boardgamegeek.com/boardgame/')
|
|
})
|
|
|
|
export const collection_search_schema = Search.extend({
|
|
collection_id: z.string()
|
|
});
|
|
|
|
export type CollectionSearchSchema = typeof collection_search_schema;
|
|
|
|
export const search_result_schema = z.object({
|
|
client_id: z.string(),
|
|
limit: z.number(),
|
|
skip: z.number(),
|
|
ids: z.string().array(),
|
|
list_id: z.string(),
|
|
kickstarter: z.boolean(),
|
|
random: z.boolean(),
|
|
name: z.string(),
|
|
exact: z.boolean(),
|
|
fuzzy_match: z.boolean(),
|
|
designer: z.string(),
|
|
publisher: z.string(),
|
|
artist: z.string(),
|
|
mechanics: z.string(),
|
|
categories: z.string(),
|
|
order_by: z.string(),
|
|
ascending: z.boolean(),
|
|
min_players: z.number(),
|
|
max_players: z.number(),
|
|
min_playtime: z.number(),
|
|
max_playtime: z.number(),
|
|
min_age: z.number(),
|
|
year_published: z.number(),
|
|
gt_min_players: z.number(),
|
|
gt_max_players: z.number(),
|
|
gt_min_playtime: z.number(),
|
|
gt_max_playtime: z.number(),
|
|
gt_min_age: z.number(),
|
|
gt_year_published: z.number(),
|
|
gt_price: z.bigint(),
|
|
gt_msrp: z.bigint(),
|
|
gt_discount: z.bigint(),
|
|
gt_reddit_count: z.number(),
|
|
gt_reddit_week_count: z.number(),
|
|
gt_reddit_day_count: z.number(),
|
|
lt_min_players: z.number(),
|
|
lt_max_players: z.number(),
|
|
lt_min_playtime: z.number(),
|
|
lt_max_playtime: z.number(),
|
|
lt_min_age: z.number(),
|
|
lt_year_published: z.number(),
|
|
lt_price: z.bigint(),
|
|
lt_msrp: z.bigint(),
|
|
lt_discount: z.bigint(),
|
|
lt_reddit_count: z.number(),
|
|
lt_reddit_week_count: z.number(),
|
|
lt_reddit_day_count: z.number(),
|
|
fields: z.string()
|
|
});
|
|
|
|
export type SearchResultSchema = typeof search_result_schema;
|
|
|
|
export const game_schema = z.object({
|
|
id: z.string(),
|
|
handle: z.string(),
|
|
name: z.string(),
|
|
url: z.string(),
|
|
edit_url: z.string(),
|
|
price: z.number(),
|
|
price_ca: z.number(),
|
|
price_uk: z.number(),
|
|
price_au: z.number(),
|
|
msrp: z.number(),
|
|
year_published: z.number(),
|
|
min_players: z.number(),
|
|
max_players: z.number(),
|
|
min_playtime: z.number(),
|
|
max_playtime: z.number(),
|
|
min_age: z.number(),
|
|
description: z.string(),
|
|
players: z.string(),
|
|
playtime: z.string()
|
|
});
|
|
|
|
export const category_schema = z.object({
|
|
id: z.string(),
|
|
name: z.string()
|
|
});
|
|
|
|
export const mechanics_schema = z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
description: z.string().optional()
|
|
});
|
|
|
|
const gameSchema = z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
description: z.string().optional(),
|
|
year_published: z.number().optional(),
|
|
min_players: z.number().optional(),
|
|
max_players: z.number().optional(),
|
|
min_playtime: z.number().optional(),
|
|
max_playtime: z.number().optional(),
|
|
min_age: z.number().optional(),
|
|
image_url: z.string().optional(),
|
|
thumb_url: z.string().optional(),
|
|
url: z.string().optional(),
|
|
weight_amount: z.number().optional(),
|
|
weight_units: z.enum(['Medium', 'Heavy']).optional(),
|
|
categories: z.array(category_schema).optional(),
|
|
mechanics: z.array(mechanics_schema).optional(),
|
|
designers: z
|
|
.array(
|
|
z.object({
|
|
id: z.string(),
|
|
name: z.string()
|
|
})
|
|
)
|
|
.optional(),
|
|
publishers: z
|
|
.array(
|
|
z.object({
|
|
id: z.string(),
|
|
name: z.string()
|
|
})
|
|
)
|
|
.optional(),
|
|
artists: z
|
|
.array(
|
|
z.object({
|
|
id: z.string(),
|
|
name: z.string()
|
|
})
|
|
)
|
|
.optional(),
|
|
names: z.array(z.string()).optional(),
|
|
expansions: z
|
|
.array(
|
|
z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
year_published: z.number().optional()
|
|
})
|
|
)
|
|
.optional(),
|
|
primary_publisher: z
|
|
.object({
|
|
id: z.string(),
|
|
name: z.string()
|
|
})
|
|
.optional()
|
|
});
|
|
|
|
const searchResultSchema = z.object({
|
|
games: z.array(gameSchema),
|
|
count: z.number()
|
|
});
|
|
|
|
export const WishlistSchema = z.object({
|
|
name: z.string(),
|
|
id: z.string()
|
|
});
|
|
|
|
// export const game_raw_schema_json = zodToJsonSchema(game_schema, {
|
|
// $refStrategy: 'none',
|
|
// });
|
|
|
|
export type Game = z.infer<typeof game_schema>;
|