refactor: improve structure validation with zod (#10103)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Danial Raza
2024-02-21 20:19:43 +00:00
committed by GitHub
parent 906ade9cc5
commit 1d565443b0
12 changed files with 95 additions and 51 deletions

View File

@@ -19,6 +19,7 @@
"eslint": "^8.53.0",
"eslint-config-neon": "^0.1.57",
"eslint-formatter-pretty": "^5.0.0",
"prettier": "^3.1.0"
"prettier": "^3.1.0",
"zod": "^3.22.4"
}
}

View File

@@ -21,6 +21,7 @@
"eslint-config-neon": "^0.1.57",
"eslint-formatter-pretty": "^5.0.0",
"prettier": "^3.1.0",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"zod": "^3.22.4"
}
}

View File

@@ -1,4 +1,5 @@
import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'npm:discord.js@^14.14.1';
import { z } from 'npm:zod@^3.22.4';
import type { StructurePredicate } from '../util/loaders.ts';
/**
@@ -17,11 +18,16 @@ export type Command = {
execute(interaction: CommandInteraction): Promise<void> | void;
};
// Defines the predicate to check if an object is a valid Command type
/**
* Defines the schema for a command
*/
export const schema = z.object({
data: z.record(z.any()),
execute: z.function(),
});
/**
* Defines the predicate to check if an object is a valid Command type.
*/
export const predicate: StructurePredicate<Command> = (structure): structure is Command =>
Boolean(structure) &&
typeof structure === 'object' &&
'data' in structure! &&
'execute' in structure &&
typeof structure.data === 'object' &&
typeof structure.execute === 'function';
schema.safeParse(structure).success;

View File

@@ -1,4 +1,5 @@
import type { ClientEvents } from 'npm:discord.js@^14.14.1';
import { z } from 'npm:zod@^3.22.4';
import type { StructurePredicate } from '../util/loaders.ts';
/**
@@ -23,11 +24,17 @@ export type Event<T extends keyof ClientEvents = keyof ClientEvents> = {
once?: boolean;
};
// Defines the predicate to check if an object is a valid Event type.
export const predicate: StructurePredicate<Event> = (structure): structure is Event =>
Boolean(structure) &&
typeof structure === 'object' &&
'name' in structure! &&
'execute' in structure &&
typeof structure.name === 'string' &&
typeof structure.execute === 'function';
/**
* Defines the schema for an event.
*/
export const schema = z.object({
name: z.string(),
once: z.boolean().optional().default(false),
execute: z.function(),
});
/**
* Defines the predicate to check if an object is a valid Event type.
*/
export const predicate: StructurePredicate<Event> = (structure: unknown): structure is Event =>
schema.safeParse(structure).success;

View File

@@ -58,7 +58,9 @@ export async function loadStructures<T>(
const structure = (await import(`${dir}/${file}`)).default;
// If the structure is a valid structure, add it
if (predicate(structure)) structures.push(structure);
if (predicate(structure)) {
structures.push(structure);
}
}
return structures;

View File

@@ -19,6 +19,7 @@
"eslint": "^8.53.0",
"eslint-config-neon": "^0.1.57",
"eslint-formatter-pretty": "^5.0.0",
"prettier": "^3.1.0"
"prettier": "^3.1.0",
"zod": "^3.22.4"
}
}

View File

@@ -1,3 +1,5 @@
import { z } from 'zod';
/**
* Defines the structure of a command.
*
@@ -6,16 +8,18 @@
* @property {(interaction: import('discord.js').CommandInteraction) => Promise<void> | void} execute The function to execute when the command is called
*/
/**
* Defines the schema for a command
*/
export const schema = z.object({
data: z.record(z.any()),
execute: z.function(),
});
/**
* Defines the predicate to check if an object is a valid Command type.
*
* @type {import('../util/loaders.js').StructurePredicate<Command>}
* @returns {structure is Command}
*/
export const predicate = (structure) =>
Boolean(structure) &&
typeof structure === 'object' &&
'data' in structure &&
'execute' in structure &&
typeof structure.data === 'object' &&
typeof structure.execute === 'function';
export const predicate = (structure) => schema.safeParse(structure).success;

View File

@@ -1,3 +1,5 @@
import { z } from 'zod';
/**
* Defines the structure of an event.
*
@@ -8,16 +10,20 @@
* @property {boolean} [once] Whether or not the event should only be listened to once
*/
/**
* Defines the schema for an event.
*
*/
export const schema = z.object({
name: z.string(),
once: z.boolean().optional().default(false),
execute: z.function(),
});
/**
* Defines the predicate to check if an object is a valid Event type.
*
* @type {import('../util/loaders').StructurePredicate<Event>}
* @returns {structure is Event}
*/
export const predicate = (structure) =>
Boolean(structure) &&
typeof structure === 'object' &&
'name' in structure &&
'execute' in structure &&
typeof structure.name === 'string' &&
typeof structure.execute === 'function';
export const predicate = (structure) => schema.safeParse(structure).success;

View File

@@ -55,7 +55,9 @@ export async function loadStructures(dir, predicate, recursive = true) {
const structure = (await import(`${dir}/${file}`)).default;
// If the structure is a valid structure, add it
if (predicate(structure)) structures.push(structure);
if (predicate(structure)) {
structures.push(structure);
}
}
return structures;

View File

@@ -23,6 +23,7 @@
"eslint-config-neon": "^0.1.57",
"eslint-formatter-pretty": "^5.0.0",
"prettier": "^3.1.0",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"zod": "^3.22.4"
}
}

View File

@@ -1,4 +1,5 @@
import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'discord.js';
import { z } from 'zod';
import type { StructurePredicate } from '../util/loaders.[REPLACE_IMPORT_EXT]';
/**
@@ -17,11 +18,16 @@ export type Command = {
execute(interaction: CommandInteraction): Promise<void> | void;
};
// Defines the predicate to check if an object is a valid Command type
export const predicate: StructurePredicate<Command> = (structure): structure is Command =>
Boolean(structure) &&
typeof structure === 'object' &&
'data' in structure! &&
'execute' in structure &&
typeof structure.data === 'object' &&
typeof structure.execute === 'function';
/**
* Defines the schema for a command
*/
export const schema = z.object({
data: z.record(z.any()),
execute: z.function(),
});
/**
* Defines the predicate to check if an object is a valid Command type.
*/
export const predicate: StructurePredicate<Command> = (structure: unknown): structure is Command =>
schema.safeParse(structure).success;

View File

@@ -1,4 +1,5 @@
import type { ClientEvents } from 'discord.js';
import { z } from 'zod';
import type { StructurePredicate } from '../util/loaders.[REPLACE_IMPORT_EXT]';
/**
@@ -23,11 +24,17 @@ export type Event<T extends keyof ClientEvents = keyof ClientEvents> = {
once?: boolean;
};
// Defines the predicate to check if an object is a valid Event type.
export const predicate: StructurePredicate<Event> = (structure): structure is Event =>
Boolean(structure) &&
typeof structure === 'object' &&
'name' in structure! &&
'execute' in structure &&
typeof structure.name === 'string' &&
typeof structure.execute === 'function';
/**
* Defines the schema for an event.
*/
export const schema = z.object({
name: z.string(),
once: z.boolean().optional().default(false),
execute: z.function(),
});
/**
* Defines the predicate to check if an object is a valid Event type.
*/
export const predicate: StructurePredicate<Event> = (structure: unknown): structure is Event =>
schema.safeParse(structure).success;