Skip to content

Commit

Permalink
feat: implement zod-validation-error (#10534)
Browse files Browse the repository at this point in the history
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
didinele and kodiakhq[bot] authored Oct 6, 2024
1 parent 24128a3 commit 8ab4124
Show file tree
Hide file tree
Showing 22 changed files with 88 additions and 97 deletions.
3 changes: 2 additions & 1 deletion packages/builders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
"discord-api-types": "^0.37.101",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3",
"zod": "^3.23.8"
"zod": "^3.23.8",
"zod-validation-error": "^3.4.0"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
Expand Down
6 changes: 2 additions & 4 deletions packages/builders/src/components/ActionRow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
import { ComponentType } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
import { resolveBuilder } from '../util/resolveBuilder.js';
import { isValidationEnabled } from '../util/validation.js';
import { validate } from '../util/validation.js';
import { actionRowPredicate } from './Assertions.js';
import { ComponentBuilder } from './Component.js';
import type { AnyActionRowComponentBuilder } from './Components.js';
Expand Down Expand Up @@ -336,9 +336,7 @@ export class ActionRowBuilder extends ComponentBuilder<APIActionRowComponent<API
components: components.map((component) => component.toJSON(validationOverride)),
};

if (validationOverride ?? isValidationEnabled()) {
actionRowPredicate.parse(data);
}
validate(actionRowPredicate, data, validationOverride);

return data as APIActionRowComponent<APIActionRowComponentTypes>;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/components/button/Button.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { APIButtonComponent } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { buttonPredicate } from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';

Expand All @@ -24,10 +24,7 @@ export abstract class BaseButtonBuilder<ButtonData extends APIButtonComponent> e
*/
public override toJSON(validationOverride?: boolean): ButtonData {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
buttonPredicate.parse(clone);
}
validate(buttonPredicate, clone, validationOverride);

return clone as ButtonData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
SelectMenuDefaultValueType,
} from 'discord-api-types/v10';
import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { selectMenuChannelPredicate } from '../Assertions.js';
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';

Expand Down Expand Up @@ -108,10 +108,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder<APIChannelSe
*/
public override toJSON(validationOverride?: boolean): APIChannelSelectComponent {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
selectMenuChannelPredicate.parse(clone);
}
validate(selectMenuChannelPredicate, clone, validationOverride);

return clone as APIChannelSelectComponent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
SelectMenuDefaultValueType,
} from 'discord-api-types/v10';
import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { selectMenuMentionablePredicate } from '../Assertions.js';
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';

Expand Down Expand Up @@ -119,10 +119,7 @@ export class MentionableSelectMenuBuilder extends BaseSelectMenuBuilder<APIMenti
*/
public override toJSON(validationOverride?: boolean): APIMentionableSelectComponent {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
selectMenuMentionablePredicate.parse(clone);
}
validate(selectMenuMentionablePredicate, clone, validationOverride);

return clone as APIMentionableSelectComponent;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/components/selectMenu/RoleSelectMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
SelectMenuDefaultValueType,
} from 'discord-api-types/v10';
import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { selectMenuRolePredicate } from '../Assertions.js';
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';

Expand Down Expand Up @@ -82,10 +82,7 @@ export class RoleSelectMenuBuilder extends BaseSelectMenuBuilder<APIRoleSelectCo
*/
public override toJSON(validationOverride?: boolean): APIRoleSelectComponent {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
selectMenuRolePredicate.parse(clone);
}
validate(selectMenuRolePredicate, clone, validationOverride);

return clone as APIRoleSelectComponent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,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 { resolveBuilder } from '../../util/resolveBuilder.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { selectMenuStringPredicate } from '../Assertions.js';
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';
import { StringSelectMenuOptionBuilder } from './StringSelectMenuOption.js';
Expand Down Expand Up @@ -156,9 +156,7 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
options: options.map((option) => option.toJSON(false)),
};

if (validationOverride ?? isValidationEnabled()) {
selectMenuStringPredicate.parse(data);
}
validate(selectMenuStringPredicate, data, validationOverride);

return data as APIStringSelectComponent;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { JSONEncodable } from '@discordjs/util';
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { selectMenuStringOptionPredicate } from '../Assertions.js';

/**
Expand Down Expand Up @@ -106,10 +106,7 @@ export class StringSelectMenuOptionBuilder implements JSONEncodable<APISelectMen
*/
public toJSON(validationOverride?: boolean): APISelectMenuOption {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
selectMenuStringOptionPredicate.parse(clone);
}
validate(selectMenuStringOptionPredicate, clone, validationOverride);

