From f45af06aaccc10c668e9b28bd374628f1c28cd15 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sun, 28 Jan 2024 22:19:57 +0100 Subject: [PATCH 1/9] refactor(builders): use zod for validation --- packages/builders/package.json | 4 +- .../builders/src/components/Assertions.ts | 80 +++++------ .../src/components/textInput/Assertions.ts | 36 ++--- .../contextMenuCommands/Assertions.ts | 37 +++--- .../src/interactions/modals/Assertions.ts | 13 +- .../interactions/slashCommands/Assertions.ts | 50 +++---- ...plicationCommandOptionChannelTypesMixin.ts | 10 +- ...ationCommandOptionWithAutocompleteMixin.ts | 4 +- ...pplicationCommandOptionWithChoicesMixin.ts | 18 +-- .../slashCommands/options/integer.ts | 4 +- .../slashCommands/options/number.ts | 4 +- .../slashCommands/options/string.ts | 6 +- .../builders/src/messages/embed/Assertions.ts | 124 ++++++++---------- pnpm-lock.yaml | 12 +- 14 files changed, 180 insertions(+), 222 deletions(-) diff --git a/packages/builders/package.json b/packages/builders/package.json index 5877a01232c5..7bb919c98a34 100644 --- a/packages/builders/package.json +++ b/packages/builders/package.json @@ -67,11 +67,11 @@ "dependencies": { "@discordjs/formatters": "workspace:^", "@discordjs/util": "workspace:^", - "@sapphire/shapeshift": "^3.9.7", "discord-api-types": "0.37.83", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", - "tslib": "^2.6.2" + "tslib": "^2.6.2", + "zod": "^3.22.4" }, "devDependencies": { "@discordjs/api-extractor": "workspace:^", diff --git a/packages/builders/src/components/Assertions.ts b/packages/builders/src/components/Assertions.ts index 960efd706c7c..a6ff6ea1a722 100644 --- a/packages/builders/src/components/Assertions.ts +++ b/packages/builders/src/components/Assertions.ts @@ -1,80 +1,60 @@ -import { s } from '@sapphire/shapeshift'; import { ButtonStyle, ChannelType, type APIMessageComponentEmoji } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../util/validation.js'; +import { z } from 'zod'; import { StringSelectMenuOptionBuilder } from './selectMenu/StringSelectMenuOption.js'; -export const customIdValidator = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(100) - .setValidationEnabled(isValidationEnabled); +export const customIdValidator = z.string().min(1).max(100); -export const emojiValidator = s +export const emojiValidator = z .object({ - id: s.string, - name: s.string, - animated: s.boolean, + id: z.string(), + name: z.string(), + animated: z.boolean(), }) - .partial.strict.setValidationEnabled(isValidationEnabled); + .partial() + .strict(); -export const disabledValidator = s.boolean; +export const disabledValidator = z.boolean(); -export const buttonLabelValidator = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(80) - .setValidationEnabled(isValidationEnabled); +export const buttonLabelValidator = z.string().min(1).max(80); -export const buttonStyleValidator = s.nativeEnum(ButtonStyle); +export const buttonStyleValidator = z.nativeEnum(ButtonStyle); -export const placeholderValidator = s.string.lengthLessThanOrEqual(150).setValidationEnabled(isValidationEnabled); -export const minMaxValidator = s.number.int - .greaterThanOrEqual(0) - .lessThanOrEqual(25) - .setValidationEnabled(isValidationEnabled); +export const placeholderValidator = z.string().max(150); +export const minMaxValidator = z.number().int().gte(0).lte(25); -export const labelValueDescriptionValidator = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(100) - .setValidationEnabled(isValidationEnabled); +export const labelValueDescriptionValidator = z.string().min(1).max(100); -export const jsonOptionValidator = s - .object({ - label: labelValueDescriptionValidator, - value: labelValueDescriptionValidator, - description: labelValueDescriptionValidator.optional, - emoji: emojiValidator.optional, - default: s.boolean.optional, - }) - .setValidationEnabled(isValidationEnabled); +export const jsonOptionValidator = z.object({ + label: labelValueDescriptionValidator, + value: labelValueDescriptionValidator, + description: labelValueDescriptionValidator.optional(), + emoji: emojiValidator.optional(), + default: z.boolean().optional(), +}); -export const optionValidator = s.instance(StringSelectMenuOptionBuilder).setValidationEnabled(isValidationEnabled); +export const optionValidator = z.instanceof(StringSelectMenuOptionBuilder); -export const optionsValidator = optionValidator.array - .lengthGreaterThanOrEqual(0) - .setValidationEnabled(isValidationEnabled); -export const optionsLengthValidator = s.number.int - .greaterThanOrEqual(0) - .lessThanOrEqual(25) - .setValidationEnabled(isValidationEnabled); +export const optionsValidator = optionValidator.array().min(0); +export const optionsLengthValidator = z.number().int().gte(0).lte(25); export function validateRequiredSelectMenuParameters(options: StringSelectMenuOptionBuilder[], customId?: string) { customIdValidator.parse(customId); optionsValidator.parse(options); } -export const defaultValidator = s.boolean; +export const defaultValidator = z.boolean(); export function validateRequiredSelectMenuOptionParameters(label?: string, value?: string) { labelValueDescriptionValidator.parse(label); labelValueDescriptionValidator.parse(value); } -export const channelTypesValidator = s.nativeEnum(ChannelType).array.setValidationEnabled(isValidationEnabled); +export const channelTypesValidator = z.nativeEnum(ChannelType).array(); -export const urlValidator = s.string - .url({ - allowedProtocols: ['http:', 'https:', 'discord:'], - }) - .setValidationEnabled(isValidationEnabled); +export const urlValidator = z + .string() + .url() + .regex(/^(?https?|discord):\/\//); export function validateRequiredButtonParameters( style?: ButtonStyle, diff --git a/packages/builders/src/components/textInput/Assertions.ts b/packages/builders/src/components/textInput/Assertions.ts index f0cfc4a3f802..482670bba89c 100644 --- a/packages/builders/src/components/textInput/Assertions.ts +++ b/packages/builders/src/components/textInput/Assertions.ts @@ -1,24 +1,24 @@ -import { s } from '@sapphire/shapeshift'; import { TextInputStyle } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../../util/validation.js'; +import { z } from 'zod'; import { customIdValidator } from '../Assertions.js'; -export const textInputStyleValidator = s.nativeEnum(TextInputStyle); -export const minLengthValidator = s.number.int - .greaterThanOrEqual(0) - .lessThanOrEqual(4_000) - .setValidationEnabled(isValidationEnabled); -export const maxLengthValidator = s.number.int - .greaterThanOrEqual(1) - .lessThanOrEqual(4_000) - .setValidationEnabled(isValidationEnabled); -export const requiredValidator = s.boolean; -export const valueValidator = s.string.lengthLessThanOrEqual(4_000).setValidationEnabled(isValidationEnabled); -export const placeholderValidator = s.string.lengthLessThanOrEqual(100).setValidationEnabled(isValidationEnabled); -export const labelValidator = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(45) - .setValidationEnabled(isValidationEnabled); +export const textInputStyleValidator = z.union([ + z.nativeEnum(TextInputStyle), + z + .enum( + Object.values(TextInputStyle).filter((value) => typeof value === 'string') as [ + keyof typeof TextInputStyle, + ...(keyof typeof TextInputStyle)[], + ], + ) + .transform((key) => TextInputStyle[key]), +]); +export const minLengthValidator = z.number().int().gte(0).lte(4_000); +export const maxLengthValidator = z.number().int().gte(1).lte(4_000); +export const requiredValidator = z.boolean(); +export const valueValidator = z.string().max(4_000); +export const placeholderValidator = z.string().max(100); +export const labelValidator = z.string().min(1).max(45); export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) { customIdValidator.parse(customId); diff --git a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts index feb97af4f521..3d0c619dc0e9 100644 --- a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts +++ b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts @@ -1,18 +1,15 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandType } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../../util/validation.js'; +import { z } from 'zod'; import type { ContextMenuCommandType } from './ContextMenuCommandBuilder.js'; -const namePredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(32) +const namePredicate = z + .string() + .min(1) + .max(32) // eslint-disable-next-line prefer-named-capture-group - .regex(/^( *[\p{P}\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}]+ *)+$/u) - .setValidationEnabled(isValidationEnabled); -const typePredicate = s - .union(s.literal(ApplicationCommandType.User), s.literal(ApplicationCommandType.Message)) - .setValidationEnabled(isValidationEnabled); -const booleanPredicate = s.boolean; + .regex(/^( *[\p{P}\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}]+ *)+$/u); +const typePredicate = z.union([z.literal(ApplicationCommandType.User), z.literal(ApplicationCommandType.Message)]); +const booleanPredicate = z.boolean(); export function validateDefaultPermission(value: unknown): asserts value is boolean { booleanPredicate.parse(value); @@ -34,17 +31,23 @@ export function validateRequiredParameters(name: string, type: number) { validateType(type); } -const dmPermissionPredicate = s.boolean.nullish; +const dmPermissionPredicate = z.boolean().nullish(); export function validateDMPermission(value: unknown): asserts value is boolean | null | undefined { dmPermissionPredicate.parse(value); } -const memberPermissionPredicate = s.union( - s.bigint.transform((value) => value.toString()), - s.number.safeInt.transform((value) => value.toString()), - s.string.regex(/^\d+$/), -).nullish; +const memberPermissionPredicate = z + .union([ + z.bigint().transform((value) => value.toString()), + z + .number() + .int() + .safe() + .transform((value) => value.toString()), + z.string().regex(/^\d+$/), + ]) + .nullish(); export function validateDefaultMemberPermissions(permissions: unknown) { return memberPermissionPredicate.parse(permissions); diff --git a/packages/builders/src/interactions/modals/Assertions.ts b/packages/builders/src/interactions/modals/Assertions.ts index a59bb24cfe62..d534f3c843c8 100644 --- a/packages/builders/src/interactions/modals/Assertions.ts +++ b/packages/builders/src/interactions/modals/Assertions.ts @@ -1,16 +1,9 @@ -import { s } from '@sapphire/shapeshift'; +import { z } from 'zod'; import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js'; import { customIdValidator } from '../../components/Assertions.js'; -import { isValidationEnabled } from '../../util/validation.js'; -export const titleValidator = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(45) - .setValidationEnabled(isValidationEnabled); -export const componentsValidator = s - .instance(ActionRowBuilder) - .array.lengthGreaterThanOrEqual(1) - .setValidationEnabled(isValidationEnabled); +export const titleValidator = z.string().min(1).max(45); +export const componentsValidator = z.instanceof(ActionRowBuilder).array().min(1); export function validateRequiredParameters( customId?: string, diff --git a/packages/builders/src/interactions/slashCommands/Assertions.ts b/packages/builders/src/interactions/slashCommands/Assertions.ts index f98e5be3ad5c..1a1f87dafdc6 100644 --- a/packages/builders/src/interactions/slashCommands/Assertions.ts +++ b/packages/builders/src/interactions/slashCommands/Assertions.ts @@ -1,31 +1,27 @@ -import { s } from '@sapphire/shapeshift'; import { Locale, type APIApplicationCommandOptionChoice, type LocalizationMap } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../../util/validation.js'; +import { z } from 'zod'; import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder.js'; import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands.js'; import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase.js'; -const namePredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(32) - .regex(/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u) - .setValidationEnabled(isValidationEnabled); +const namePredicate = z + .string() + .min(1) + .max(32) + .regex(/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u); export function validateName(name: unknown): asserts name is string { namePredicate.parse(name); } -const descriptionPredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(100) - .setValidationEnabled(isValidationEnabled); -const localePredicate = s.nativeEnum(Locale); +const descriptionPredicate = z.string().min(1).max(100); +const localePredicate = z.nativeEnum(Locale); export function validateDescription(description: unknown): asserts description is string { descriptionPredicate.parse(description); } -const maxArrayLengthPredicate = s.unknown.array.lengthLessThanOrEqual(25).setValidationEnabled(isValidationEnabled); +const maxArrayLengthPredicate = z.unknown().array().max(25); export function validateLocale(locale: unknown) { return localePredicate.parse(locale); } @@ -49,7 +45,7 @@ export function validateRequiredParameters( validateMaxOptionsLength(options); } -const booleanPredicate = s.boolean; +const booleanPredicate = z.boolean(); export function validateDefaultPermission(value: unknown): asserts value is boolean { booleanPredicate.parse(value); @@ -59,7 +55,7 @@ export function validateRequired(required: unknown): asserts required is boolean booleanPredicate.parse(required); } -const choicesLengthPredicate = s.number.lessThanOrEqual(25).setValidationEnabled(isValidationEnabled); +const choicesLengthPredicate = z.number().max(25); export function validateChoicesLength(amountAdding: number, choices?: APIApplicationCommandOptionChoice[]): void { choicesLengthPredicate.parse((choices?.length ?? 0) + amountAdding); @@ -68,28 +64,32 @@ export function validateChoicesLength(amountAdding: number, choices?: APIApplica export function assertReturnOfBuilder< ReturnType extends ApplicationCommandOptionBase | SlashCommandSubcommandBuilder | SlashCommandSubcommandGroupBuilder, >(input: unknown, ExpectedInstanceOf: new () => ReturnType): asserts input is ReturnType { - s.instance(ExpectedInstanceOf).parse(input); + z.instanceof(ExpectedInstanceOf).parse(input); } -export const localizationMapPredicate = s - .object(Object.fromEntries(Object.values(Locale).map((locale) => [locale, s.string.nullish]))) - .strict.nullish.setValidationEnabled(isValidationEnabled); +export const localizationMapPredicate = z.record(z.nativeEnum(Locale), z.string().nullish()).nullish(); export function validateLocalizationMap(value: unknown): asserts value is LocalizationMap { localizationMapPredicate.parse(value); } -const dmPermissionPredicate = s.boolean.nullish; +const dmPermissionPredicate = z.boolean().nullish(); export function validateDMPermission(value: unknown): asserts value is boolean | null | undefined { dmPermissionPredicate.parse(value); } -const memberPermissionPredicate = s.union( - s.bigint.transform((value) => value.toString()), - s.number.safeInt.transform((value) => value.toString()), - s.string.regex(/^\d+$/), -).nullish; +const memberPermissionPredicate = z + .union([ + z.bigint().transform((value) => value.toString()), + z + .number() + .int() + .safe() + .transform((value) => value.toString()), + z.string().regex(/^\d+$/), + ]) + .nullish(); export function validateDefaultMemberPermissions(permissions: unknown) { return memberPermissionPredicate.parse(permissions); diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts index 83cb16b2b12c..df61a58b18fc 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts @@ -1,6 +1,6 @@ -import { s } from '@sapphire/shapeshift'; import { ChannelType } from 'discord-api-types/v10'; import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray'; +import { z } from 'zod'; /** * The allowed channel types used for a channel option in a slash command builder. @@ -26,7 +26,13 @@ const allowedChannelTypes = [ */ export type ApplicationCommandOptionAllowedChannelTypes = (typeof allowedChannelTypes)[number]; -const channelTypesPredicate = s.array(s.union(...allowedChannelTypes.map((type) => s.literal(type)))); +const channelTypesPredicate = z.array( + z.union([ + z.literal(allowedChannelTypes[0]), + z.literal(allowedChannelTypes[1]), + ...allowedChannelTypes.slice(2).map((type) => z.literal(type)), + ]), +); /** * This mixin holds channel type symbols used for options. diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts index 550768f3b8d7..e854d02a0f2f 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts @@ -1,7 +1,7 @@ -import { s } from '@sapphire/shapeshift'; import type { ApplicationCommandOptionType } from 'discord-api-types/v10'; +import { z } from 'zod'; -const booleanPredicate = s.boolean; +const booleanPredicate = z.boolean(); /** * This mixin holds choices and autocomplete symbols used for options. diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts index 6bc75d24a44e..4871e4a6c237 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts @@ -1,15 +1,17 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10'; import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js'; +import { z } from 'zod'; import { localizationMapPredicate, validateChoicesLength } from '../Assertions.js'; -const stringPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100); -const numberPredicate = s.number.greaterThan(Number.NEGATIVE_INFINITY).lessThan(Number.POSITIVE_INFINITY); -const choicesPredicate = s.object({ - name: stringPredicate, - name_localizations: localizationMapPredicate, - value: s.union(stringPredicate, numberPredicate), -}).array; +const stringPredicate = z.string().min(1).max(100); +const numberPredicate = z.number().gt(Number.NEGATIVE_INFINITY).lt(Number.POSITIVE_INFINITY); +const choicesPredicate = z + .object({ + name: stringPredicate, + name_localizations: localizationMapPredicate, + value: z.union([stringPredicate, numberPredicate]), + }) + .array(); /** * This mixin holds choices and autocomplete symbols used for options. diff --git a/packages/builders/src/interactions/slashCommands/options/integer.ts b/packages/builders/src/interactions/slashCommands/options/integer.ts index ed2c0291da52..0bb1c515cc4a 100644 --- a/packages/builders/src/interactions/slashCommands/options/integer.ts +++ b/packages/builders/src/interactions/slashCommands/options/integer.ts @@ -1,12 +1,12 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandOptionType, type APIApplicationCommandIntegerOption } from 'discord-api-types/v10'; import { mix } from 'ts-mixer'; +import { z } from 'zod'; import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js'; import { ApplicationCommandOptionWithAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithAutocompleteMixin.js'; import { ApplicationCommandOptionWithChoicesMixin } from '../mixins/ApplicationCommandOptionWithChoicesMixin.js'; -const numberValidator = s.number.int; +const numberValidator = z.number().int(); /** * A slash command integer option. diff --git a/packages/builders/src/interactions/slashCommands/options/number.ts b/packages/builders/src/interactions/slashCommands/options/number.ts index 59e1505d1029..0e9144ac5150 100644 --- a/packages/builders/src/interactions/slashCommands/options/number.ts +++ b/packages/builders/src/interactions/slashCommands/options/number.ts @@ -1,12 +1,12 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandOptionType, type APIApplicationCommandNumberOption } from 'discord-api-types/v10'; import { mix } from 'ts-mixer'; +import { z } from 'zod'; import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js'; import { ApplicationCommandOptionWithAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithAutocompleteMixin.js'; import { ApplicationCommandOptionWithChoicesMixin } from '../mixins/ApplicationCommandOptionWithChoicesMixin.js'; -const numberValidator = s.number; +const numberValidator = z.number(); /** * A slash command number option. diff --git a/packages/builders/src/interactions/slashCommands/options/string.ts b/packages/builders/src/interactions/slashCommands/options/string.ts index 71c3c682fd9a..0cba225a8b11 100644 --- a/packages/builders/src/interactions/slashCommands/options/string.ts +++ b/packages/builders/src/interactions/slashCommands/options/string.ts @@ -1,12 +1,12 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandOptionType, type APIApplicationCommandStringOption } from 'discord-api-types/v10'; import { mix } from 'ts-mixer'; +import { z } from 'zod'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js'; import { ApplicationCommandOptionWithAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithAutocompleteMixin.js'; import { ApplicationCommandOptionWithChoicesMixin } from '../mixins/ApplicationCommandOptionWithChoicesMixin.js'; -const minLengthValidator = s.number.greaterThanOrEqual(0).lessThanOrEqual(6_000); -const maxLengthValidator = s.number.greaterThanOrEqual(1).lessThanOrEqual(6_000); +const minLengthValidator = z.number().min(0).max(6_000); +const maxLengthValidator = z.number().min(1).max(6_000); /** * A slash command string option. diff --git a/packages/builders/src/messages/embed/Assertions.ts b/packages/builders/src/messages/embed/Assertions.ts index fe2bcb308274..be5020e7141e 100644 --- a/packages/builders/src/messages/embed/Assertions.ts +++ b/packages/builders/src/messages/embed/Assertions.ts @@ -1,84 +1,64 @@ -import { s } from '@sapphire/shapeshift'; import type { APIEmbedField } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../../util/validation.js'; +import { z } from 'zod'; -export const fieldNamePredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(256) - .setValidationEnabled(isValidationEnabled); +export const fieldNamePredicate = z.string().min(1).max(256); -export const fieldValuePredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(1_024) - .setValidationEnabled(isValidationEnabled); +export const fieldValuePredicate = z.string().min(1).max(1_024); -export const fieldInlinePredicate = s.boolean.optional; +export const fieldInlinePredicate = z.boolean().optional(); -export const embedFieldPredicate = s - .object({ - name: fieldNamePredicate, - value: fieldValuePredicate, - inline: fieldInlinePredicate, - }) - .setValidationEnabled(isValidationEnabled); +export const embedFieldPredicate = z.object({ + name: fieldNamePredicate, + value: fieldValuePredicate, + inline: fieldInlinePredicate, +}); -export const embedFieldsArrayPredicate = embedFieldPredicate.array.setValidationEnabled(isValidationEnabled); +export const embedFieldsArrayPredicate = embedFieldPredicate.array(); -export const fieldLengthPredicate = s.number.lessThanOrEqual(25).setValidationEnabled(isValidationEnabled); +export const fieldLengthPredicate = z.number().max(25); export function validateFieldLength(amountAdding: number, fields?: APIEmbedField[]): void { fieldLengthPredicate.parse((fields?.length ?? 0) + amountAdding); } -export const authorNamePredicate = fieldNamePredicate.nullable.setValidationEnabled(isValidationEnabled); - -export const imageURLPredicate = s.string - .url({ - allowedProtocols: ['http:', 'https:', 'attachment:'], - }) - .nullish.setValidationEnabled(isValidationEnabled); - -export const urlPredicate = s.string - .url({ - allowedProtocols: ['http:', 'https:'], - }) - .nullish.setValidationEnabled(isValidationEnabled); - -export const embedAuthorPredicate = s - .object({ - name: authorNamePredicate, - iconURL: imageURLPredicate, - url: urlPredicate, - }) - .setValidationEnabled(isValidationEnabled); - -export const RGBPredicate = s.number.int - .greaterThanOrEqual(0) - .lessThanOrEqual(255) - .setValidationEnabled(isValidationEnabled); -export const colorPredicate = s.number.int - .greaterThanOrEqual(0) - .lessThanOrEqual(0xffffff) - .or(s.tuple([RGBPredicate, RGBPredicate, RGBPredicate])) - .nullable.setValidationEnabled(isValidationEnabled); - -export const descriptionPredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(4_096) - .nullable.setValidationEnabled(isValidationEnabled); - -export const footerTextPredicate = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(2_048) - .nullable.setValidationEnabled(isValidationEnabled); - -export const embedFooterPredicate = s - .object({ - text: footerTextPredicate, - iconURL: imageURLPredicate, - }) - .setValidationEnabled(isValidationEnabled); - -export const timestampPredicate = s.union(s.number, s.date).nullable.setValidationEnabled(isValidationEnabled); - -export const titlePredicate = fieldNamePredicate.nullable.setValidationEnabled(isValidationEnabled); +export const authorNamePredicate = fieldNamePredicate.nullable(); + +export const imageURLPredicate = z + .string() + .url() + .regex(/^(?https?|attachment):\/\//) + .nullish(); + +export const urlPredicate = z + .string() + .url() + .regex(/^https?:\/\//) + .nullish(); + +export const embedAuthorPredicate = z.object({ + name: authorNamePredicate, + iconURL: imageURLPredicate, + url: urlPredicate, +}); + +export const RGBPredicate = z.number().int().min(0).max(255); +export const colorPredicate = z + .number() + .int() + .min(0) + .max(0xffffff) + .or(z.tuple([RGBPredicate, RGBPredicate, RGBPredicate])) + .nullable(); + +export const descriptionPredicate = z.string().min(1).max(4_096).nullable(); + +export const footerTextPredicate = z.string().min(1).max(2_048).nullable(); + +export const embedFooterPredicate = z.object({ + text: footerTextPredicate, + iconURL: imageURLPredicate, +}); + +export const timestampPredicate = z.union([z.number(), z.date()]).nullable(); + +export const titlePredicate = fieldNamePredicate.nullable(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee173c6e40a5..ed25267373ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -676,9 +676,6 @@ importers: '@discordjs/util': specifier: workspace:^ version: link:../util - '@sapphire/shapeshift': - specifier: ^3.9.7 - version: 3.9.7 discord-api-types: specifier: 0.37.83 version: 0.37.83 @@ -691,6 +688,9 @@ importers: tslib: specifier: ^2.6.2 version: 2.6.2 + zod: + specifier: ^3.22.4 + version: 3.22.4 devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -18908,11 +18908,6 @@ snapshots: '@sapphire/result@2.6.6': {} - '@sapphire/shapeshift@3.9.7': - dependencies: - fast-deep-equal: 3.1.3 - lodash: 4.17.21 - '@sapphire/snowflake@3.5.3': {} '@sapphire/utilities@3.13.0': {} @@ -30547,7 +30542,6 @@ snapshots: optionalDependencies: vite: 5.2.9(@types/node@20.12.7)(terser@5.29.1) transitivePeerDependencies: - - postcss - rollup - supports-color From f1698443f2f06d12a51ad5557d5886edce7ca046 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sun, 4 Feb 2024 12:35:23 +0100 Subject: [PATCH 2/9] fix(builders): disableValidators working again --- packages/builders/package.json | 3 ++- .../builders/src/components/Assertions.ts | 9 ++++--- .../builders/src/components/button/Button.ts | 13 +++++----- .../components/selectMenu/BaseSelectMenu.ts | 13 +++++----- .../selectMenu/ChannelSelectMenu.ts | 11 ++++---- .../selectMenu/MentionableSelectMenu.ts | 9 ++++--- .../components/selectMenu/RoleSelectMenu.ts | 5 ++-- .../components/selectMenu/StringSelectMenu.ts | 9 ++++--- .../selectMenu/StringSelectMenuOption.ts | 11 ++++---- .../components/selectMenu/UserSelectMenu.ts | 5 ++-- .../src/components/textInput/Assertions.ts | 7 +++--- .../src/components/textInput/TextInput.ts | 17 +++++++------ .../contextMenuCommands/Assertions.ts | 11 ++++---- .../src/interactions/modals/Assertions.ts | 7 +++--- .../builders/src/interactions/modals/Modal.ts | 5 ++-- .../interactions/slashCommands/Assertions.ts | 25 ++++++++++--------- ...plicationCommandOptionChannelTypesMixin.ts | 5 ++-- ...ationCommandOptionWithAutocompleteMixin.ts | 3 ++- ...pplicationCommandOptionWithChoicesMixin.ts | 11 ++++---- .../slashCommands/options/integer.ts | 5 ++-- .../slashCommands/options/number.ts | 5 ++-- .../slashCommands/options/string.ts | 5 ++-- .../builders/src/messages/embed/Assertions.ts | 3 ++- packages/builders/src/messages/embed/Embed.ts | 24 ++++++++++-------- packages/builders/src/util/validation.ts | 20 +++++++++++++++ pnpm-lock.yaml | 17 +++++++++++-- 26 files changed, 158 insertions(+), 100 deletions(-) diff --git a/packages/builders/package.json b/packages/builders/package.json index 7bb919c98a34..892bae56a882 100644 --- a/packages/builders/package.json +++ b/packages/builders/package.json @@ -71,7 +71,8 @@ "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.2", - "zod": "^3.22.4" + "zod": "^3.22.4", + "zod-validation-error": "^3.0.0" }, "devDependencies": { "@discordjs/api-extractor": "workspace:^", diff --git a/packages/builders/src/components/Assertions.ts b/packages/builders/src/components/Assertions.ts index a6ff6ea1a722..7ae832673447 100644 --- a/packages/builders/src/components/Assertions.ts +++ b/packages/builders/src/components/Assertions.ts @@ -1,5 +1,6 @@ import { ButtonStyle, ChannelType, type APIMessageComponentEmoji } from 'discord-api-types/v10'; import { z } from 'zod'; +import { parse } from '../util/validation.js'; import { StringSelectMenuOptionBuilder } from './selectMenu/StringSelectMenuOption.js'; export const customIdValidator = z.string().min(1).max(100); @@ -38,15 +39,15 @@ export const optionsValidator = optionValidator.array().min(0); export const optionsLengthValidator = z.number().int().gte(0).lte(25); export function validateRequiredSelectMenuParameters(options: StringSelectMenuOptionBuilder[], customId?: string) { - customIdValidator.parse(customId); - optionsValidator.parse(options); + parse(customIdValidator, customId); + parse(optionsValidator, options); } export const defaultValidator = z.boolean(); export function validateRequiredSelectMenuOptionParameters(label?: string, value?: string) { - labelValueDescriptionValidator.parse(label); - labelValueDescriptionValidator.parse(value); + parse(labelValueDescriptionValidator, label); + parse(labelValueDescriptionValidator, value); } export const channelTypesValidator = z.nativeEnum(ChannelType).array(); diff --git a/packages/builders/src/components/button/Button.ts b/packages/builders/src/components/button/Button.ts index 30aad629b8a8..ff1ef79594c0 100644 --- a/packages/builders/src/components/button/Button.ts +++ b/packages/builders/src/components/button/Button.ts @@ -6,6 +6,7 @@ import { type APIButtonComponentWithCustomId, type ButtonStyle, } from 'discord-api-types/v10'; +import { parse } from '../../util/validation.js'; import { buttonLabelValidator, buttonStyleValidator, @@ -59,7 +60,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param style - The style to use */ public setStyle(style: ButtonStyle) { - this.data.style = buttonStyleValidator.parse(style); + this.data.style = parse(buttonStyleValidator, style); return this; } @@ -72,7 +73,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param url - The URL to use */ public setURL(url: string) { - (this.data as APIButtonComponentWithURL).url = urlValidator.parse(url); + (this.data as APIButtonComponentWithURL).url = parse(urlValidator, url); return this; } @@ -84,7 +85,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param customId - The custom id to use */ public setCustomId(customId: string) { - (this.data as APIButtonComponentWithCustomId).custom_id = customIdValidator.parse(customId); + (this.data as APIButtonComponentWithCustomId).custom_id = parse(customIdValidator, customId); return this; } @@ -94,7 +95,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param emoji - The emoji to use */ public setEmoji(emoji: APIMessageComponentEmoji) { - this.data.emoji = emojiValidator.parse(emoji); + this.data.emoji = parse(emojiValidator, emoji); return this; } @@ -104,7 +105,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param disabled - Whether to disable this button */ public setDisabled(disabled = true) { - this.data.disabled = disabledValidator.parse(disabled); + this.data.disabled = parse(disabledValidator, disabled); return this; } @@ -114,7 +115,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param label - The label to use */ public setLabel(label: string) { - this.data.label = buttonLabelValidator.parse(label); + this.data.label = parse(buttonLabelValidator, label); return this; } diff --git a/packages/builders/src/components/selectMenu/BaseSelectMenu.ts b/packages/builders/src/components/selectMenu/BaseSelectMenu.ts index 298d7dc5e1fd..a53a26be295c 100644 --- a/packages/builders/src/components/selectMenu/BaseSelectMenu.ts +++ b/packages/builders/src/components/selectMenu/BaseSelectMenu.ts @@ -1,4 +1,5 @@ import type { APISelectMenuComponent } from 'discord-api-types/v10'; +import { parse } from '../../util/validation.js'; import { customIdValidator, disabledValidator, minMaxValidator, placeholderValidator } from '../Assertions.js'; import { ComponentBuilder } from '../Component.js'; @@ -16,7 +17,7 @@ export abstract class BaseSelectMenuBuilder< * @param placeholder - The placeholder to use */ public setPlaceholder(placeholder: string) { - this.data.placeholder = placeholderValidator.parse(placeholder); + this.data.placeholder = parse(placeholderValidator, placeholder); return this; } @@ -26,7 +27,7 @@ export abstract class BaseSelectMenuBuilder< * @param minValues - The minimum values that must be selected */ public setMinValues(minValues: number) { - this.data.min_values = minMaxValidator.parse(minValues); + this.data.min_values = parse(minMaxValidator, minValues); return this; } @@ -36,7 +37,7 @@ export abstract class BaseSelectMenuBuilder< * @param maxValues - The maximum values that must be selected */ public setMaxValues(maxValues: number) { - this.data.max_values = minMaxValidator.parse(maxValues); + this.data.max_values = parse(minMaxValidator, maxValues); return this; } @@ -46,7 +47,7 @@ export abstract class BaseSelectMenuBuilder< * @param customId - The custom id to use */ public setCustomId(customId: string) { - this.data.custom_id = customIdValidator.parse(customId); + this.data.custom_id = parse(customIdValidator, customId); return this; } @@ -56,7 +57,7 @@ export abstract class BaseSelectMenuBuilder< * @param disabled - Whether this select menu is disabled */ public setDisabled(disabled = true) { - this.data.disabled = disabledValidator.parse(disabled); + this.data.disabled = parse(disabledValidator, disabled); return this; } @@ -64,7 +65,7 @@ export abstract class BaseSelectMenuBuilder< * {@inheritDoc ComponentBuilder.toJSON} */ public toJSON(): SelectMenuType { - customIdValidator.parse(this.data.custom_id); + parse(customIdValidator, this.data.custom_id); return { ...this.data, } as SelectMenuType; diff --git a/packages/builders/src/components/selectMenu/ChannelSelectMenu.ts b/packages/builders/src/components/selectMenu/ChannelSelectMenu.ts index 35dfb9a53a0b..91d9b70dbd1a 100644 --- a/packages/builders/src/components/selectMenu/ChannelSelectMenu.ts +++ b/packages/builders/src/components/selectMenu/ChannelSelectMenu.ts @@ -6,6 +6,7 @@ import { SelectMenuDefaultValueType, } from 'discord-api-types/v10'; import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js'; +import { parse } from '../../util/validation.js'; import { channelTypesValidator, customIdValidator, optionsLengthValidator } from '../Assertions.js'; import { BaseSelectMenuBuilder } from './BaseSelectMenu.js'; @@ -48,7 +49,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedTypes = normalizeArray(types); this.data.channel_types ??= []; - this.data.channel_types.push(...channelTypesValidator.parse(normalizedTypes)); + this.data.channel_types.push(...parse(channelTypesValidator, normalizedTypes)); return this; } @@ -60,7 +61,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedTypes = normalizeArray(types); this.data.channel_types ??= []; - this.data.channel_types.splice(0, this.data.channel_types.length, ...channelTypesValidator.parse(normalizedTypes)); + this.data.channel_types.splice(0, this.data.channel_types.length, ...parse(channelTypesValidator, normalizedTypes)); return this; } @@ -71,7 +72,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(channels); - optionsLengthValidator.parse((this.data.default_values?.length ?? 0) + normalizedValues.length); + parse(optionsLengthValidator, (this.data.default_values?.length ?? 0) + normalizedValues.length); this.data.default_values ??= []; this.data.default_values.push( @@ -91,7 +92,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(channels); - optionsLengthValidator.parse(normalizedValues.length); + parse(optionsLengthValidator, normalizedValues.length); this.data.default_values = normalizedValues.map((id) => ({ id, @@ -105,7 +106,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(roles); - optionsLengthValidator.parse((this.data.default_values?.length ?? 0) + normalizedValues.length); + parse(optionsLengthValidator, (this.data.default_values?.length ?? 0) + normalizedValues.length); this.data.default_values ??= []; this.data.default_values.push( @@ -66,7 +67,7 @@ export class MentionableSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(users); - optionsLengthValidator.parse((this.data.default_values?.length ?? 0) + normalizedValues.length); + parse(optionsLengthValidator, (this.data.default_values?.length ?? 0) + normalizedValues.length); this.data.default_values ??= []; this.data.default_values.push( @@ -91,7 +92,7 @@ export class MentionableSelectMenuBuilder extends BaseSelectMenuBuilder ) { const normalizedValues = normalizeArray(values); - optionsLengthValidator.parse((this.data.default_values?.length ?? 0) + normalizedValues.length); + parse(optionsLengthValidator, (this.data.default_values?.length ?? 0) + normalizedValues.length); this.data.default_values ??= []; this.data.default_values.push(...normalizedValues); return this; @@ -109,7 +110,7 @@ export class MentionableSelectMenuBuilder extends BaseSelectMenuBuilder ) { const normalizedValues = normalizeArray(values); - optionsLengthValidator.parse(normalizedValues.length); + parse(optionsLengthValidator, normalizedValues.length); this.data.default_values = normalizedValues; return this; } diff --git a/packages/builders/src/components/selectMenu/RoleSelectMenu.ts b/packages/builders/src/components/selectMenu/RoleSelectMenu.ts index f3ae4ab01421..a5fea7f11577 100644 --- a/packages/builders/src/components/selectMenu/RoleSelectMenu.ts +++ b/packages/builders/src/components/selectMenu/RoleSelectMenu.ts @@ -5,6 +5,7 @@ import { SelectMenuDefaultValueType, } from 'discord-api-types/v10'; import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js'; +import { parse } from '../../util/validation.js'; import { optionsLengthValidator } from '../Assertions.js'; import { BaseSelectMenuBuilder } from './BaseSelectMenu.js'; @@ -45,7 +46,7 @@ export class RoleSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(roles); - optionsLengthValidator.parse((this.data.default_values?.length ?? 0) + normalizedValues.length); + parse(optionsLengthValidator, (this.data.default_values?.length ?? 0) + normalizedValues.length); this.data.default_values ??= []; this.data.default_values.push( @@ -65,7 +66,7 @@ export class RoleSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(roles); - optionsLengthValidator.parse(normalizedValues.length); + parse(optionsLengthValidator, normalizedValues.length); this.data.default_values = normalizedValues.map((id) => ({ id, diff --git a/packages/builders/src/components/selectMenu/StringSelectMenu.ts b/packages/builders/src/components/selectMenu/StringSelectMenu.ts index 9c6542387db0..7425b1a6dfa2 100644 --- a/packages/builders/src/components/selectMenu/StringSelectMenu.ts +++ b/packages/builders/src/components/selectMenu/StringSelectMenu.ts @@ -1,6 +1,7 @@ import { ComponentType } from 'discord-api-types/v10'; import type { APIStringSelectComponent, APISelectMenuOption } from 'discord-api-types/v10'; import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js'; +import { parse } from '../../util/validation.js'; import { jsonOptionValidator, optionsLengthValidator, validateRequiredSelectMenuParameters } from '../Assertions.js'; import { BaseSelectMenuBuilder } from './BaseSelectMenu.js'; import { StringSelectMenuOptionBuilder } from './StringSelectMenuOption.js'; @@ -58,12 +59,12 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedOptions = normalizeArray(options); - optionsLengthValidator.parse(this.options.length + normalizedOptions.length); + parse(optionsLengthValidator, this.options.length + normalizedOptions.length); this.options.push( ...normalizedOptions.map((normalizedOption) => normalizedOption instanceof StringSelectMenuOptionBuilder ? normalizedOption - : new StringSelectMenuOptionBuilder(jsonOptionValidator.parse(normalizedOption)), + : new StringSelectMenuOptionBuilder(parse(jsonOptionValidator, normalizedOption)), ), ); return this; @@ -120,11 +121,11 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder normalizedOption instanceof StringSelectMenuOptionBuilder ? normalizedOption - : new StringSelectMenuOptionBuilder(jsonOptionValidator.parse(normalizedOption)), + : new StringSelectMenuOptionBuilder(parse(jsonOptionValidator, normalizedOption)), ), ); - optionsLengthValidator.parse(clone.length); + parse(optionsLengthValidator, clone.length); this.options.splice(0, this.options.length, ...clone); return this; } diff --git a/packages/builders/src/components/selectMenu/StringSelectMenuOption.ts b/packages/builders/src/components/selectMenu/StringSelectMenuOption.ts index 3e45970878e2..f66c978f7fdf 100644 --- a/packages/builders/src/components/selectMenu/StringSelectMenuOption.ts +++ b/packages/builders/src/components/selectMenu/StringSelectMenuOption.ts @@ -1,5 +1,6 @@ import type { JSONEncodable } from '@discordjs/util'; import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10'; +import { parse } from '../../util/validation.js'; import { defaultValidator, emojiValidator, @@ -41,7 +42,7 @@ export class StringSelectMenuOptionBuilder implements JSONEncodable) { const normalizedValues = normalizeArray(users); - optionsLengthValidator.parse((this.data.default_values?.length ?? 0) + normalizedValues.length); + parse(optionsLengthValidator, (this.data.default_values?.length ?? 0) + normalizedValues.length); this.data.default_values ??= []; this.data.default_values.push( @@ -65,7 +66,7 @@ export class UserSelectMenuBuilder extends BaseSelectMenuBuilder) { const normalizedValues = normalizeArray(users); - optionsLengthValidator.parse(normalizedValues.length); + parse(optionsLengthValidator, normalizedValues.length); this.data.default_values = normalizedValues.map((id) => ({ id, diff --git a/packages/builders/src/components/textInput/Assertions.ts b/packages/builders/src/components/textInput/Assertions.ts index 482670bba89c..1957bdc353e8 100644 --- a/packages/builders/src/components/textInput/Assertions.ts +++ b/packages/builders/src/components/textInput/Assertions.ts @@ -1,5 +1,6 @@ import { TextInputStyle } from 'discord-api-types/v10'; import { z } from 'zod'; +import { parse } from '../../util/validation.js'; import { customIdValidator } from '../Assertions.js'; export const textInputStyleValidator = z.union([ @@ -21,7 +22,7 @@ export const placeholderValidator = z.string().max(100); export const labelValidator = z.string().min(1).max(45); export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) { - customIdValidator.parse(customId); - textInputStyleValidator.parse(style); - labelValidator.parse(label); + parse(customIdValidator, customId); + parse(textInputStyleValidator, style); + parse(labelValidator, label); } diff --git a/packages/builders/src/components/textInput/TextInput.ts b/packages/builders/src/components/textInput/TextInput.ts index dcfa0560c01e..d1155d67de8a 100644 --- a/packages/builders/src/components/textInput/TextInput.ts +++ b/packages/builders/src/components/textInput/TextInput.ts @@ -1,6 +1,7 @@ import { isJSONEncodable, type Equatable, type JSONEncodable } from '@discordjs/util'; import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10'; import isEqual from 'fast-deep-equal'; +import { parse } from '../../util/validation.js'; import { customIdValidator } from '../Assertions.js'; import { ComponentBuilder } from '../Component.js'; import { @@ -54,7 +55,7 @@ export class TextInputBuilder * @param customId - The custom id to use */ public setCustomId(customId: string) { - this.data.custom_id = customIdValidator.parse(customId); + this.data.custom_id = parse(customIdValidator, customId); return this; } @@ -64,7 +65,7 @@ export class TextInputBuilder * @param label - The label to use */ public setLabel(label: string) { - this.data.label = labelValidator.parse(label); + this.data.label = parse(labelValidator, label); return this; } @@ -74,7 +75,7 @@ export class TextInputBuilder * @param style - The style to use */ public setStyle(style: TextInputStyle) { - this.data.style = textInputStyleValidator.parse(style); + this.data.style = parse(textInputStyleValidator, style); return this; } @@ -84,7 +85,7 @@ export class TextInputBuilder * @param minLength - The minimum length of text for this text input */ public setMinLength(minLength: number) { - this.data.min_length = minLengthValidator.parse(minLength); + this.data.min_length = parse(minLengthValidator, minLength); return this; } @@ -94,7 +95,7 @@ export class TextInputBuilder * @param maxLength - The maximum length of text for this text input */ public setMaxLength(maxLength: number) { - this.data.max_length = maxLengthValidator.parse(maxLength); + this.data.max_length = parse(maxLengthValidator, maxLength); return this; } @@ -104,7 +105,7 @@ export class TextInputBuilder * @param placeholder - The placeholder to use */ public setPlaceholder(placeholder: string) { - this.data.placeholder = placeholderValidator.parse(placeholder); + this.data.placeholder = parse(placeholderValidator, placeholder); return this; } @@ -114,7 +115,7 @@ export class TextInputBuilder * @param value - The value to use */ public setValue(value: string) { - this.data.value = valueValidator.parse(value); + this.data.value = parse(valueValidator, value); return this; } @@ -124,7 +125,7 @@ export class TextInputBuilder * @param required - Whether this text input is required */ public setRequired(required = true) { - this.data.required = requiredValidator.parse(required); + this.data.required = parse(requiredValidator, required); return this; } diff --git a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts index 3d0c619dc0e9..0bc4e2e95d26 100644 --- a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts +++ b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts @@ -1,5 +1,6 @@ import { ApplicationCommandType } from 'discord-api-types/v10'; import { z } from 'zod'; +import { parse } from '../../util/validation.js'; import type { ContextMenuCommandType } from './ContextMenuCommandBuilder.js'; const namePredicate = z @@ -12,15 +13,15 @@ const typePredicate = z.union([z.literal(ApplicationCommandType.User), z.literal const booleanPredicate = z.boolean(); export function validateDefaultPermission(value: unknown): asserts value is boolean { - booleanPredicate.parse(value); + parse(booleanPredicate, value); } export function validateName(name: unknown): asserts name is string { - namePredicate.parse(name); + parse(namePredicate, name); } export function validateType(type: unknown): asserts type is ContextMenuCommandType { - typePredicate.parse(type); + parse(typePredicate, type); } export function validateRequiredParameters(name: string, type: number) { @@ -34,7 +35,7 @@ export function validateRequiredParameters(name: string, type: number) { const dmPermissionPredicate = z.boolean().nullish(); export function validateDMPermission(value: unknown): asserts value is boolean | null | undefined { - dmPermissionPredicate.parse(value); + parse(dmPermissionPredicate, value); } const memberPermissionPredicate = z @@ -50,5 +51,5 @@ const memberPermissionPredicate = z .nullish(); export function validateDefaultMemberPermissions(permissions: unknown) { - return memberPermissionPredicate.parse(permissions); + return parse(memberPermissionPredicate, permissions); } diff --git a/packages/builders/src/interactions/modals/Assertions.ts b/packages/builders/src/interactions/modals/Assertions.ts index d534f3c843c8..c61b974048a4 100644 --- a/packages/builders/src/interactions/modals/Assertions.ts +++ b/packages/builders/src/interactions/modals/Assertions.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js'; import { customIdValidator } from '../../components/Assertions.js'; +import { parse } from '../../util/validation.js'; export const titleValidator = z.string().min(1).max(45); export const componentsValidator = z.instanceof(ActionRowBuilder).array().min(1); @@ -10,7 +11,7 @@ export function validateRequiredParameters( title?: string, components?: ActionRowBuilder[], ) { - customIdValidator.parse(customId); - titleValidator.parse(title); - componentsValidator.parse(components); + parse(customIdValidator, customId); + parse(titleValidator, title); + parse(componentsValidator, components); } diff --git a/packages/builders/src/interactions/modals/Modal.ts b/packages/builders/src/interactions/modals/Modal.ts index 948d774df203..0343f252600a 100644 --- a/packages/builders/src/interactions/modals/Modal.ts +++ b/packages/builders/src/interactions/modals/Modal.ts @@ -10,6 +10,7 @@ import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../com import { customIdValidator } from '../../components/Assertions.js'; import { createComponentBuilder } from '../../components/Components.js'; import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js'; +import { parse } from '../../util/validation.js'; import { titleValidator, validateRequiredParameters } from './Assertions.js'; /** @@ -43,7 +44,7 @@ export class ModalBuilder implements JSONEncodable(input: unknown, ExpectedInstanceOf: new () => ReturnType): asserts input is ReturnType { - z.instanceof(ExpectedInstanceOf).parse(input); + parse(z.instanceof(ExpectedInstanceOf), input); } export const localizationMapPredicate = z.record(z.nativeEnum(Locale), z.string().nullish()).nullish(); export function validateLocalizationMap(value: unknown): asserts value is LocalizationMap { - localizationMapPredicate.parse(value); + parse(localizationMapPredicate, value); } const dmPermissionPredicate = z.boolean().nullish(); export function validateDMPermission(value: unknown): asserts value is boolean | null | undefined { - dmPermissionPredicate.parse(value); + parse(dmPermissionPredicate, value); } const memberPermissionPredicate = z @@ -92,9 +93,9 @@ const memberPermissionPredicate = z .nullish(); export function validateDefaultMemberPermissions(permissions: unknown) { - return memberPermissionPredicate.parse(permissions); + return parse(memberPermissionPredicate, permissions); } export function validateNSFW(value: unknown): asserts value is boolean { - booleanPredicate.parse(value); + parse(booleanPredicate, value); } diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts index df61a58b18fc..4f7889a34734 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts @@ -1,6 +1,7 @@ import { ChannelType } from 'discord-api-types/v10'; -import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray'; import { z } from 'zod'; +import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray'; +import { parse } from '../../../util/validation.js'; /** * The allowed channel types used for a channel option in a slash command builder. @@ -53,7 +54,7 @@ export class ApplicationCommandOptionChannelTypesMixin { Reflect.set(this, 'channel_types', []); } - this.channel_types!.push(...channelTypesPredicate.parse(normalizeArray(channelTypes))); + this.channel_types!.push(...parse(channelTypesPredicate, normalizeArray(channelTypes))); return this; } diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts index e854d02a0f2f..8f0a18d7db1e 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts @@ -1,5 +1,6 @@ import type { ApplicationCommandOptionType } from 'discord-api-types/v10'; import { z } from 'zod'; +import { parse } from '../../../util/validation.js'; const booleanPredicate = z.boolean(); @@ -26,7 +27,7 @@ export class ApplicationCommandOptionWithAutocompleteMixin { */ public setAutocomplete(autocomplete: boolean): this { // Assert that you actually passed a boolean - booleanPredicate.parse(autocomplete); + parse(booleanPredicate, autocomplete); if (autocomplete && 'choices' in this && Array.isArray(this.choices) && this.choices.length > 0) { throw new RangeError('Autocomplete and choices are mutually exclusive to each other.'); diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts index 4871e4a6c237..bd0c3ff733b6 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts @@ -1,6 +1,7 @@ import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10'; -import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js'; import { z } from 'zod'; +import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js'; +import { parse } from '../../../util/validation.js'; import { localizationMapPredicate, validateChoicesLength } from '../Assertions.js'; const stringPredicate = z.string().min(1).max(100); @@ -40,7 +41,7 @@ export class ApplicationCommandOptionWithChoicesMixin(validator: Validator, value: unknown): output { + const result = validator.safeParse(value); + if (isValidationEnabled() && !result.success) { + throw fromZodError(result.error); + } + + return result.success ? result.data : value; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed25267373ee..b13008c31d95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -691,6 +691,9 @@ importers: zod: specifier: ^3.22.4 version: 3.22.4 + zod-validation-error: + specifier: ^3.0.0 + version: 3.0.0(zod@3.22.4) devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -31348,10 +31351,20 @@ snapshots: dependencies: nan: 2.18.0 - zod-validation-error@2.1.0(zod@3.22.4): + /zod-validation-error@3.0.0(zod@3.22.4): + resolution: {integrity: sha512-x+agsJJG9rvC7axF0xqTEdZhJkLHyIZkdOAWDJSmwGPzxNHMHwtU6w2yDOAAP6yuSfTAUhAMJRBfhVGY64ySEQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + peerDependenciesMeta: + zod: + optional: true dependencies: zod: 3.22.4 + dev: false - zod@3.22.4: {} + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false zwitch@2.0.4: {} From b58e44a1ea154972dd771571475e095d04d0e791 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:05:30 +0100 Subject: [PATCH 3/9] fix: wrong lockfile merge --- pnpm-lock.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b13008c31d95..e7d02d2b83c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30545,6 +30545,7 @@ snapshots: optionalDependencies: vite: 5.2.9(@types/node@20.12.7)(terser@5.29.1) transitivePeerDependencies: + - postcss - rollup - supports-color From 6b43f5ba11ebfdc1e49d5157955ee213f3e03a6d Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Fri, 23 Feb 2024 19:55:37 +0100 Subject: [PATCH 4/9] refactor: enum resolution for other builders --- .../builders/src/components/Assertions.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/builders/src/components/Assertions.ts b/packages/builders/src/components/Assertions.ts index 7ae832673447..7da0c65dc3bc 100644 --- a/packages/builders/src/components/Assertions.ts +++ b/packages/builders/src/components/Assertions.ts @@ -18,7 +18,17 @@ export const disabledValidator = z.boolean(); export const buttonLabelValidator = z.string().min(1).max(80); -export const buttonStyleValidator = z.nativeEnum(ButtonStyle); +export const buttonStyleValidator = z.union([ + z.nativeEnum(ButtonStyle), + z + .enum( + Object.values(ButtonStyle).filter((value) => typeof value === 'string') as [ + keyof typeof ButtonStyle, + ...(keyof typeof ButtonStyle)[], + ], + ) + .transform((key) => ButtonStyle[key]), +]); export const placeholderValidator = z.string().max(150); export const minMaxValidator = z.number().int().gte(0).lte(25); @@ -50,7 +60,19 @@ export function validateRequiredSelectMenuOptionParameters(label?: string, value parse(labelValueDescriptionValidator, value); } -export const channelTypesValidator = z.nativeEnum(ChannelType).array(); +export const channelTypesValidator = z + .union([ + z.nativeEnum(ChannelType), + z + .enum( + Object.values(ChannelType).filter((value) => typeof value === 'string') as [ + keyof typeof ChannelType, + ...(keyof typeof ChannelType)[], + ], + ) + .transform((key) => ChannelType[key]), + ]) + .array(); export const urlValidator = z .string() From f998a5f80c038bea5e765521c7bcc7dad7820e5d Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sun, 26 May 2024 14:28:27 +0200 Subject: [PATCH 5/9] fix: merge mistake --- pnpm-lock.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7d02d2b83c8..c5debdb9468b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31367,5 +31367,7 @@ snapshots: /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false + + zod@3.22.4: {} zwitch@2.0.4: {} From 9a91657800af436e879b3e01c1358fbf22d52aaa Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sun, 26 May 2024 14:32:03 +0200 Subject: [PATCH 6/9] fix: again --- pnpm-lock.yaml | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5debdb9468b..e2a5e126b599 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -693,7 +693,7 @@ importers: version: 3.22.4 zod-validation-error: specifier: ^3.0.0 - version: 3.0.0(zod@3.22.4) + version: 3.3.0(zod@3.22.4) devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -5089,10 +5089,6 @@ packages: resolution: {integrity: sha512-QCjj7X/QlY0QUCeAaZQmnrsMH/b2BMQYee3F1Y5iF17JagUQqO3KZlG7vfXWQU3SRAJX5OgZZynBjixUH+nNGg==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - '@sapphire/shapeshift@3.9.7': - resolution: {integrity: sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==} - engines: {node: '>=v16'} - '@sapphire/snowflake@3.5.3': resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} @@ -13799,6 +13795,12 @@ packages: peerDependencies: zod: ^3.18.0 + zod-validation-error@3.3.0: + resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} @@ -31352,22 +31354,14 @@ snapshots: dependencies: nan: 2.18.0 - /zod-validation-error@3.0.0(zod@3.22.4): - resolution: {integrity: sha512-x+agsJJG9rvC7axF0xqTEdZhJkLHyIZkdOAWDJSmwGPzxNHMHwtU6w2yDOAAP6yuSfTAUhAMJRBfhVGY64ySEQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.18.0 - peerDependenciesMeta: - zod: - optional: true + zod-validation-error@2.1.0(zod@3.22.4): + dependencies: + zod: 3.22.4 + + zod-validation-error@3.3.0(zod@3.22.4): dependencies: zod: 3.22.4 - dev: false - /zod@3.22.4: - resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - dev: false - zod@3.22.4: {} zwitch@2.0.4: {} From 628c3d58d6b8c23d496c37d08dd50cde130e25ac Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sat, 6 Jul 2024 14:28:55 +0200 Subject: [PATCH 7/9] fix: merge errors --- .../src/interactions/contextMenuCommands/Assertions.ts | 2 +- .../contextMenuCommands/ContextMenuCommandBuilder.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts index 3803376a5859..b8ba493dea23 100644 --- a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts +++ b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts @@ -56,4 +56,4 @@ export function validateDefaultMemberPermissions(permissions: unknown) { export const contextsPredicate = z.nativeEnum(InteractionContextType).array(); -export const integrationTypesPredicate = s.nativeEnum(ApplicationIntegrationType).array(isValidationEnabled); +export const integrationTypesPredicate = z.nativeEnum(ApplicationIntegrationType).array(); diff --git a/packages/builders/src/interactions/contextMenuCommands/ContextMenuCommandBuilder.ts b/packages/builders/src/interactions/contextMenuCommands/ContextMenuCommandBuilder.ts index 1c11391c8797..363e6984d612 100644 --- a/packages/builders/src/interactions/contextMenuCommands/ContextMenuCommandBuilder.ts +++ b/packages/builders/src/interactions/contextMenuCommands/ContextMenuCommandBuilder.ts @@ -9,6 +9,7 @@ import type { } from 'discord-api-types/v10'; import type { RestOrArray } from '../../util/normalizeArray.js'; import { normalizeArray } from '../../util/normalizeArray.js'; +import { parse } from '../../util/validation.js'; import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions.js'; import { validateRequiredParameters, @@ -81,7 +82,7 @@ export class ContextMenuCommandBuilder { * @param contexts - The contexts */ public setContexts(...contexts: RestOrArray) { - Reflect.set(this, 'contexts', contextsPredicate.parse(normalizeArray(contexts))); + Reflect.set(this, 'contexts', parse(contextsPredicate, normalizeArray(contexts))); return this; } @@ -92,7 +93,7 @@ export class ContextMenuCommandBuilder { * @param integrationTypes - The integration types */ public setIntegrationTypes(...integrationTypes: RestOrArray) { - Reflect.set(this, 'integration_types', integrationTypesPredicate.parse(normalizeArray(integrationTypes))); + Reflect.set(this, 'integration_types', parse(integrationTypesPredicate, normalizeArray(integrationTypes))); return this; } From 046f8b12ec9b446408b893d4f47219b78debe2e4 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:15:58 +0200 Subject: [PATCH 8/9] fix: missing conversion after merge --- .../builders/src/interactions/modals/Assertions.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/builders/src/interactions/modals/Assertions.ts b/packages/builders/src/interactions/modals/Assertions.ts index 70754e8de44c..c61b974048a4 100644 --- a/packages/builders/src/interactions/modals/Assertions.ts +++ b/packages/builders/src/interactions/modals/Assertions.ts @@ -3,14 +3,8 @@ import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../com import { customIdValidator } from '../../components/Assertions.js'; import { parse } from '../../util/validation.js'; -export const titleValidator = s.string - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(45) - .setValidationEnabled(isValidationEnabled); -export const componentsValidator = s - .instance(ActionRowBuilder) - .array.lengthGreaterThanOrEqual(1) - .setValidationEnabled(isValidationEnabled); +export const titleValidator = z.string().min(1).max(45); +export const componentsValidator = z.instanceof(ActionRowBuilder).array().min(1); export function validateRequiredParameters( customId?: string, From 914cf543a9cbea6a1bc0ad58247428bed1e15475 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Fri, 23 Aug 2024 21:35:56 +0200 Subject: [PATCH 9/9] fix: merge conflicts --- pnpm-lock.yaml | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f36c3f6fc9ec..d4322b343264 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -685,6 +685,12 @@ importers: tslib: specifier: ^2.6.3 version: 2.6.3 + zod: + specifier: ^3.22.4 + version: 3.23.8 + zod-validation-error: + specifier: ^3.0.0 + version: 3.3.1(zod@3.23.8) devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -2583,12 +2589,12 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@definitelytyped/header-parser@0.2.11': - resolution: {integrity: sha512-OUE+bz0puVYJaJUz39CeOrV7NYpfyRLZyEhRGJhLdTH+nzAnItiXNHRqsbw2jWKXuOSloez3VNcylyKqd10d6w==} + '@definitelytyped/header-parser@0.2.12': + resolution: {integrity: sha512-UYtSXiLMhzRFKh7xHMkgiWsscgHxIndmjetaptZMMS0EOvfhUTuEM68GpjiCtz5shXw22Vacs1vDTAkKGDhNmg==} engines: {node: '>=18.18.0'} - '@definitelytyped/typescript-versions@0.1.3': - resolution: {integrity: sha512-/OSa91o/iZWVnsIZD6C0yryQSMv4UKtoEyxOz2baqE4j4lOV/+ZLR+9A5Gew96lFaUkmWzA1x+rhx013mzaI7w==} + '@definitelytyped/typescript-versions@0.1.4': + resolution: {integrity: sha512-4Rz5kCpyxofwXCtBQaNfmWYXZcH0sMJxpbIgJzS+PAxgFCAa9W+2Jil7rrkpzsjx9E7+zOPukbXBXjyXohcyuQ==} engines: {node: '>=18.18.0'} '@definitelytyped/utils@0.1.7': @@ -13517,6 +13523,12 @@ packages: peerDependencies: zod: ^3.18.0 + zod-validation-error@3.3.1: + resolution: {integrity: sha512-uFzCZz7FQis256dqw4AhPQgD6f3pzNca/Zh62RNELavlumQB3nDIUFbF5JQfFLcMbO1s02Q7Xg/gpcOBlEnYZA==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} @@ -14665,13 +14677,13 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@definitelytyped/header-parser@0.2.11': + '@definitelytyped/header-parser@0.2.12': dependencies: - '@definitelytyped/typescript-versions': 0.1.3 + '@definitelytyped/typescript-versions': 0.1.4 '@definitelytyped/utils': 0.1.7 semver: 7.5.4 - '@definitelytyped/typescript-versions@0.1.3': {} + '@definitelytyped/typescript-versions@0.1.4': {} '@definitelytyped/utils@0.1.7': dependencies: @@ -21301,7 +21313,7 @@ snapshots: dts-critic@3.3.11(typescript@5.5.4): dependencies: - '@definitelytyped/header-parser': 0.2.11 + '@definitelytyped/header-parser': 0.2.12 command-exists: 1.2.9 rimraf: 3.0.2 semver: 6.3.1 @@ -21311,8 +21323,8 @@ snapshots: dtslint@4.2.1(typescript@5.5.4): dependencies: - '@definitelytyped/header-parser': 0.2.11 - '@definitelytyped/typescript-versions': 0.1.3 + '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/typescript-versions': 0.1.4 '@definitelytyped/utils': 0.1.7 dts-critic: 3.3.11(typescript@5.5.4) fs-extra: 6.0.1 @@ -29463,6 +29475,10 @@ snapshots: dependencies: zod: 3.23.8 + zod-validation-error@3.3.1(zod@3.23.8): + dependencies: + zod: 3.23.8 + zod@3.23.8: {} zwitch@2.0.4: {}