import { base64url } from "jose"; import { z } from "zod/v4"; import { decodePatternData } from "./PatternDecoder"; import { PlayerPattern } from "./Schemas"; export type Cosmetics = z.infer; export type Pattern = z.infer; export type Flag = z.infer; export type PatternName = z.infer; export type Product = z.infer; export type ColorPalette = z.infer; export type PatternData = z.infer; export const ProductSchema = z.object({ productId: z.string(), priceId: z.string(), price: z.string(), }); export const CosmeticNameSchema = z .string() .regex(/^[a-z0-9_]+$/) .max(32); export const PatternDataSchema = z .string() .max(1403) .base64url() .refine( (val) => { try { decodePatternData(val, base64url.decode); return true; } catch (e) { if (e instanceof Error) { console.error(JSON.stringify(e.message, null, 2)); } else { console.error(String(e)); } return false; } }, { message: "Invalid pattern", }, ); export const ColorPaletteSchema = z.object({ name: z.string(), primaryColor: z.string(), secondaryColor: z.string(), }); export const PatternSchema = z.object({ name: CosmeticNameSchema, pattern: PatternDataSchema, colorPalettes: z .object({ name: z.string(), isArchived: z.boolean(), }) .array() .optional(), affiliateCode: z.string().nullable(), product: ProductSchema.nullable(), artist: z.string().optional(), }); export const FlagSchema = z.object({ name: CosmeticNameSchema, url: z.string(), affiliateCode: z.string().nullable(), product: ProductSchema.nullable(), artist: z.string().optional(), }); // Schema for resources/cosmetics/cosmetics.json export const CosmeticsSchema = z.object({ colorPalettes: z.record(z.string(), ColorPaletteSchema).optional(), patterns: z.record(z.string(), PatternSchema), flags: z.record(z.string(), FlagSchema), }); export const DefaultPattern = { name: "default", patternData: "AAAAAA", colorPalette: undefined, } satisfies PlayerPattern;