return clone as APISelectMenuOption;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/components/selectMenu/UserSelectMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
SelectMenuDefaultValueType,
} from 'discord-api-types/v10';
import { type RestOrArray, normalizeArray } from '../../util/normalizeArray.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { selectMenuUserPredicate } from '../Assertions.js';
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';

Expand Down Expand Up @@ -82,10 +82,7 @@ export class UserSelectMenuBuilder extends BaseSelectMenuBuilder<APIUserSelectCo
*/
public override toJSON(validationOverride?: boolean): APIUserSelectComponent {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
selectMenuUserPredicate.parse(clone);
}
validate(selectMenuUserPredicate, clone, validationOverride);

return clone as APIUserSelectComponent;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/components/textInput/TextInput.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { ComponentBuilder } from '../Component.js';
import { textInputPredicate } from './Assertions.js';

Expand Down Expand Up @@ -154,10 +154,7 @@ export class TextInputBuilder extends ComponentBuilder<APITextInputComponent> {
*/
public toJSON(validationOverride?: boolean): APITextInputComponent {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
textInputPredicate.parse(clone);
}
validate(textInputPredicate, clone, validationOverride);

return clone as APITextInputComponent;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApplicationCommandType, type RESTPostAPIChatInputApplicationCommandsJSONBody } from 'discord-api-types/v10';
import { Mixin } from 'ts-mixer';
import { isValidationEnabled } from '../../../util/validation.js';
import { validate } from '../../../util/validation.js';
import { CommandBuilder } from '../Command.js';
import { SharedNameAndDescription } from '../SharedNameAndDescription.js';
import { chatInputCommandPredicate } from './Assertions.js';
Expand Down Expand Up @@ -28,9 +28,7 @@ export class ChatInputCommandBuilder extends Mixin(
options: options?.map((option) => option.toJSON(validationOverride)),
};

if (validationOverride ?? isValidationEnabled()) {
chatInputCommandPredicate.parse(data);
}
validate(chatInputCommandPredicate, data, validationOverride);

return data;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ApplicationCommandOptionType } from 'discord-api-types/v10';
import { Mixin } from 'ts-mixer';
import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js';
import { resolveBuilder } from '../../../util/resolveBuilder.js';
import { isValidationEnabled } from '../../../util/validation.js';
import { validate } from '../../../util/validation.js';
import type { SharedNameAndDescriptionData } from '../SharedNameAndDescription.js';
import { SharedNameAndDescription } from '../SharedNameAndDescription.js';
import { chatInputCommandSubcommandGroupPredicate, chatInputCommandSubcommandPredicate } from './Assertions.js';
Expand Down Expand Up @@ -69,9 +69,7 @@ export class ChatInputCommandSubcommandGroupBuilder
options: options?.map((option) => option.toJSON(validationOverride)) ?? [],
};

if (validationOverride ?? isValidationEnabled()) {
chatInputCommandSubcommandGroupPredicate.parse(data);
}
validate(chatInputCommandSubcommandGroupPredicate, data, validationOverride);

return data;
}
Expand Down Expand Up @@ -102,9 +100,7 @@ export class ChatInputCommandSubcommandBuilder
options: options?.map((option) => option.toJSON(validationOverride)) ?? [],
};

if (validationOverride ?? isValidationEnabled()) {
chatInputCommandSubcommandPredicate.parse(data);
}
validate(chatInputCommandSubcommandPredicate, data, validationOverride);

return data;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
ApplicationCommandOptionType,
} from 'discord-api-types/v10';
import type { z } from 'zod';
import { isValidationEnabled } from '../../../../util/validation.js';
import { validate } from '../../../../util/validation.js';
import type { SharedNameAndDescriptionData } from '../../SharedNameAndDescription.js';
import { SharedNameAndDescription } from '../../SharedNameAndDescription.js';
import { basicOptionPredicate } from '../Assertions.js';
Expand Down Expand Up @@ -49,10 +49,7 @@ export abstract class ApplicationCommandOptionBase
*/
public toJSON(validationOverride?: boolean): APIApplicationCommandBasicOption {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
(this.constructor as typeof ApplicationCommandOptionBase).predicate.parse(clone);
}
validate((this.constructor as typeof ApplicationCommandOptionBase).predicate, clone, validationOverride);

return clone as APIApplicationCommandBasicOption;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApplicationCommandType, type RESTPostAPIContextMenuApplicationCommandsJSONBody } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../../util/validation.js';
import { validate } from '../../../util/validation.js';
import { messageCommandPredicate } from './Assertions.js';
import { ContextMenuCommandBuilder } from './ContextMenuCommand.js';

