mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-16 03:23:29 +01:00
feat!: use zod v4 (#10922)
* feat: zod 4 * feat: zod v3, but v4 feat: validation error class Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * chore: bump --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { AllowedMentionsTypes, ComponentType, MessageFlags, MessageReferenceType } from 'discord-api-types/v10';
|
||||
import { z } from 'zod';
|
||||
import { z } from 'zod/v4';
|
||||
import { embedPredicate } from './embed/Assertions.js';
|
||||
import { pollPredicate } from './poll/Assertions.js';
|
||||
|
||||
@@ -17,7 +17,7 @@ export const attachmentPredicate = z.object({
|
||||
|
||||
export const allowedMentionPredicate = z
|
||||
.object({
|
||||
parse: z.nativeEnum(AllowedMentionsTypes).array().optional(),
|
||||
parse: z.enum(AllowedMentionsTypes).array().optional(),
|
||||
roles: z.string().array().max(100).optional(),
|
||||
users: z.string().array().max(100).optional(),
|
||||
replied_user: z.boolean().optional(),
|
||||
@@ -29,7 +29,7 @@ export const allowedMentionPredicate = z
|
||||
(data.parse?.includes(AllowedMentionsTypes.Role) && data.roles?.length)
|
||||
),
|
||||
{
|
||||
message:
|
||||
error:
|
||||
'Cannot specify both parse: ["users"] and non-empty users array, or parse: ["roles"] and non-empty roles array. These are mutually exclusive',
|
||||
},
|
||||
);
|
||||
@@ -39,7 +39,7 @@ export const messageReferencePredicate = z.object({
|
||||
fail_if_not_exists: z.boolean().optional(),
|
||||
guild_id: z.string().optional(),
|
||||
message_id: z.string(),
|
||||
type: z.nativeEnum(MessageReferenceType).optional(),
|
||||
type: z.enum(MessageReferenceType).optional(),
|
||||
});
|
||||
|
||||
const baseMessagePredicate = z.object({
|
||||
@@ -55,13 +55,13 @@ const basicActionRowPredicate = z.object({
|
||||
type: z.literal(ComponentType.ActionRow),
|
||||
components: z
|
||||
.object({
|
||||
type: z.union([
|
||||
z.literal(ComponentType.Button),
|
||||
z.literal(ComponentType.ChannelSelect),
|
||||
z.literal(ComponentType.MentionableSelect),
|
||||
z.literal(ComponentType.RoleSelect),
|
||||
z.literal(ComponentType.StringSelect),
|
||||
z.literal(ComponentType.UserSelect),
|
||||
type: z.literal([
|
||||
ComponentType.Button,
|
||||
ComponentType.ChannelSelect,
|
||||
ComponentType.MentionableSelect,
|
||||
ComponentType.RoleSelect,
|
||||
ComponentType.StringSelect,
|
||||
ComponentType.UserSelect,
|
||||
]),
|
||||
})
|
||||
.array(),
|
||||
@@ -75,15 +75,10 @@ const messageNoComponentsV2Predicate = baseMessagePredicate
|
||||
poll: pollPredicate.optional(),
|
||||
components: basicActionRowPredicate.array().max(5).optional(),
|
||||
flags: z
|
||||
.number()
|
||||
.int()
|
||||
.optional()
|
||||
.refine((flags) => {
|
||||
// If we have flags, ensure we don't have the ComponentsV2 flag
|
||||
if (flags) {
|
||||
return (flags & MessageFlags.IsComponentsV2) === 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
.refine((flags) => !flags || (flags & MessageFlags.IsComponentsV2) === 0, {
|
||||
error: 'Cannot set content, embeds, stickers, or poll with IsComponentsV2 flag set',
|
||||
}),
|
||||
})
|
||||
.refine(
|
||||
@@ -94,22 +89,22 @@ const messageNoComponentsV2Predicate = baseMessagePredicate
|
||||
(data.attachments !== undefined && data.attachments.length > 0) ||
|
||||
(data.components !== undefined && data.components.length > 0) ||
|
||||
(data.sticker_ids !== undefined && data.sticker_ids.length > 0),
|
||||
{ message: 'Messages must have content, embeds, a poll, attachments, components or stickers' },
|
||||
{ error: 'Messages must have content, embeds, a poll, attachments, components or stickers' },
|
||||
);
|
||||
|
||||
const allTopLevelComponentsPredicate = z
|
||||
.union([
|
||||
basicActionRowPredicate,
|
||||
z.object({
|
||||
type: z.union([
|
||||
type: z.literal([
|
||||
// Components v2
|
||||
z.literal(ComponentType.Container),
|
||||
z.literal(ComponentType.File),
|
||||
z.literal(ComponentType.MediaGallery),
|
||||
z.literal(ComponentType.Section),
|
||||
z.literal(ComponentType.Separator),
|
||||
z.literal(ComponentType.TextDisplay),
|
||||
z.literal(ComponentType.Thumbnail),
|
||||
ComponentType.Container,
|
||||
ComponentType.File,
|
||||
ComponentType.MediaGallery,
|
||||
ComponentType.Section,
|
||||
ComponentType.Separator,
|
||||
ComponentType.TextDisplay,
|
||||
ComponentType.Thumbnail,
|
||||
]),
|
||||
}),
|
||||
])
|
||||
@@ -119,7 +114,9 @@ const allTopLevelComponentsPredicate = z
|
||||
|
||||
const messageComponentsV2Predicate = baseMessagePredicate.extend({
|
||||
components: allTopLevelComponentsPredicate,
|
||||
flags: z.number().refine((flags) => (flags & MessageFlags.IsComponentsV2) === MessageFlags.IsComponentsV2),
|
||||
flags: z.int().refine((flags) => (flags & MessageFlags.IsComponentsV2) === MessageFlags.IsComponentsV2, {
|
||||
error: 'Must set IsComponentsV2 flag to use Components V2',
|
||||
}),
|
||||
// These fields cannot be set
|
||||
content: z.string().length(0).nullish(),
|
||||
embeds: z.array(z.never()).nullish(),
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
import { refineURLPredicate } from '../../Assertions.js';
|
||||
import { z } from 'zod/v4';
|
||||
import { embedLength } from '../../util/componentUtil.js';
|
||||
|
||||
const namePredicate = z.string().max(256);
|
||||
|
||||
const URLPredicate = z
|
||||
.string()
|
||||
.url()
|
||||
.refine(refineURLPredicate(['http:', 'https:']), { message: 'Invalid protocol for URL. Must be http: or https:' });
|
||||
const URLPredicate = z.url({ protocol: /^https?$/ });
|
||||
|
||||
const URLWithAttachmentProtocolPredicate = z
|
||||
.string()
|
||||
.url()
|
||||
.refine(refineURLPredicate(['http:', 'https:', 'attachment:']), {
|
||||
message: 'Invalid protocol for URL. Must be http:, https:, or attachment:',
|
||||
});
|
||||
const URLWithAttachmentProtocolPredicate = z.url({ protocol: /^(?:https?|attachment)$/ });
|
||||
|
||||
export const embedFieldPredicate = z.object({
|
||||
name: namePredicate,
|
||||
@@ -39,7 +30,7 @@ export const embedPredicate = z
|
||||
description: z.string().min(1).max(4_096).optional(),
|
||||
url: URLPredicate.optional(),
|
||||
timestamp: z.string().optional(),
|
||||
color: z.number().int().min(0).max(0xffffff).optional(),
|
||||
color: z.int().min(0).max(0xffffff).optional(),
|
||||
footer: embedFooterPredicate.optional(),
|
||||
image: z.object({ url: URLWithAttachmentProtocolPredicate }).optional(),
|
||||
thumbnail: z.object({ url: URLWithAttachmentProtocolPredicate }).optional(),
|
||||
@@ -56,7 +47,7 @@ export const embedPredicate = z
|
||||
embed.image !== undefined ||
|
||||
embed.thumbnail !== undefined,
|
||||
{
|
||||
message: 'Embed must have at least a title, description, a field, a footer, an author, an image, OR a thumbnail.',
|
||||
error: 'Embed must have at least a title, description, a field, a footer, an author, an image, OR a thumbnail.',
|
||||
},
|
||||
)
|
||||
.refine((embed) => embedLength(embed) <= 6_000, { message: 'Embeds must not exceed 6000 characters in total.' });
|
||||
.refine((embed) => embedLength(embed) <= 6_000, { error: 'Embeds must not exceed 6000 characters in total.' });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PollLayoutType } from 'discord-api-types/v10';
|
||||
import { z } from 'zod';
|
||||
import { z } from 'zod/v4';
|
||||
import { emojiPredicate } from '../../components/Assertions';
|
||||
|
||||
export const pollQuestionPredicate = z.object({ text: z.string().min(1).max(300) });
|
||||
@@ -16,5 +16,5 @@ export const pollPredicate = z.object({
|
||||
answers: z.array(pollAnswerPredicate).min(1).max(10),
|
||||
duration: z.number().min(1).max(768).optional(),
|
||||
allow_multiselect: z.boolean().optional(),
|
||||
layout_type: z.nativeEnum(PollLayoutType).optional(),
|
||||
layout_type: z.enum(PollLayoutType).optional(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user