feat: add zod validation to config

This commit is contained in:
syntaxbullet
2025-12-18 17:22:11 +01:00
parent 7cf8d68d39
commit 1d650bb2c7

View File

@@ -1,5 +1,6 @@
import { readFileSync, existsSync, writeFileSync } from 'node:fs'; import { readFileSync, existsSync, writeFileSync } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import { z } from 'zod';
const configPath = join(import.meta.dir, '..', '..', 'config', 'config.json'); const configPath = join(import.meta.dir, '..', '..', 'config', 'config.json');
@@ -45,6 +46,56 @@ export interface GameConfigType {
// Initial default config state // Initial default config state
export const config: GameConfigType = {} as GameConfigType; 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() { export function reloadConfig() {
if (!existsSync(configPath)) { if (!existsSync(configPath)) {
throw new Error(`Config file not found at ${configPath}`); throw new Error(`Config file not found at ${configPath}`);
@@ -82,7 +133,10 @@ reloadConfig();
// Backwards compatibility alias // Backwards compatibility alias
export const GameConfig = config; 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) => { const replacer = (key: string, value: any) => {
if (typeof value === 'bigint') { if (typeof value === 'bigint') {
return value.toString(); return value.toString();
@@ -90,7 +144,7 @@ export function saveConfig(newConfig: GameConfigType) {
return value; return value;
}; };
const jsonString = JSON.stringify(newConfig, replacer, 4); const jsonString = JSON.stringify(validatedConfig, replacer, 4);
writeFileSync(configPath, jsonString, 'utf-8'); writeFileSync(configPath, jsonString, 'utf-8');
reloadConfig(); reloadConfig();
} }