diff --git a/packages/builders/package.json b/packages/builders/package.json index 220a500429c5..e6c62030778b 100644 --- a/packages/builders/package.json +++ b/packages/builders/package.json @@ -67,11 +67,12 @@ "dependencies": { "@discordjs/formatters": "workspace:^", "@discordjs/util": "workspace:^", - "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "0.37.97", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", - "tslib": "^2.6.3" + "tslib": "^2.6.3", + "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 926159eedc08..0500a53f9fd4 100644 --- a/packages/builders/src/components/Assertions.ts +++ b/packages/builders/src/components/Assertions.ts @@ -1,91 +1,83 @@ -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 { parse } from '../util/validation.js'; 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); + .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.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 = 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); + parse(customIdValidator, customId); + parse(optionsValidator, options); } -export const defaultValidator = s.boolean(); +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 = s.nativeEnum(ChannelType).array().setValidationEnabled(isValidationEnabled); - -export const urlValidator = s +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() - .url({ - allowedProtocols: ['http:', 'https:', 'discord:'], - }) - .setValidationEnabled(isValidationEnabled); + .url() + .regex(/^(?https?|discord):\/\//); export function validateRequiredButtonParameters( style?: ButtonStyle, diff --git a/packages/builders/src/components/button/Button.ts b/packages/builders/src/components/button/Button.ts index cc36d80dabcb..ac523e223992 100644 --- a/packages/builders/src/components/button/Button.ts +++ b/packages/builders/src/components/button/Button.ts @@ -8,6 +8,7 @@ import { type ButtonStyle, type Snowflake, } from 'discord-api-types/v10'; +import { parse } from '../../util/validation.js'; import { buttonLabelValidator, buttonStyleValidator, @@ -61,7 +62,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; } @@ -74,7 +75,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; } @@ -86,7 +87,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; } @@ -107,7 +108,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param emoji - The emoji to use */ public setEmoji(emoji: APIMessageComponentEmoji) { - (this.data as Exclude).emoji = emojiValidator.parse(emoji); + (this.data as Exclude).emoji = parse(emojiValidator, emoji); return this; } @@ -117,7 +118,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; } @@ -127,7 +128,7 @@ export class ButtonBuilder extends ComponentBuilder { * @param label - The label to use */ public setLabel(label: string) { - (this.data as Exclude).label = buttonLabelValidator.parse(label); + (this.data as Exclude).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 204dcf84a178..5e2b355a5cf5 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 640be8f81539..5f9c8d5efea1 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 084a01c6aa8e..1957bdc353e8 100644 --- a/packages/builders/src/components/textInput/Assertions.ts +++ b/packages/builders/src/components/textInput/Assertions.ts @@ -1,32 +1,28 @@ -import { s } from '@sapphire/shapeshift'; import { TextInputStyle } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../../util/validation.js'; +import { z } from 'zod'; +import { parse } from '../../util/validation.js'; 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); - 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 c8bb1f838746..ed5654c7db4a 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 72d6c50f05cf..b8ba493dea23 100644 --- a/packages/builders/src/interactions/contextMenuCommands/Assertions.ts +++ b/packages/builders/src/interactions/contextMenuCommands/Assertions.ts @@ -1,30 +1,27 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandType, ApplicationIntegrationType, InteractionContextType } from 'discord-api-types/v10'; -import { isValidationEnabled } from '../../util/validation.js'; +import { z } from 'zod'; +import { parse } from '../../util/validation.js'; import type { ContextMenuCommandType } from './ContextMenuCommandBuilder.js'; -const namePredicate = s +const namePredicate = z .string() - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(32) + .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); + 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) { @@ -35,31 +32,28 @@ 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); + parse(dmPermissionPredicate, value); } -const memberPermissionPredicate = s +const memberPermissionPredicate = z .union([ - s.bigint().transform((value) => value.toString()), - s + z.bigint().transform((value) => value.toString()), + z .number() - .safeInt() + .int() + .safe() .transform((value) => value.toString()), - s.string().regex(/^\d+$/), + z.string().regex(/^\d+$/), ]) .nullish(); export function validateDefaultMemberPermissions(permissions: unknown) { - return memberPermissionPredicate.parse(permissions); + return parse(memberPermissionPredicate, permissions); } -export const contextsPredicate = s.array( - s.nativeEnum(InteractionContextType).setValidationEnabled(isValidationEnabled), -); +export const contextsPredicate = z.nativeEnum(InteractionContextType).array(); -export const integrationTypesPredicate = s.array( - s.nativeEnum(ApplicationIntegrationType).setValidationEnabled(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; } diff --git a/packages/builders/src/interactions/modals/Assertions.ts b/packages/builders/src/interactions/modals/Assertions.ts index 79597ff47076..c61b974048a4 100644 --- a/packages/builders/src/interactions/modals/Assertions.ts +++ b/packages/builders/src/interactions/modals/Assertions.ts @@ -1,25 +1,17 @@ -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'; +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, 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 { - s.instance(ExpectedInstanceOf).parse(input); + parse(z.instanceof(ExpectedInstanceOf), 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); + parse(localizationMapPredicate, 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); + parse(dmPermissionPredicate, value); } -const memberPermissionPredicate = s +const memberPermissionPredicate = z .union([ - s.bigint().transform((value) => value.toString()), - s + z.bigint().transform((value) => value.toString()), + z .number() - .safeInt() + .int() + .safe() .transform((value) => value.toString()), - s.string().regex(/^\d+$/), + z.string().regex(/^\d+$/), ]) .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); } -export const contextsPredicate = s.array( - s.nativeEnum(InteractionContextType).setValidationEnabled(isValidationEnabled), -); +export const contextsPredicate = z.nativeEnum(InteractionContextType).array(); -export const integrationTypesPredicate = s.array( - s.nativeEnum(ApplicationIntegrationType).setValidationEnabled(isValidationEnabled), -); +export const integrationTypesPredicate = z.nativeEnum(ApplicationIntegrationType).array(); diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts index 98f3242bcd49..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 { s } from '@sapphire/shapeshift'; import { ChannelType } from 'discord-api-types/v10'; +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. @@ -26,7 +27,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. @@ -47,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 6f2ceee10966..8f0a18d7db1e 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithAutocompleteMixin.ts @@ -1,7 +1,8 @@ -import { s } from '@sapphire/shapeshift'; import type { ApplicationCommandOptionType } from 'discord-api-types/v10'; +import { z } from 'zod'; +import { parse } from '../../../util/validation.js'; -const booleanPredicate = s.boolean(); +const booleanPredicate = z.boolean(); /** * This mixin holds choices and autocomplete symbols used for options. @@ -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 68359b4b2130..bd0c3ff733b6 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts @@ -1,15 +1,16 @@ -import { s } from '@sapphire/shapeshift'; import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10'; +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 = s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100); -const numberPredicate = s.number().greaterThan(Number.NEGATIVE_INFINITY).lessThan(Number.POSITIVE_INFINITY); -const choicesPredicate = s +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: s.union([stringPredicate, numberPredicate]), + value: z.union([stringPredicate, numberPredicate]), }) .array(); @@ -40,7 +41,7 @@ export class ApplicationCommandOptionWithChoicesMixinhttps?|attachment):\/\//) + .nullish(); -export const urlPredicate = s +export const urlPredicate = z .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 + .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() - .greaterThanOrEqual(0) - .lessThanOrEqual(0xffffff) - .or(s.tuple([RGBPredicate, RGBPredicate, RGBPredicate])) - .nullable() - .setValidationEnabled(isValidationEnabled); + .min(0) + .max(0xffffff) + .or(z.tuple([RGBPredicate, RGBPredicate, RGBPredicate])) + .nullable(); -export const descriptionPredicate = s - .string() - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(4_096) - .nullable() - .setValidationEnabled(isValidationEnabled); +export const descriptionPredicate = z.string().min(1).max(4_096).nullable(); -export const footerTextPredicate = s - .string() - .lengthGreaterThanOrEqual(1) - .lengthLessThanOrEqual(2_048) - .nullable() - .setValidationEnabled(isValidationEnabled); +export const footerTextPredicate = z.string().min(1).max(2_048).nullable(); -export const embedFooterPredicate = s - .object({ - text: footerTextPredicate, - iconURL: imageURLPredicate, - }) - .setValidationEnabled(isValidationEnabled); +export const embedFooterPredicate = z.object({ + text: footerTextPredicate, + iconURL: imageURLPredicate, +}); -export const timestampPredicate = s.union([s.number(), s.date()]).nullable().setValidationEnabled(isValidationEnabled); +export const timestampPredicate = z.union([z.number(), z.date()]).nullable(); -export const titlePredicate = fieldNamePredicate.nullable().setValidationEnabled(isValidationEnabled); +export const titlePredicate = fieldNamePredicate.nullable(); diff --git a/packages/builders/src/messages/embed/Embed.ts b/packages/builders/src/messages/embed/Embed.ts index 683e0598c188..d7ed9ec64fc0 100644 --- a/packages/builders/src/messages/embed/Embed.ts +++ b/packages/builders/src/messages/embed/Embed.ts @@ -1,5 +1,6 @@ import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10'; import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js'; +import { parse } from '../../util/validation.js'; import { colorPredicate, descriptionPredicate, @@ -113,7 +114,7 @@ export class EmbedBuilder { validateFieldLength(normalizedFields.length, this.data.fields); // Data assertions - embedFieldsArrayPredicate.parse(normalizedFields); + parse(embedFieldsArrayPredicate, normalizedFields); if (this.data.fields) this.data.fields.push(...normalizedFields); else this.data.fields = normalizedFields; @@ -154,7 +155,8 @@ export class EmbedBuilder { validateFieldLength(fields.length - deleteCount, this.data.fields); // Data assertions - embedFieldsArrayPredicate.parse(fields); + parse(embedFieldsArrayPredicate, fields); + if (this.data.fields) this.data.fields.splice(index, deleteCount, ...fields); else this.data.fields = fields; return this; @@ -188,7 +190,7 @@ export class EmbedBuilder { } // Data assertions - embedAuthorPredicate.parse(options); + parse(embedAuthorPredicate, options); this.data.author = { name: options.name, url: options.url, icon_url: options.iconURL }; return this; @@ -201,7 +203,7 @@ export class EmbedBuilder { */ public setColor(color: RGBTuple | number | null): this { // Data assertions - colorPredicate.parse(color); + parse(colorPredicate, color); if (Array.isArray(color)) { const [red, green, blue] = color; @@ -220,7 +222,7 @@ export class EmbedBuilder { */ public setDescription(description: string | null): this { // Data assertions - descriptionPredicate.parse(description); + parse(descriptionPredicate, description); this.data.description = description ?? undefined; return this; @@ -238,7 +240,7 @@ export class EmbedBuilder { } // Data assertions - embedFooterPredicate.parse(options); + parse(embedFooterPredicate, options); this.data.footer = { text: options.text, icon_url: options.iconURL }; return this; @@ -251,7 +253,7 @@ export class EmbedBuilder { */ public setImage(url: string | null): this { // Data assertions - imageURLPredicate.parse(url); + parse(imageURLPredicate, url); this.data.image = url ? { url } : undefined; return this; @@ -264,7 +266,7 @@ export class EmbedBuilder { */ public setThumbnail(url: string | null): this { // Data assertions - imageURLPredicate.parse(url); + parse(imageURLPredicate, url); this.data.thumbnail = url ? { url } : undefined; return this; @@ -277,7 +279,7 @@ export class EmbedBuilder { */ public setTimestamp(timestamp: Date | number | null = Date.now()): this { // Data assertions - timestampPredicate.parse(timestamp); + parse(timestampPredicate, timestamp); this.data.timestamp = timestamp ? new Date(timestamp).toISOString() : undefined; return this; @@ -290,7 +292,7 @@ export class EmbedBuilder { */ public setTitle(title: string | null): this { // Data assertions - titlePredicate.parse(title); + parse(titlePredicate, title); this.data.title = title ?? undefined; return this; @@ -303,7 +305,7 @@ export class EmbedBuilder { */ public setURL(url: string | null): this { // Data assertions - urlPredicate.parse(url); + parse(urlPredicate, url); this.data.url = url ?? undefined; return this; diff --git a/packages/builders/src/util/validation.ts b/packages/builders/src/util/validation.ts index 37e5c224bc6e..8d5924521bde 100644 --- a/packages/builders/src/util/validation.ts +++ b/packages/builders/src/util/validation.ts @@ -1,3 +1,6 @@ +import type { ZodTypeAny, output } from 'zod'; +import { fromZodError } from 'zod-validation-error'; + let validate = true; /** @@ -24,3 +27,20 @@ export function disableValidators() { export function isValidationEnabled() { return validate; } + +/** + * Parses a value with a given validator + * + * @param validator - Tthe zod validator to use + * @param value - The value to parse + * @returns The result from parsing + * @internal + */ +export function parse(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 52983c69cdf8..d4322b343264 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -673,9 +673,6 @@ importers: '@discordjs/util': specifier: workspace:^ version: link:../util - '@sapphire/shapeshift': - specifier: ^4.0.0 - version: 4.0.0 discord-api-types: specifier: 0.37.97 version: 0.37.97 @@ -688,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:^ @@ -2586,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': @@ -5047,10 +5050,6 @@ packages: resolution: {integrity: sha512-QCjj7X/QlY0QUCeAaZQmnrsMH/b2BMQYee3F1Y5iF17JagUQqO3KZlG7vfXWQU3SRAJX5OgZZynBjixUH+nNGg==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - '@sapphire/shapeshift@4.0.0': - resolution: {integrity: sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==} - engines: {node: '>=v16'} - '@sapphire/snowflake@3.5.3': resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} @@ -13524,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==} @@ -14672,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: @@ -17921,11 +17926,6 @@ snapshots: '@sapphire/result@2.6.6': {} - '@sapphire/shapeshift@4.0.0': - dependencies: - fast-deep-equal: 3.1.3 - lodash: 4.17.21 - '@sapphire/snowflake@3.5.3': {} '@sapphire/utilities@3.15.3': {} @@ -21313,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 @@ -21323,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 @@ -29475,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: {}