Expand All @@ -9,10 +9,7 @@ export class MessageContextCommandBuilder extends ContextMenuCommandBuilder {
*/
public override toJSON(validationOverride?: boolean): RESTPostAPIContextMenuApplicationCommandsJSONBody {
const data = { ...structuredClone(this.data), type: ApplicationCommandType.Message };

if (validationOverride ?? isValidationEnabled()) {
messageCommandPredicate.parse(data);
}
validate(messageCommandPredicate, data, validationOverride);

return data as RESTPostAPIContextMenuApplicationCommandsJSONBody;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApplicationCommandType, type RESTPostAPIContextMenuApplicationCommandsJSONBody } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../../util/validation.js';
import { validate } from '../../../util/validation.js';
import { userCommandPredicate } from './Assertions.js';
import { ContextMenuCommandBuilder } from './ContextMenuCommand.js';

Expand All @@ -9,10 +9,7 @@ export class UserContextCommandBuilder extends ContextMenuCommandBuilder {
*/
public override toJSON(validationOverride?: boolean): RESTPostAPIContextMenuApplicationCommandsJSONBody {
const data = { ...structuredClone(this.data), type: ApplicationCommandType.User };

if (validationOverride ?? isValidationEnabled()) {
userCommandPredicate.parse(data);
}
validate(userCommandPredicate, data, validationOverride);

return data as RESTPostAPIContextMenuApplicationCommandsJSONBody;
}
Expand Down
6 changes: 2 additions & 4 deletions packages/builders/src/interactions/modals/Modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ActionRowBuilder } from '../../components/ActionRow.js';
import { createComponentBuilder } from '../../components/Components.js';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { resolveBuilder } from '../../util/resolveBuilder.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { modalPredicate } from './Assertions.js';

export interface ModalBuilderData extends Partial<Omit<APIModalInteractionResponseCallbackData, 'components'>> {
Expand Down Expand Up @@ -162,9 +162,7 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
components: components.map((component) => component.toJSON(validationOverride)),
};

if (validationOverride ?? isValidationEnabled()) {
modalPredicate.parse(data);
}
validate(modalPredicate, data, validationOverride);

return data as APIModalInteractionResponseCallbackData;
}
Expand Down
6 changes: 2 additions & 4 deletions packages/builders/src/messages/embed/Embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter } from 'di
import type { RestOrArray } from '../../util/normalizeArray.js';
import { normalizeArray } from '../../util/normalizeArray.js';
import { resolveBuilder } from '../../util/resolveBuilder.js';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { embedPredicate } from './Assertions.js';
import { EmbedAuthorBuilder } from './EmbedAuthor.js';
import { EmbedFieldBuilder } from './EmbedField.js';
Expand Down Expand Up @@ -343,9 +343,7 @@ export class EmbedBuilder implements JSONEncodable<APIEmbed> {
footer: this.data.footer?.toJSON(false),
};

if (validationOverride ?? isValidationEnabled()) {
embedPredicate.parse(data);
}
validate(embedPredicate, data, validationOverride);

return data;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/messages/embed/EmbedAuthor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { APIEmbedAuthor } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { embedAuthorPredicate } from './Assertions.js';

/**
Expand Down Expand Up @@ -72,10 +72,7 @@ export class EmbedAuthorBuilder {
*/
public toJSON(validationOverride?: boolean): APIEmbedAuthor {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
embedAuthorPredicate.parse(clone);
}
validate(embedAuthorPredicate, clone, validationOverride);

return clone as APIEmbedAuthor;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/messages/embed/EmbedField.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { APIEmbedField } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { embedFieldPredicate } from './Assertions.js';

/**
Expand Down Expand Up @@ -56,10 +56,7 @@ export class EmbedFieldBuilder {
*/
public toJSON(validationOverride?: boolean): APIEmbedField {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
embedFieldPredicate.parse(clone);
}
validate(embedFieldPredicate, clone, validationOverride);

return clone as APIEmbedField;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/builders/src/messages/embed/EmbedFooter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { APIEmbedFooter } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { validate } from '../../util/validation.js';
import { embedFooterPredicate } from './Assertions.js';

/**
Expand Down Expand Up @@ -54,10 +54,7 @@ export class EmbedFooterBuilder {
*/
public toJSON(validationOverride?: boolean): APIEmbedFooter {
const clone = structuredClone(this.data);

if (validationOverride ?? isValidationEnabled()) {
embedFooterPredicate.parse(clone);
}
validate(embedFooterPredicate, clone, validationOverride);

return clone as APIEmbedFooter;
}
Expand Down
Loading

0 comments on commit 8ab4124

Please sign in to comment.