feat: add zod validation to config
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { z } from 'zod';
|
||||
|
||||
const configPath = join(import.meta.dir, '..', '..', 'config', 'config.json');
|
||||
|
||||
@@ -45,6 +46,56 @@ export interface GameConfigType {
|
||||
// Initial default config state
|
||||
export const config: GameConfigType = {} as GameConfigType;
|
||||
|
||||
const bigIntSchema = z.union([z.string(), z.number(), z.bigint()])
|
||||
.refine((val) => {
|
||||
try {
|
||||
BigInt(val);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}, { message: "Must be a valid integer" })
|
||||
.transform((val) => BigInt(val));
|
||||
|
||||
const configSchema = z.object({
|
||||
leveling: z.object({
|
||||
base: z.number(),
|
||||
exponent: z.number(),
|
||||
chat: z.object({
|
||||
cooldownMs: z.number(),
|
||||
minXp: z.number(),
|
||||
maxXp: z.number(),
|
||||
})
|
||||
}),
|
||||
economy: z.object({
|
||||
daily: z.object({
|
||||
amount: bigIntSchema,
|
||||
streakBonus: bigIntSchema,
|
||||
cooldownMs: z.number(),
|
||||
}),
|
||||
transfers: z.object({
|
||||
allowSelfTransfer: z.boolean(),
|
||||
minAmount: bigIntSchema,
|
||||
})
|
||||
}),
|
||||
inventory: z.object({
|
||||
maxStackSize: bigIntSchema,
|
||||
maxSlots: z.number(),
|
||||
}),
|
||||
commands: z.record(z.string(), z.boolean()),
|
||||
lootdrop: z.object({
|
||||
activityWindowMs: z.number(),
|
||||
minMessages: z.number(),
|
||||
spawnChance: z.number(),
|
||||
cooldownMs: z.number(),
|
||||
reward: z.object({
|
||||
min: z.number(),
|
||||
max: z.number(),
|
||||
currency: z.string(),
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export function reloadConfig() {
|
||||
if (!existsSync(configPath)) {
|
||||
throw new Error(`Config file not found at ${configPath}`);
|
||||
@@ -82,7 +133,10 @@ reloadConfig();
|
||||
// Backwards compatibility alias
|
||||
export const GameConfig = config;
|
||||
|
||||
export function saveConfig(newConfig: GameConfigType) {
|
||||
export function saveConfig(newConfig: unknown) {
|
||||
// Validate and transform input
|
||||
const validatedConfig = configSchema.parse(newConfig);
|
||||
|
||||
const replacer = (key: string, value: any) => {
|
||||
if (typeof value === 'bigint') {
|
||||
return value.toString();
|
||||
@@ -90,7 +144,7 @@ export function saveConfig(newConfig: GameConfigType) {
|
||||
return value;
|
||||
};
|
||||
|
||||
const jsonString = JSON.stringify(newConfig, replacer, 4);
|
||||
const jsonString = JSON.stringify(validatedConfig, replacer, 4);
|
||||
writeFileSync(configPath, jsonString, 'utf-8');
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user