From 072f557c4c8a11a694d784ece7caa920f64fc7f1 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Mon, 13 Dec 2021 12:58:25 -0500 Subject: [PATCH 01/21] feat: add message components --- src/components/ActionRow.ts | 0 src/components/BaseComponent.ts | 15 +++++++ src/components/Button.ts | 61 +++++++++++++++++++++++++++++ src/components/InteractionButton.ts | 4 ++ src/components/LinkButton.ts | 16 ++++++++ src/components/SelectMenu.ts | 3 ++ 6 files changed, 99 insertions(+) create mode 100644 src/components/ActionRow.ts create mode 100644 src/components/BaseComponent.ts create mode 100644 src/components/Button.ts create mode 100644 src/components/InteractionButton.ts create mode 100644 src/components/LinkButton.ts create mode 100644 src/components/SelectMenu.ts diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/BaseComponent.ts b/src/components/BaseComponent.ts new file mode 100644 index 00000000..9ce0fe62 --- /dev/null +++ b/src/components/BaseComponent.ts @@ -0,0 +1,15 @@ +import type { APIBaseMessageComponent, ComponentType } from 'discord-api-types'; + +export abstract class BaseComponent { + public type?: T; + + public constructor(data?: APIBaseMessageComponent | BaseComponent) { + this.type = data?.type; + } + + public toJSON() { + return { + type: this.type!, + }; + } +} diff --git a/src/components/Button.ts b/src/components/Button.ts new file mode 100644 index 00000000..ad75dfa2 --- /dev/null +++ b/src/components/Button.ts @@ -0,0 +1,61 @@ +import { APIButtonComponent, ButtonStyle, ComponentType } from 'discord-api-types'; +import { BaseComponent } from './BaseComponent'; +import { InteractionButtonComponent } from './InteractionButton'; +import { LinkButtonComponent } from './LinkButton'; + +export class ButtonComponent extends BaseComponent { + public style?: T; + public label?: string; + public emoji?: string; + public disabled?: boolean; + public url?: string; + public customId?: string; + + public constructor(data?: APIButtonComponent | ButtonComponent) { + super(data); + } + + public setStyle( + style: S, + ): S extends ButtonStyle.Link ? LinkButtonComponent : InteractionButtonComponent; + public setStyle(style: T) { + this.style = style; + + switch (style) { + case ButtonStyle.Link: + return new LinkButtonComponent(); + default: + return new InteractionButtonComponent(); + } + } + + public setEmoji(emoji: string) { + this.emoji = emoji; + return this; + } + + public setDisabled(disabled: boolean) { + this.disabled = disabled; + return this; + } + + public setLabel(label: string) { + this.label = label; + return this; + } + + public setURL(url: string): LinkButtonComponent { + this.url = url; + return new LinkButtonComponent(this); + } + + public override toJSON() { + return { + ...super.toJSON(), + style: this.style!, + }; + } +} + +new ButtonComponent().setURL('test').setStyle(ButtonStyle.Danger); +new ButtonComponent().setStyle(ButtonStyle.Link); diff --git a/src/components/InteractionButton.ts b/src/components/InteractionButton.ts new file mode 100644 index 00000000..bdeac1b7 --- /dev/null +++ b/src/components/InteractionButton.ts @@ -0,0 +1,4 @@ +import type { ButtonStyle } from 'discord-api-types'; +import { ButtonComponent } from './Button'; + +export class InteractionButtonComponent extends ButtonComponent> {} diff --git a/src/components/LinkButton.ts b/src/components/LinkButton.ts new file mode 100644 index 00000000..29795ba2 --- /dev/null +++ b/src/components/LinkButton.ts @@ -0,0 +1,16 @@ +import type { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types'; +import { ButtonComponent } from './Button'; + +export class LinkButtonComponent extends ButtonComponent { + public constructor(data?: ButtonComponent | APIButtonComponentWithURL) { + super(data); + this.url = data.url!; + } + + public override toJSON(): APIButtonComponentWithURL { + return { + ...super.toJSON(), + url: this.url!, + }; + } +} diff --git a/src/components/SelectMenu.ts b/src/components/SelectMenu.ts new file mode 100644 index 00000000..93f639f3 --- /dev/null +++ b/src/components/SelectMenu.ts @@ -0,0 +1,3 @@ +import { BaseComponent } from './BaseComponent'; + +export class SelectMenuComponent extends BaseComponent {} From 2434201a5ef49cf8fb154a4289e8aa7019cfdcfc Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni Date: Mon, 13 Dec 2021 17:27:50 -0500 Subject: [PATCH 02/21] types: redo some typings --- src/components/Button.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/Button.ts b/src/components/Button.ts index ad75dfa2..d743d6b7 100644 --- a/src/components/Button.ts +++ b/src/components/Button.ts @@ -15,9 +15,6 @@ export class ButtonComponent extends BaseComponent( - style: S, - ): S extends ButtonStyle.Link ? LinkButtonComponent : InteractionButtonComponent; public setStyle(style: T) { this.style = style; @@ -46,7 +43,7 @@ export class ButtonComponent extends BaseComponent extends BaseComponent Date: Tue, 14 Dec 2021 13:37:18 -0500 Subject: [PATCH 03/21] types: finalized button typings --- src/components/BaseButton.ts | 52 ++++++++++++++++++++++++++ src/components/Button.ts | 58 ----------------------------- src/components/InteractionButton.ts | 35 +++++++++++++++-- src/components/LinkButton.ts | 22 ++++++++--- 4 files changed, 100 insertions(+), 67 deletions(-) create mode 100644 src/components/BaseButton.ts delete mode 100644 src/components/Button.ts diff --git a/src/components/BaseButton.ts b/src/components/BaseButton.ts new file mode 100644 index 00000000..1dee5abc --- /dev/null +++ b/src/components/BaseButton.ts @@ -0,0 +1,52 @@ +import type { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; +import { BaseComponent } from './BaseComponent'; + +export type BuilderButtonBaseData = Omit & { style: T }; + +export abstract class BaseButtonComponent extends BaseComponent { + public style!: T; + public label?: string; + public emoji?: APIMessageComponentEmoji; + public disabled?: boolean; + + public constructor(data?: APIButtonComponent | BaseButtonComponent) { + super(data); + } + + /** + * Sets the emoji to display on this button + * @param emoji The emoji to display on this button + */ + public setEmoji(emoji: APIMessageComponentEmoji) { + this.emoji = emoji; + return this; + } + + /** + * Sets whether this button is disable or not + * @param disabled Whether or not to disable this button or not + */ + public setDisabled(disabled: boolean) { + this.disabled = disabled; + return this; + } + + /** + * Sets the label for this button + * @param label The label to display on this button + */ + public setLabel(label: string) { + this.label = label; + return this; + } + + public override toJSON(): BuilderButtonBaseData { + return { + ...super.toJSON(), + style: this.style, + label: this.label, + emoji: this.emoji, + disabled: this.disabled, + }; + } +} diff --git a/src/components/Button.ts b/src/components/Button.ts deleted file mode 100644 index d743d6b7..00000000 --- a/src/components/Button.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { APIButtonComponent, ButtonStyle, ComponentType } from 'discord-api-types'; -import { BaseComponent } from './BaseComponent'; -import { InteractionButtonComponent } from './InteractionButton'; -import { LinkButtonComponent } from './LinkButton'; - -export class ButtonComponent extends BaseComponent { - public style?: T; - public label?: string; - public emoji?: string; - public disabled?: boolean; - public url?: string; - public customId?: string; - - public constructor(data?: APIButtonComponent | ButtonComponent) { - super(data); - } - - public setStyle(style: T) { - this.style = style; - - switch (style) { - case ButtonStyle.Link: - return new LinkButtonComponent(); - default: - return new InteractionButtonComponent(); - } - } - - public setEmoji(emoji: string) { - this.emoji = emoji; - return this; - } - - public setDisabled(disabled: boolean) { - this.disabled = disabled; - return this; - } - - public setLabel(label: string) { - this.label = label; - return this; - } - - public setURL(url: string): LinkButtonComponent { - this.url = url; - return this; - } - - public override toJSON() { - return { - ...super.toJSON(), - style: this.style!, - }; - } -} - -new ButtonComponent().setURL('test').setStyle(ButtonStyle.Link); -new ButtonComponent().setStyle(ButtonStyle.Link); diff --git a/src/components/InteractionButton.ts b/src/components/InteractionButton.ts index bdeac1b7..63596284 100644 --- a/src/components/InteractionButton.ts +++ b/src/components/InteractionButton.ts @@ -1,4 +1,33 @@ -import type { ButtonStyle } from 'discord-api-types'; -import { ButtonComponent } from './Button'; +import type { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types'; +import { BaseButtonComponent } from './BaseButton'; -export class InteractionButtonComponent extends ButtonComponent> {} +/** + * Represents the a button that can send interactions whenever clicked. + */ +export class InteractionButtonComponent extends BaseButtonComponent> { + public customId!: string; + + /** + * Sets the style of this button + * @param style The style to use for this button + */ + public setStyle(style: Exclude) { + this.style = style; + return this; + } + + /** + * Sets the custom Id for this button + * @param customId The custom ID to use for this button + */ + public setCustomId(customId: string) { + this.customId = customId; + } + + public override toJSON(): APIButtonComponentWithCustomId { + return { + ...super.toJSON(), + custom_id: this.customId, + }; + } +} diff --git a/src/components/LinkButton.ts b/src/components/LinkButton.ts index 29795ba2..d713822d 100644 --- a/src/components/LinkButton.ts +++ b/src/components/LinkButton.ts @@ -1,16 +1,26 @@ -import type { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types'; -import { ButtonComponent } from './Button'; +import { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types'; +import { BaseButtonComponent } from './BaseButton'; -export class LinkButtonComponent extends ButtonComponent { - public constructor(data?: ButtonComponent | APIButtonComponentWithURL) { +/** + * Represents a button that opens a specified URL when clicked. + */ +export class LinkButtonComponent extends BaseButtonComponent { + public url!: string; + + public constructor(data?: BaseButtonComponent | APIButtonComponentWithURL) { super(data); - this.url = data.url!; + + if (!(data instanceof BaseButtonComponent) && data !== undefined) { + this.url = data.url; + } + + this.style = ButtonStyle.Link; } public override toJSON(): APIButtonComponentWithURL { return { ...super.toJSON(), - url: this.url!, + url: this.url, }; } } From 46a7055e47d4b5892a9ba4629ce4f97c5e7d3b7d Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni Date: Tue, 14 Dec 2021 14:08:59 -0500 Subject: [PATCH 04/21] chore: add button validations --- src/components/Assertions.ts | 3 +++ src/components/BaseButton.ts | 32 +++++++++++++++++++++++------ src/components/InteractionButton.ts | 16 +++++++++++++++ src/components/LinkButton.ts | 13 ++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/components/Assertions.ts diff --git a/src/components/Assertions.ts b/src/components/Assertions.ts new file mode 100644 index 00000000..40aa57ac --- /dev/null +++ b/src/components/Assertions.ts @@ -0,0 +1,3 @@ +import z from 'zod'; + +export const customIdValidator = z.string().max(100); diff --git a/src/components/BaseButton.ts b/src/components/BaseButton.ts index 1dee5abc..d5489a2f 100644 --- a/src/components/BaseButton.ts +++ b/src/components/BaseButton.ts @@ -1,23 +1,40 @@ import type { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; +import z from 'zod'; import { BaseComponent } from './BaseComponent'; export type BuilderButtonBaseData = Omit & { style: T }; +function validateButtonFields(button: BaseButtonComponent) { + if (button.emoji && button.label) { + throw new TypeError('Cannot construct a button with both a label and an emoji field present.'); + } +} + +const labelValidator = z.string().nonempty().max(80); + +const emojiValidator = z + .object({ + id: z.string(), + name: z.string(), + animated: z.boolean(), + }) + .partial() + .strict(); + +const disabledValidator = z.boolean(); + export abstract class BaseButtonComponent extends BaseComponent { public style!: T; public label?: string; public emoji?: APIMessageComponentEmoji; public disabled?: boolean; - public constructor(data?: APIButtonComponent | BaseButtonComponent) { - super(data); - } - /** * Sets the emoji to display on this button * @param emoji The emoji to display on this button */ - public setEmoji(emoji: APIMessageComponentEmoji) { + public setEmoji(emoji: APIMessageComponentEmoji): Omit { + emojiValidator.parse(emoji); this.emoji = emoji; return this; } @@ -27,6 +44,7 @@ export abstract class BaseButtonComponent extends BaseCom * @param disabled Whether or not to disable this button or not */ public setDisabled(disabled: boolean) { + disabledValidator.parse(disabled); this.disabled = disabled; return this; } @@ -35,12 +53,14 @@ export abstract class BaseButtonComponent extends BaseCom * Sets the label for this button * @param label The label to display on this button */ - public setLabel(label: string) { + public setLabel(label: string): Omit { + labelValidator.parse(label); this.label = label; return this; } public override toJSON(): BuilderButtonBaseData { + validateButtonFields(this); return { ...super.toJSON(), style: this.style, diff --git a/src/components/InteractionButton.ts b/src/components/InteractionButton.ts index 63596284..e05bbeda 100644 --- a/src/components/InteractionButton.ts +++ b/src/components/InteractionButton.ts @@ -1,17 +1,32 @@ import type { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types'; +import z from 'zod'; +import { customIdValidator } from './Assertions'; import { BaseButtonComponent } from './BaseButton'; +const styleValidator = z.union([z.string(), z.number()]); + /** * Represents the a button that can send interactions whenever clicked. */ export class InteractionButtonComponent extends BaseButtonComponent> { public customId!: string; + public constructor( + data?: BaseButtonComponent> | APIButtonComponentWithCustomId, + ) { + super(data); + + if (!(data instanceof BaseButtonComponent) && data !== undefined) { + this.customId = data.custom_id; + } + } + /** * Sets the style of this button * @param style The style to use for this button */ public setStyle(style: Exclude) { + styleValidator.parse(style); this.style = style; return this; } @@ -21,6 +36,7 @@ export class InteractionButtonComponent extends BaseButtonComponent { this.style = ButtonStyle.Link; } + /** + * Sets the URL for this button + * @param url The URL to open when this button is clicked + */ + public setURL(url: string) { + urlValidator.parse(url); + this.url = url; + return this; + } + public override toJSON(): APIButtonComponentWithURL { return { ...super.toJSON(), From 3c12a8bf6386d2c8e758f8a3783ba9cfd0ebdf58 Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni Date: Tue, 14 Dec 2021 15:49:07 -0500 Subject: [PATCH 05/21] feat: add select menu component; --- src/components/Assertions.ts | 13 +++- src/components/BaseButton.ts | 18 ++--- src/components/BaseComponent.ts | 12 ++-- src/components/InteractionButton.ts | 14 +--- src/components/LinkButton.ts | 12 ++-- src/components/SelectMenu.ts | 102 +++++++++++++++++++++++++++- src/components/SelectMenuOption.ts | 81 ++++++++++++++++++++++ 7 files changed, 213 insertions(+), 39 deletions(-) create mode 100644 src/components/SelectMenuOption.ts diff --git a/src/components/Assertions.ts b/src/components/Assertions.ts index 40aa57ac..3d3f6931 100644 --- a/src/components/Assertions.ts +++ b/src/components/Assertions.ts @@ -1,3 +1,14 @@ import z from 'zod'; -export const customIdValidator = z.string().max(100); +export const customIdValidator = z.string().min(1).max(100); + +export const emojiValidator = z + .object({ + id: z.string(), + name: z.string(), + animated: z.boolean(), + }) + .partial() + .strict(); + +export const disabledValidator = z.boolean(); diff --git a/src/components/BaseButton.ts b/src/components/BaseButton.ts index d5489a2f..1fdbee08 100644 --- a/src/components/BaseButton.ts +++ b/src/components/BaseButton.ts @@ -1,5 +1,6 @@ -import type { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; +import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; import z from 'zod'; +import { disabledValidator, emojiValidator } from './Assertions'; import { BaseComponent } from './BaseComponent'; export type BuilderButtonBaseData = Omit & { style: T }; @@ -12,23 +13,16 @@ function validateButtonFields(button: BaseButtonComponent) { const labelValidator = z.string().nonempty().max(80); -const emojiValidator = z - .object({ - id: z.string(), - name: z.string(), - animated: z.boolean(), - }) - .partial() - .strict(); - -const disabledValidator = z.boolean(); - export abstract class BaseButtonComponent extends BaseComponent { public style!: T; public label?: string; public emoji?: APIMessageComponentEmoji; public disabled?: boolean; + public constructor() { + super(ComponentType.Button); + } + /** * Sets the emoji to display on this button * @param emoji The emoji to display on this button diff --git a/src/components/BaseComponent.ts b/src/components/BaseComponent.ts index 9ce0fe62..b61a289f 100644 --- a/src/components/BaseComponent.ts +++ b/src/components/BaseComponent.ts @@ -1,15 +1,15 @@ -import type { APIBaseMessageComponent, ComponentType } from 'discord-api-types'; +import type { ComponentType } from 'discord-api-types'; -export abstract class BaseComponent { - public type?: T; +export class BaseComponent { + public type: T; - public constructor(data?: APIBaseMessageComponent | BaseComponent) { - this.type = data?.type; + public constructor(type: T) { + this.type = type; } public toJSON() { return { - type: this.type!, + type: this.type, }; } } diff --git a/src/components/InteractionButton.ts b/src/components/InteractionButton.ts index e05bbeda..3c1dce56 100644 --- a/src/components/InteractionButton.ts +++ b/src/components/InteractionButton.ts @@ -11,22 +11,11 @@ const styleValidator = z.union([z.string(), z.number()]); export class InteractionButtonComponent extends BaseButtonComponent> { public customId!: string; - public constructor( - data?: BaseButtonComponent> | APIButtonComponentWithCustomId, - ) { - super(data); - - if (!(data instanceof BaseButtonComponent) && data !== undefined) { - this.customId = data.custom_id; - } - } - /** * Sets the style of this button * @param style The style to use for this button */ public setStyle(style: Exclude) { - styleValidator.parse(style); this.style = style; return this; } @@ -38,9 +27,12 @@ export class InteractionButtonComponent extends BaseButtonComponent { public url!: string; - public constructor(data?: BaseButtonComponent | APIButtonComponentWithURL) { - super(data); - - if (!(data instanceof BaseButtonComponent) && data !== undefined) { - this.url = data.url; - } - + public constructor() { + super(); this.style = ButtonStyle.Link; } @@ -25,12 +20,13 @@ export class LinkButtonComponent extends BaseButtonComponent { * @param url The URL to open when this button is clicked */ public setURL(url: string) { - urlValidator.parse(url); this.url = url; return this; } public override toJSON(): APIButtonComponentWithURL { + // url is required. + urlValidator.parse(this.url); return { ...super.toJSON(), url: this.url, diff --git a/src/components/SelectMenu.ts b/src/components/SelectMenu.ts index 93f639f3..eea75dda 100644 --- a/src/components/SelectMenu.ts +++ b/src/components/SelectMenu.ts @@ -1,3 +1,103 @@ +import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; +import { customIdValidator } from './Assertions'; import { BaseComponent } from './BaseComponent'; +import z from 'zod'; +import type { SelectMenuOption } from './SelectMenuOption'; -export class SelectMenuComponent extends BaseComponent {} +const placeholderValidator = z.string().max(100); +const minMaxValidator = z.number().max(25).min(0); + +const optionsValidator = z.object({}).array().nonempty(); + +function validateRequiredParameters(customId: string, options: SelectMenuOption[]) { + customIdValidator.parse(customId); + optionsValidator.parse(options); +} + +/** + * Represents a select menu component + */ +export class SelectMenuComponent extends BaseComponent { + public options!: SelectMenuOption[]; + public placeholder?: string; + public minValues?: number; + public maxValues?: number; + public customId!: string; + public disabled?: boolean; + + public constructor() { + super(ComponentType.SelectMenu); + } + + /** + * Sets the placeholder for this select menu + * @param placeholder The placeholder to use for this select menu + */ + public setPlaceholder(placeholder: string) { + placeholderValidator.parse(placeholder); + this.placeholder = placeholder; + return this; + } + + /** + * Sets thes minimum values that must be selected in the select menu + * @param minValues The minimum values that must be selected + */ + public setMinValues(minValues: number) { + minMaxValidator.parse(minValues); + this.minValues = minValues; + return this; + } + + /** + * Sets thes maximum values that must be selected in the select menu + * @param minValues The maximum values that must be selected + */ + public setMaxValues(maxValues: number) { + minMaxValidator.parse(maxValues); + this.maxValues = maxValues; + return this; + } + + /** + * Sets the custom Id for this select menu + * @param customId The custom ID to use for this select menu + */ + public setCustomId(customId: string) { + customIdValidator.parse(customId); + this.customId = customId; + return this; + } + + /** + * Adds options to this select menu + * @param options The options to add to this select menu + * @returns + */ + public addOptions(...options: SelectMenuOption[]) { + this.options.push(...options); + return this; + } + + /** + * Sets the options on this select menu + * @param options The options to set on this select menu + */ + public setOptions(options: SelectMenuOption[]) { + this.options = options; + return this; + } + + public override toJSON(): APISelectMenuComponent { + validateRequiredParameters(this.customId, this.options); + return { + ...super.toJSON(), + custom_id: this.customId, + options: this.options.map((option) => option.toJSON()), + placeholder: this.placeholder, + min_values: this.minValues, + max_values: this.maxValues, + disabled: this.disabled, + }; + } +} diff --git a/src/components/SelectMenuOption.ts b/src/components/SelectMenuOption.ts new file mode 100644 index 00000000..6b37db25 --- /dev/null +++ b/src/components/SelectMenuOption.ts @@ -0,0 +1,81 @@ +import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types'; +import z from 'zod'; +import { emojiValidator } from './Assertions'; + +const labelValueValidator = z.string().min(1).max(100); +const defaultValidator = z.boolean(); + +export function validateRequiredParameters(label: string, value: string) { + labelValueValidator.parse(label); + labelValueValidator.parse(value); +} + +/** + * Represents an option within a select menu component + */ +export class SelectMenuOption { + public label!: string; + public value!: string; + public description?: string; + public emoji?: APIMessageComponentEmoji; + public default?: boolean; + + /** + * Sets the label of this option + * @param label The label to show on this option + */ + public setLabel(label: string) { + this.label = label; + return this; + } + + /** + * Sets the value of this option + * @param value The value of this option + */ + public setValue(value: string) { + this.value = value; + return this; + } + + /** + * Sets the description of this option. + * @param description The description of this option + */ + public setDescription(description: string) { + labelValueValidator.parse(description); + this.description = description; + return this; + } + + /** + * Sets whether this option is selected by default + * @param isDefault Whether or not this option is selected by default + */ + public setDefault(isDefault: boolean) { + defaultValidator.parse(isDefault); + this.default = isDefault; + return this; + } + + /** + * Sets the emoji to display on this button + * @param emoji The emoji to display on this button + */ + public setEmoji(emoji: APIMessageComponentEmoji) { + emojiValidator.parse(emoji); + this.emoji = emoji; + return this; + } + + public toJSON(): APISelectMenuOption { + validateRequiredParameters(this.label, this.value); + return { + label: this.label, + value: this.value, + description: this.description, + emoji: this.emoji, + default: this.default, + }; + } +} From 907da2f34f23b447bc66e12704d1becf7ef11c2e Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni Date: Tue, 14 Dec 2021 16:01:37 -0500 Subject: [PATCH 06/21] chore: move to folders --- src/components/{ => button}/BaseButton.ts | 4 ++-- .../{ => button}/InteractionButton.ts | 2 +- src/components/{ => button}/LinkButton.ts | 0 src/components/{ => selectMenu}/SelectMenu.ts | 20 +++++++++++++++---- .../{ => selectMenu}/SelectMenuOption.ts | 14 ++++++++++++- src/index.ts | 7 +++++++ 6 files changed, 39 insertions(+), 8 deletions(-) rename src/components/{ => button}/BaseButton.ts (93%) rename src/components/{ => button}/InteractionButton.ts (95%) rename src/components/{ => button}/LinkButton.ts (100%) rename src/components/{ => selectMenu}/SelectMenu.ts (82%) rename src/components/{ => selectMenu}/SelectMenuOption.ts (87%) diff --git a/src/components/BaseButton.ts b/src/components/button/BaseButton.ts similarity index 93% rename from src/components/BaseButton.ts rename to src/components/button/BaseButton.ts index 1fdbee08..5b7f43ee 100644 --- a/src/components/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -1,7 +1,7 @@ import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; import z from 'zod'; -import { disabledValidator, emojiValidator } from './Assertions'; -import { BaseComponent } from './BaseComponent'; +import { disabledValidator, emojiValidator } from '../Assertions'; +import { BaseComponent } from '../BaseComponent'; export type BuilderButtonBaseData = Omit & { style: T }; diff --git a/src/components/InteractionButton.ts b/src/components/button/InteractionButton.ts similarity index 95% rename from src/components/InteractionButton.ts rename to src/components/button/InteractionButton.ts index 3c1dce56..0f199de7 100644 --- a/src/components/InteractionButton.ts +++ b/src/components/button/InteractionButton.ts @@ -1,6 +1,6 @@ import type { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types'; import z from 'zod'; -import { customIdValidator } from './Assertions'; +import { customIdValidator } from '../Assertions'; import { BaseButtonComponent } from './BaseButton'; const styleValidator = z.union([z.string(), z.number()]); diff --git a/src/components/LinkButton.ts b/src/components/button/LinkButton.ts similarity index 100% rename from src/components/LinkButton.ts rename to src/components/button/LinkButton.ts diff --git a/src/components/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts similarity index 82% rename from src/components/SelectMenu.ts rename to src/components/selectMenu/SelectMenu.ts index eea75dda..14710109 100644 --- a/src/components/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,8 +1,8 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; -import { customIdValidator } from './Assertions'; -import { BaseComponent } from './BaseComponent'; +import { customIdValidator } from '../Assertions'; +import { BaseComponent } from '../BaseComponent'; import z from 'zod'; -import type { SelectMenuOption } from './SelectMenuOption'; +import { SelectMenuOption } from './SelectMenuOption'; const placeholderValidator = z.string().max(100); const minMaxValidator = z.number().max(25).min(0); @@ -25,8 +25,20 @@ export class SelectMenuComponent extends BaseComponent public customId!: string; public disabled?: boolean; - public constructor() { + public constructor(data?: APISelectMenuComponent) { super(ComponentType.SelectMenu); + + if (!data) { + this.options = []; + return; + } + + this.options = data.options ? data.options.map((option) => new SelectMenuOption(option)) : []; + this.placeholder = data.placeholder; + this.minValues = data.min_values; + this.maxValues = data.max_values; + this.customId = data.custom_id; + this.disabled = data.disabled; } /** diff --git a/src/components/SelectMenuOption.ts b/src/components/selectMenu/SelectMenuOption.ts similarity index 87% rename from src/components/SelectMenuOption.ts rename to src/components/selectMenu/SelectMenuOption.ts index 6b37db25..bce20a74 100644 --- a/src/components/SelectMenuOption.ts +++ b/src/components/selectMenu/SelectMenuOption.ts @@ -1,6 +1,6 @@ import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types'; import z from 'zod'; -import { emojiValidator } from './Assertions'; +import { emojiValidator } from '../Assertions'; const labelValueValidator = z.string().min(1).max(100); const defaultValidator = z.boolean(); @@ -20,6 +20,18 @@ export class SelectMenuOption { public emoji?: APIMessageComponentEmoji; public default?: boolean; + public constructor(data?: APISelectMenuOption) { + if (!data) { + return; + } + + this.label = data.label; + this.value = data.value; + this.description = data.description; + this.emoji = data.emoji; + this.default = data.default; + } + /** * Sets the label of this option * @param label The label to show on this option diff --git a/src/index.ts b/src/index.ts index fa51ade9..fce484f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,13 @@ export * as EmbedAssertions from './messages/embed/Assertions'; export * from './messages/embed/Embed'; export * from './messages/formatters'; +export * as ComponentAssertions from './components/Assertions'; +export * from './components/button/BaseButton'; +export * from './components/button/InteractionButton'; +export * from './components/button/LinkButton'; +export * from './components/selectMenu/SelectMenu'; +export * from './components/selectMenu/SelectMenuOption'; + export * as SlashCommandAssertions from './interactions/slashCommands/Assertions'; export * from './interactions/slashCommands/SlashCommandBuilder'; export * from './interactions/slashCommands/SlashCommandSubcommands'; From 71d964e68a995dfe529373c3a9f06bd2673b8e31 Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni Date: Tue, 14 Dec 2021 16:35:04 -0500 Subject: [PATCH 07/21] feat: add action row component --- src/components/ActionRow.ts | 60 ++++++++++++++++++++++ src/components/BaseComponent.ts | 2 +- src/components/button/BaseButton.ts | 10 +++- src/components/button/InteractionButton.ts | 11 ++++ src/components/button/LinkButton.ts | 10 +++- src/index.ts | 1 + 6 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index e69de29b..6b3cedb5 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -0,0 +1,60 @@ +import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; +import { InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '..'; +import { BaseComponent } from './BaseComponent'; + +export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponent | SelectMenuComponent; + +// TODO: Add valid form component types + +/** + * Represents an action row component + */ +export class ActionRow extends BaseComponent { + public components: T[] = []; + + public constructor(data?: APIActionRowComponent) { + super(ComponentType.ActionRow); + + if (!data) { + return; + } + + this.components = data.components.map((component) => { + switch (component.type) { + case ComponentType.Button: + if (component.style === ButtonStyle.Link) { + return new LinkButtonComponent(component); + } + return new InteractionButtonComponent(component); + case ComponentType.SelectMenu: + return new SelectMenuComponent(component); + } + }) as T[]; + } + + /** + * Adds components to this action row. + * @param components The components to add to this action row. + * @returns + */ + public addComponents(...components: T[]) { + this.components.push(...components); + return this; + } + + /** + * Sets the components in this action row + * @param components The components to set this row to + */ + public setComponents(components: T[]) { + this.components = components; + return this; + } + + public override toJSON(): APIActionRowComponent { + return { + ...super.toJSON(), + components: this.components.map((component) => component.toJSON()), + }; + } +} diff --git a/src/components/BaseComponent.ts b/src/components/BaseComponent.ts index b61a289f..68e1dcb8 100644 --- a/src/components/BaseComponent.ts +++ b/src/components/BaseComponent.ts @@ -1,6 +1,6 @@ import type { ComponentType } from 'discord-api-types'; -export class BaseComponent { +export abstract class BaseComponent { public type: T; public constructor(type: T) { diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index 5b7f43ee..d1891e8b 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -19,8 +19,16 @@ export abstract class BaseButtonComponent extends BaseCom public emoji?: APIMessageComponentEmoji; public disabled?: boolean; - public constructor() { + public constructor(data?: APIButtonComponent) { super(ComponentType.Button); + + if (!data) { + return; + } + + this.label = data.label; + this.emoji = data.emoji; + this.disabled = data.disabled; } /** diff --git a/src/components/button/InteractionButton.ts b/src/components/button/InteractionButton.ts index 0f199de7..8f4942da 100644 --- a/src/components/button/InteractionButton.ts +++ b/src/components/button/InteractionButton.ts @@ -11,6 +11,17 @@ const styleValidator = z.union([z.string(), z.number()]); export class InteractionButtonComponent extends BaseButtonComponent> { public customId!: string; + public constructor(data?: APIButtonComponentWithCustomId) { + super(data); + + if (!data) { + return; + } + + this.customId = data.custom_id; + this.style = data.style; + } + /** * Sets the style of this button * @param style The style to use for this button diff --git a/src/components/button/LinkButton.ts b/src/components/button/LinkButton.ts index ee17d732..db4099d3 100644 --- a/src/components/button/LinkButton.ts +++ b/src/components/button/LinkButton.ts @@ -10,9 +10,15 @@ const urlValidator = z.string().url(); export class LinkButtonComponent extends BaseButtonComponent { public url!: string; - public constructor() { - super(); + public constructor(data?: APIButtonComponentWithURL) { + super(data); this.style = ButtonStyle.Link; + + if (!data) { + return; + } + + this.url = data.url; } /** diff --git a/src/index.ts b/src/index.ts index fce484f2..02cb0323 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ export * from './messages/embed/Embed'; export * from './messages/formatters'; export * as ComponentAssertions from './components/Assertions'; +export * from './components/ActionRow'; export * from './components/button/BaseButton'; export * from './components/button/InteractionButton'; export * from './components/button/LinkButton'; From e69bd134d2d88425bf342d3aeddd44647ba81dd2 Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni Date: Tue, 14 Dec 2021 17:02:10 -0500 Subject: [PATCH 08/21] chore: begin writing unit tests --- __tests__/components/button.ts | 48 +++++++++++++++++++ src/components/Assertions.ts | 2 +- src/components/button/BaseButton.ts | 8 ++-- src/components/button/InteractionButton.ts | 6 +-- src/components/button/LinkButton.ts | 4 +- src/components/selectMenu/SelectMenu.ts | 8 ++-- src/components/selectMenu/SelectMenuOption.ts | 6 +-- 7 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 __tests__/components/button.ts diff --git a/__tests__/components/button.ts b/__tests__/components/button.ts new file mode 100644 index 00000000..8e10d261 --- /dev/null +++ b/__tests__/components/button.ts @@ -0,0 +1,48 @@ +import { ButtonStyle } from 'discord-api-types'; +import { buttonLabelValidator, InteractionButtonComponent, styleValidator } from '../../src/index'; + +const interactionButtonComponent = () => new InteractionButtonComponent(); + +describe('Button Components', () => { + describe('Assertion Tests', () => { + test('GIVEN valid label THEN validator does not throw', () => { + expect(() => buttonLabelValidator.parse('foobar')).not.toThrowError(); + }); + + test('GIVEN invalid label THEN validator does throw', () => { + expect(() => buttonLabelValidator.parse(null)).toThrowError(); + expect(() => buttonLabelValidator.parse('')).toThrowError(); + + expect(() => + buttonLabelValidator.parse( + 'loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong', + ), + ); + }); + + test('GIVEN valid style then validator does not throw', () => { + expect(() => styleValidator.parse(3)).not.toThrowError(); + expect(() => styleValidator.parse(ButtonStyle.Secondary)).not.toThrowError(); + }); + + test('GIVEN invalid style then validator does not throw', () => { + expect(() => styleValidator.parse(7)).toThrowError(); + + // Note: this throws because link styles are always set for you. + expect(() => styleValidator.parse(ButtonStyle.Link)).toThrowError(); + }); + + test('GIVEN valid fields THEN builder does not throw', () => { + expect(() => + interactionButtonComponent().setCustomId('custom').setStyle(ButtonStyle.Primary).setLabel('test'), + ).not.toThrowError(); + expect(() => + interactionButtonComponent() + .setCustomId('custom') + .setStyle(ButtonStyle.Primary) + .setLabel('test') + .setDisabled(true), + ).not.toThrowError(); + }); + }); +}); diff --git a/src/components/Assertions.ts b/src/components/Assertions.ts index 3d3f6931..79868fef 100644 --- a/src/components/Assertions.ts +++ b/src/components/Assertions.ts @@ -1,4 +1,4 @@ -import z from 'zod'; +import { z } from 'zod'; export const customIdValidator = z.string().min(1).max(100); diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index d1891e8b..ee162877 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -1,17 +1,17 @@ import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; -import z from 'zod'; +import { z } from 'zod'; import { disabledValidator, emojiValidator } from '../Assertions'; import { BaseComponent } from '../BaseComponent'; export type BuilderButtonBaseData = Omit & { style: T }; -function validateButtonFields(button: BaseButtonComponent) { +export function validateButtonFields(button: BaseButtonComponent) { if (button.emoji && button.label) { throw new TypeError('Cannot construct a button with both a label and an emoji field present.'); } } -const labelValidator = z.string().nonempty().max(80); +export const buttonLabelValidator = z.string().nonempty().max(80); export abstract class BaseButtonComponent extends BaseComponent { public style!: T; @@ -56,7 +56,7 @@ export abstract class BaseButtonComponent extends BaseCom * @param label The label to display on this button */ public setLabel(label: string): Omit { - labelValidator.parse(label); + buttonLabelValidator.parse(label); this.label = label; return this; } diff --git a/src/components/button/InteractionButton.ts b/src/components/button/InteractionButton.ts index 8f4942da..b5654340 100644 --- a/src/components/button/InteractionButton.ts +++ b/src/components/button/InteractionButton.ts @@ -1,9 +1,9 @@ -import type { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types'; -import z from 'zod'; +import { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types'; +import { z } from 'zod'; import { customIdValidator } from '../Assertions'; import { BaseButtonComponent } from './BaseButton'; -const styleValidator = z.union([z.string(), z.number()]); +export const styleValidator = z.number().min(ButtonStyle.Primary).max(ButtonStyle.Danger); /** * Represents the a button that can send interactions whenever clicked. diff --git a/src/components/button/LinkButton.ts b/src/components/button/LinkButton.ts index db4099d3..b5660d90 100644 --- a/src/components/button/LinkButton.ts +++ b/src/components/button/LinkButton.ts @@ -1,8 +1,8 @@ import { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types'; import { BaseButtonComponent } from './BaseButton'; -import z from 'zod'; +import { z } from 'zod'; -const urlValidator = z.string().url(); +export const urlValidator = z.string().url(); /** * Represents a button that opens a specified URL when clicked. diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index 14710109..754d51d0 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,13 +1,13 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; import { customIdValidator } from '../Assertions'; import { BaseComponent } from '../BaseComponent'; -import z from 'zod'; +import { z } from 'zod'; import { SelectMenuOption } from './SelectMenuOption'; -const placeholderValidator = z.string().max(100); -const minMaxValidator = z.number().max(25).min(0); +export const placeholderValidator = z.string().max(100); +export const minMaxValidator = z.number().max(25).min(0); -const optionsValidator = z.object({}).array().nonempty(); +export const optionsValidator = z.object({}).array().nonempty(); function validateRequiredParameters(customId: string, options: SelectMenuOption[]) { customIdValidator.parse(customId); diff --git a/src/components/selectMenu/SelectMenuOption.ts b/src/components/selectMenu/SelectMenuOption.ts index bce20a74..b8872723 100644 --- a/src/components/selectMenu/SelectMenuOption.ts +++ b/src/components/selectMenu/SelectMenuOption.ts @@ -1,9 +1,9 @@ import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types'; -import z from 'zod'; +import { z } from 'zod'; import { emojiValidator } from '../Assertions'; -const labelValueValidator = z.string().min(1).max(100); -const defaultValidator = z.boolean(); +export const labelValueValidator = z.string().min(1).max(100); +export const defaultValidator = z.boolean(); export function validateRequiredParameters(label: string, value: string) { labelValueValidator.parse(label); From 9f065fbd219f40278e2dd445e659afe8ad703502 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 14 Dec 2021 18:22:22 -0500 Subject: [PATCH 09/21] test: add button test --- __tests__/components/button.test.ts | 118 ++++++++++++++++++++++++++++ __tests__/components/button.ts | 48 ----------- src/components/button/LinkButton.ts | 1 + 3 files changed, 119 insertions(+), 48 deletions(-) create mode 100644 __tests__/components/button.test.ts delete mode 100644 __tests__/components/button.ts diff --git a/__tests__/components/button.test.ts b/__tests__/components/button.test.ts new file mode 100644 index 00000000..941fa823 --- /dev/null +++ b/__tests__/components/button.test.ts @@ -0,0 +1,118 @@ +import { + APIButtonComponentWithCustomId, + APIButtonComponentWithURL, + ButtonStyle, + ComponentType, +} from 'discord-api-types'; +import { buttonLabelValidator, InteractionButtonComponent, LinkButtonComponent, styleValidator } from '../../src/index'; + +const interactionButtonComponent = () => new InteractionButtonComponent(); +const linkButtonComponent = () => new LinkButtonComponent(); + +const longStr = + 'looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong'; + +describe('Button Components', () => { + describe('Assertion Tests', () => { + test('GIVEN valid label THEN validator does not throw', () => { + expect(() => buttonLabelValidator.parse('foobar')).not.toThrowError(); + }); + + test('GIVEN invalid label THEN validator does throw', () => { + expect(() => buttonLabelValidator.parse(null)).toThrowError(); + expect(() => buttonLabelValidator.parse('')).toThrowError(); + + expect(() => buttonLabelValidator.parse(longStr)).toThrowError(); + }); + + test('GIVEN valid style then validator does not throw', () => { + expect(() => styleValidator.parse(3)).not.toThrowError(); + expect(() => styleValidator.parse(ButtonStyle.Secondary)).not.toThrowError(); + }); + + test('GIVEN invalid style then validator does not throw', () => { + expect(() => styleValidator.parse(7)).toThrowError(); + + // Note: this throws because link styles are always set for you. + expect(() => styleValidator.parse(ButtonStyle.Link)).toThrowError(); + }); + + test('GIVEN valid fields THEN builder does not throw', () => { + expect(() => + interactionButtonComponent().setCustomId('custom').setStyle(ButtonStyle.Primary).setLabel('test'), + ).not.toThrowError(); + + expect(() => { + const button = interactionButtonComponent() + .setCustomId('custom') + .setStyle(ButtonStyle.Primary) + .setDisabled(true) + .setEmoji({ name: 'test' }); + + button.toJSON(); + }).not.toThrowError(); + + expect(() => linkButtonComponent().setURL('https://google.com')).not.toThrowError(); + }); + + test('GIVEN invalid fields THEN build does throw', () => { + expect(() => { + interactionButtonComponent().setCustomId(longStr); + }).toThrowError(); + + expect(() => { + const button = interactionButtonComponent() + .setCustomId('custom') + .setStyle(ButtonStyle.Primary) + .setDisabled(true) + .setLabel('test') + // @ts-expect-error + .setEmoji({ name: 'test' }); + + button.toJSON(); + }).toThrowError(); + + expect(() => interactionButtonComponent().setStyle(24)); + expect(() => interactionButtonComponent().setLabel(longStr)); + // @ts-ignore + expect(() => interactionButtonComponent().setDisabled(0)); + // @ts-ignore + expect(() => interactionButtonComponent().setEmoji('foo')); + + expect(() => linkButtonComponent().setURL('foobar')).toThrowError(); + }); + + test('GiVEN valid input THEN valid JSON outputs are given', () => { + const interactionData: APIButtonComponentWithCustomId = { + type: ComponentType.Button, + custom_id: 'test', + label: 'test', + style: ButtonStyle.Primary, + disabled: true, + }; + + expect(new InteractionButtonComponent(interactionData).toJSON()).toEqual(interactionData); + + expect( + interactionButtonComponent() + .setCustomId(interactionData.custom_id) + .setLabel(interactionData.label) + .setStyle(interactionData.style) + .setDisabled(interactionData.disabled) + .toJSON(), + ).toEqual(interactionData); + + const linkData: APIButtonComponentWithURL = { + type: ComponentType.Button, + label: 'test', + style: ButtonStyle.Link, + disabled: true, + url: 'https://google.com', + }; + + expect(new LinkButtonComponent(linkData).toJSON()).toEqual(linkData); + + expect(linkButtonComponent().setLabel(linkData.label).setDisabled(true).setURL(linkData.url)); + }); + }); +}); diff --git a/__tests__/components/button.ts b/__tests__/components/button.ts deleted file mode 100644 index 8e10d261..00000000 --- a/__tests__/components/button.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ButtonStyle } from 'discord-api-types'; -import { buttonLabelValidator, InteractionButtonComponent, styleValidator } from '../../src/index'; - -const interactionButtonComponent = () => new InteractionButtonComponent(); - -describe('Button Components', () => { - describe('Assertion Tests', () => { - test('GIVEN valid label THEN validator does not throw', () => { - expect(() => buttonLabelValidator.parse('foobar')).not.toThrowError(); - }); - - test('GIVEN invalid label THEN validator does throw', () => { - expect(() => buttonLabelValidator.parse(null)).toThrowError(); - expect(() => buttonLabelValidator.parse('')).toThrowError(); - - expect(() => - buttonLabelValidator.parse( - 'loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong', - ), - ); - }); - - test('GIVEN valid style then validator does not throw', () => { - expect(() => styleValidator.parse(3)).not.toThrowError(); - expect(() => styleValidator.parse(ButtonStyle.Secondary)).not.toThrowError(); - }); - - test('GIVEN invalid style then validator does not throw', () => { - expect(() => styleValidator.parse(7)).toThrowError(); - - // Note: this throws because link styles are always set for you. - expect(() => styleValidator.parse(ButtonStyle.Link)).toThrowError(); - }); - - test('GIVEN valid fields THEN builder does not throw', () => { - expect(() => - interactionButtonComponent().setCustomId('custom').setStyle(ButtonStyle.Primary).setLabel('test'), - ).not.toThrowError(); - expect(() => - interactionButtonComponent() - .setCustomId('custom') - .setStyle(ButtonStyle.Primary) - .setLabel('test') - .setDisabled(true), - ).not.toThrowError(); - }); - }); -}); diff --git a/src/components/button/LinkButton.ts b/src/components/button/LinkButton.ts index b5660d90..c4ced4bf 100644 --- a/src/components/button/LinkButton.ts +++ b/src/components/button/LinkButton.ts @@ -26,6 +26,7 @@ export class LinkButtonComponent extends BaseButtonComponent { * @param url The URL to open when this button is clicked */ public setURL(url: string) { + urlValidator.parse(url); this.url = url; return this; } From f2289880aa59cbcd6d089eed9ce096fe33411a8a Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 14 Dec 2021 18:51:43 -0500 Subject: [PATCH 10/21] test: add select menu tests --- __tests__/components/actionRow.test.ts | 0 __tests__/components/selectMenu.test.ts | 72 +++++++++++++++++++++++++ src/components/selectMenu/SelectMenu.ts | 14 ++++- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 __tests__/components/actionRow.test.ts create mode 100644 __tests__/components/selectMenu.test.ts diff --git a/__tests__/components/actionRow.test.ts b/__tests__/components/actionRow.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/__tests__/components/selectMenu.test.ts b/__tests__/components/selectMenu.test.ts new file mode 100644 index 00000000..a5abd302 --- /dev/null +++ b/__tests__/components/selectMenu.test.ts @@ -0,0 +1,72 @@ +import { APISelectMenuComponent, APISelectMenuOption, ComponentType } from 'discord-api-types'; +import { SelectMenuComponent, SelectMenuOption } from '../../src/index'; + +const selectMenu = () => new SelectMenuComponent(); +const selectMenuOption = () => new SelectMenuOption(); + +const longStr = + 'looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong'; + +describe('Button Components', () => { + describe('Assertion Tests', () => { + test('GIVEN valid inputs THEN Select Menu does not throw', () => { + expect(() => selectMenu().setCustomId('foo')).not.toThrowError(); + expect(() => selectMenu().setMaxValues(10)).not.toThrowError(); + expect(() => selectMenu().setMinValues(3)).not.toThrowError(); + expect(() => selectMenu().setDisabled(true)).not.toThrowError(); + expect(() => selectMenu().setPlaceholder('description')).not.toThrowError(); + + const option = selectMenuOption() + .setLabel('test') + .setValue('test') + .setDefault(true) + .setEmoji({ name: 'test' }) + .setDescription('description'); + expect(() => selectMenu().addOptions(option)).not.toThrowError(); + expect(() => selectMenu().setOptions([option])).not.toThrowError(); + }); + + test('GIVEN invalid inputs THEN Select Menu does throw', () => { + expect(() => selectMenu().setCustomId(longStr)).toThrowError(); + expect(() => selectMenu().setMaxValues(30)).toThrowError(); + expect(() => selectMenu().setMinValues(-20)).toThrowError(); + // @ts-expect-error + expect(() => selectMenu().setDisabled(0)).toThrowError(); + expect(() => selectMenu().setPlaceholder(longStr)).toThrowError(); + + expect(() => { + selectMenuOption() + .setLabel(longStr) + .setValue(longStr) + // @ts-expect-error + .setDefault(-1) + // @ts-expect-error + .setEmoji({ name: 1 }) + .setDescription(longStr); + }).toThrowError(); + }); + + test('GIVEN valid JSON input THEN valid JSON history is correct', () => { + const selectMenuOptionData: APISelectMenuOption = { + label: 'test', + value: 'test', + emoji: { name: 'test' }, + default: true, + description: 'test', + }; + + const selectMenuData: APISelectMenuComponent = { + type: ComponentType.SelectMenu, + custom_id: 'test', + max_values: 10, + min_values: 3, + disabled: true, + options: [selectMenuOptionData], + placeholder: 'test', + }; + + expect(new SelectMenuComponent(selectMenuData).toJSON()).toEqual(selectMenuData); + expect(new SelectMenuOption(selectMenuOptionData).toJSON()).toEqual(selectMenuOptionData); + }); + }); +}); diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index 754d51d0..a737924e 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,5 +1,5 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; -import { customIdValidator } from '../Assertions'; +import { customIdValidator, disabledValidator } from '../Assertions'; import { BaseComponent } from '../BaseComponent'; import { z } from 'zod'; import { SelectMenuOption } from './SelectMenuOption'; @@ -33,7 +33,7 @@ export class SelectMenuComponent extends BaseComponent return; } - this.options = data.options ? data.options.map((option) => new SelectMenuOption(option)) : []; + this.options = data.options!.map((option) => new SelectMenuOption(option)); this.placeholder = data.placeholder; this.minValues = data.min_values; this.maxValues = data.max_values; @@ -81,6 +81,16 @@ export class SelectMenuComponent extends BaseComponent return this; } + /** + * Sets whether or not this select menu is disabled + * @param disabled Whether or not this select menu is disabled + */ + public setDisabled(disabled: boolean) { + disabledValidator.parse(disabled); + this.disabled = disabled; + return this; + } + /** * Adds options to this select menu * @param options The options to add to this select menu From 83712141201673e411321da133b3747df47ec47b Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 14 Dec 2021 19:12:25 -0500 Subject: [PATCH 11/21] test: add action row tests --- __tests__/components/actionRow.test.ts | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/__tests__/components/actionRow.test.ts b/__tests__/components/actionRow.test.ts index e69de29b..21b08be4 100644 --- a/__tests__/components/actionRow.test.ts +++ b/__tests__/components/actionRow.test.ts @@ -0,0 +1,45 @@ +import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; +import { ActionRow, LinkButtonComponent } from '../../src'; + +describe('Action Row Components', () => { + describe('Assertion Tests', () => { + test('GIVEN valid components THEN do not throw', () => { + expect(() => new ActionRow().addComponents(new LinkButtonComponent())).not.toThrowError(); + expect(() => new ActionRow().setComponents([new LinkButtonComponent()])).not.toThrowError(); + }); + + test('GIVEN valid JSON input THEN valid JSON output is given', () => { + const actionRowData: APIActionRowComponent = { + type: ComponentType.ActionRow, + components: [ + { + type: ComponentType.Button, + label: 'button', + style: ButtonStyle.Primary, + custom_id: 'test', + }, + { + type: ComponentType.Button, + label: 'link', + style: ButtonStyle.Link, + url: 'https://google.com', + }, + { + type: ComponentType.SelectMenu, + placeholder: 'test', + custom_id: 'test', + options: [ + { + label: 'option', + value: 'option', + }, + ], + }, + ], + }; + + expect(new ActionRow(actionRowData).toJSON()).toEqual(actionRowData); + expect(new ActionRow().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] }); + }); + }); +}); From 62f9ea2c9223c3544c9a6a114e89880bf48a1f4d Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 14 Dec 2021 19:21:42 -0500 Subject: [PATCH 12/21] refactor: change BaseComponent -> Component --- src/components/ActionRow.ts | 4 ++-- src/components/BaseComponent.ts | 2 +- src/components/button/BaseButton.ts | 4 ++-- src/components/selectMenu/SelectMenu.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index 6b3cedb5..bda5231b 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -1,6 +1,6 @@ import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; import { InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '..'; -import { BaseComponent } from './BaseComponent'; +import { Component } from './BaseComponent'; export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponent | SelectMenuComponent; @@ -9,7 +9,7 @@ export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponen /** * Represents an action row component */ -export class ActionRow extends BaseComponent { +export class ActionRow extends Component { public components: T[] = []; public constructor(data?: APIActionRowComponent) { diff --git a/src/components/BaseComponent.ts b/src/components/BaseComponent.ts index 68e1dcb8..82425311 100644 --- a/src/components/BaseComponent.ts +++ b/src/components/BaseComponent.ts @@ -1,6 +1,6 @@ import type { ComponentType } from 'discord-api-types'; -export abstract class BaseComponent { +export abstract class Component { public type: T; public constructor(type: T) { diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index ee162877..37798ea9 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -1,7 +1,7 @@ import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; import { z } from 'zod'; import { disabledValidator, emojiValidator } from '../Assertions'; -import { BaseComponent } from '../BaseComponent'; +import { Component } from '../BaseComponent'; export type BuilderButtonBaseData = Omit & { style: T }; @@ -13,7 +13,7 @@ export function validateButtonFields(button: BaseButtonComponent) { export const buttonLabelValidator = z.string().nonempty().max(80); -export abstract class BaseButtonComponent extends BaseComponent { +export abstract class BaseButtonComponent extends Component { public style!: T; public label?: string; public emoji?: APIMessageComponentEmoji; diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index a737924e..16ad8608 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,6 +1,6 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; import { customIdValidator, disabledValidator } from '../Assertions'; -import { BaseComponent } from '../BaseComponent'; +import { Component } from '../BaseComponent'; import { z } from 'zod'; import { SelectMenuOption } from './SelectMenuOption'; @@ -17,7 +17,7 @@ function validateRequiredParameters(customId: string, options: SelectMenuOption[ /** * Represents a select menu component */ -export class SelectMenuComponent extends BaseComponent { +export class SelectMenuComponent extends Component { public options!: SelectMenuOption[]; public placeholder?: string; public minValues?: number; From c798e65ad20709a55d6fa08121c2f8994c9a2203 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 14 Dec 2021 21:14:51 -0500 Subject: [PATCH 13/21] refactor: change filename --- src/components/ActionRow.ts | 2 +- src/components/{BaseComponent.ts => Component.ts} | 0 src/components/button/BaseButton.ts | 2 +- src/components/selectMenu/SelectMenu.ts | 2 +- src/index.ts | 1 + 5 files changed, 4 insertions(+), 3 deletions(-) rename src/components/{BaseComponent.ts => Component.ts} (100%) diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index bda5231b..cf63ec0f 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -1,6 +1,6 @@ import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; import { InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '..'; -import { Component } from './BaseComponent'; +import { Component } from './Component'; export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponent | SelectMenuComponent; diff --git a/src/components/BaseComponent.ts b/src/components/Component.ts similarity index 100% rename from src/components/BaseComponent.ts rename to src/components/Component.ts diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index 37798ea9..49de0c19 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -1,7 +1,7 @@ import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; import { z } from 'zod'; import { disabledValidator, emojiValidator } from '../Assertions'; -import { Component } from '../BaseComponent'; +import { Component } from '../Component'; export type BuilderButtonBaseData = Omit & { style: T }; diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index 16ad8608..9e74e0fc 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,6 +1,6 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; import { customIdValidator, disabledValidator } from '../Assertions'; -import { Component } from '../BaseComponent'; +import { Component } from '../Component'; import { z } from 'zod'; import { SelectMenuOption } from './SelectMenuOption'; diff --git a/src/index.ts b/src/index.ts index 02cb0323..3d20277e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export * from './messages/formatters'; export * as ComponentAssertions from './components/Assertions'; export * from './components/ActionRow'; export * from './components/button/BaseButton'; +export * from './components/Component'; export * from './components/button/InteractionButton'; export * from './components/button/LinkButton'; export * from './components/selectMenu/SelectMenu'; From 89ca2270f5375a53dafcc3d9dc4dbc6dfa3fd676 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 14 Dec 2021 22:04:18 -0500 Subject: [PATCH 14/21] feat: add component factory --- __tests__/components/actionRow.test.ts | 3 ++- src/components/ActionRow.ts | 17 ++++------------- src/components/Components.ts | 26 ++++++++++++++++++++++++++ src/index.ts | 1 + 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 src/components/Components.ts diff --git a/__tests__/components/actionRow.test.ts b/__tests__/components/actionRow.test.ts index 21b08be4..d1f17de8 100644 --- a/__tests__/components/actionRow.test.ts +++ b/__tests__/components/actionRow.test.ts @@ -1,5 +1,5 @@ import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; -import { ActionRow, LinkButtonComponent } from '../../src'; +import { ActionRow, createComponent, LinkButtonComponent } from '../../src'; describe('Action Row Components', () => { describe('Assertion Tests', () => { @@ -40,6 +40,7 @@ describe('Action Row Components', () => { expect(new ActionRow(actionRowData).toJSON()).toEqual(actionRowData); expect(new ActionRow().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] }); + expect(() => createComponent({ type: ComponentType.ActionRow, components: [] })).not.toThrowError(); }); }); }); diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index cf63ec0f..96fcb854 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -1,6 +1,7 @@ -import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; -import { InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '..'; +import { APIActionRowComponent, ComponentType } from 'discord-api-types'; +import type { LinkButtonComponent, InteractionButtonComponent, SelectMenuComponent } from '..'; import { Component } from './Component'; +import { createComponent } from './Components'; export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponent | SelectMenuComponent; @@ -19,17 +20,7 @@ export class ActionRow extends Component { - switch (component.type) { - case ComponentType.Button: - if (component.style === ButtonStyle.Link) { - return new LinkButtonComponent(component); - } - return new InteractionButtonComponent(component); - case ComponentType.SelectMenu: - return new SelectMenuComponent(component); - } - }) as T[]; + this.components = data.components.map(createComponent) as T[]; } /** diff --git a/src/components/Components.ts b/src/components/Components.ts new file mode 100644 index 00000000..1a3ba09c --- /dev/null +++ b/src/components/Components.ts @@ -0,0 +1,26 @@ +import { + APIActionRowComponent, + APIButtonComponent, + APISelectMenuComponent, + ButtonStyle, + ComponentType, +} from 'discord-api-types'; +import { ActionRow, Component, InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '../index'; + +/** + * Factory for creating components from api data + * @param data The api data to transform to a component class + */ +export function createComponent(data: APIButtonComponent | APISelectMenuComponent | APIActionRowComponent): Component { + switch (data.type) { + case ComponentType.ActionRow: + return new ActionRow(data); + case ComponentType.Button: + if (data.style === ButtonStyle.Link) { + return new LinkButtonComponent(data); + } + return new InteractionButtonComponent(data); + case ComponentType.SelectMenu: + return new SelectMenuComponent(data); + } +} diff --git a/src/index.ts b/src/index.ts index 3d20277e..b94d7abd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ export * as ComponentAssertions from './components/Assertions'; export * from './components/ActionRow'; export * from './components/button/BaseButton'; export * from './components/Component'; +export * from './components/Components'; export * from './components/button/InteractionButton'; export * from './components/button/LinkButton'; export * from './components/selectMenu/SelectMenu'; From 25012b5b2a8c6dd198030e8df7e1ee58bac7031f Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Mon, 27 Dec 2021 18:17:22 -0800 Subject: [PATCH 15/21] refactor: remove generics and improve serialization --- src/components/ActionRow.ts | 15 ++++--- src/components/Component.ts | 26 ++++++------ src/components/Components.ts | 25 +++++++---- src/components/button/BaseButton.ts | 41 ++++++++++--------- src/components/button/InteractionButton.ts | 12 +++--- src/components/button/LinkButton.ts | 10 +++-- src/components/selectMenu/SelectMenu.ts | 37 ++++++++--------- src/components/selectMenu/SelectMenuOption.ts | 20 ++++----- 8 files changed, 99 insertions(+), 87 deletions(-) diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index 96fcb854..533ac654 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -1,6 +1,6 @@ import { APIActionRowComponent, ComponentType } from 'discord-api-types'; import type { LinkButtonComponent, InteractionButtonComponent, SelectMenuComponent } from '..'; -import { Component } from './Component'; +import type { Component } from './Component'; import { createComponent } from './Components'; export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponent | SelectMenuComponent; @@ -10,12 +10,11 @@ export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponen /** * Represents an action row component */ -export class ActionRow extends Component { - public components: T[] = []; +export class ActionRow implements Component { + public readonly components: T[] = []; + public readonly type = ComponentType.ActionRow; public constructor(data?: APIActionRowComponent) { - super(ComponentType.ActionRow); - if (!data) { return; } @@ -38,13 +37,13 @@ export class ActionRow extends Component component.toJSON()), }; } diff --git a/src/components/Component.ts b/src/components/Component.ts index 82425311..b878a5c4 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -1,15 +1,15 @@ -import type { ComponentType } from 'discord-api-types'; +import type { APIMessageComponent, ComponentType } from 'discord-api-types'; -export abstract class Component { - public type: T; - - public constructor(type: T) { - this.type = type; - } - - public toJSON() { - return { - type: this.type, - }; - } +/** + * Represents a discord component + */ +export interface Component { + /** + * The type of this component + */ + readonly type: ComponentType; + /** + * Converts this component to an API-compatible JSON object + */ + toJSON(): APIMessageComponent; } diff --git a/src/components/Components.ts b/src/components/Components.ts index 1a3ba09c..9046e62d 100644 --- a/src/components/Components.ts +++ b/src/components/Components.ts @@ -1,17 +1,21 @@ -import { - APIActionRowComponent, - APIButtonComponent, - APISelectMenuComponent, - ButtonStyle, - ComponentType, -} from 'discord-api-types'; +import { APIMessageComponent, ButtonStyle, ComponentType } from 'discord-api-types'; import { ActionRow, Component, InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '../index'; +import type { ActionRowComponent } from './ActionRow'; + +export interface MappedComponentTypes { + [ComponentType.ActionRow]: ActionRow; + [ComponentType.Button]: LinkButtonComponent | InteractionButtonComponent; + [ComponentType.SelectMenu]: SelectMenuComponent; +} /** - * Factory for creating components from api data + * Factory for creating components from API data * @param data The api data to transform to a component class */ -export function createComponent(data: APIButtonComponent | APISelectMenuComponent | APIActionRowComponent): Component { +export function createComponent( + data: APIMessageComponent & { type: T }, +): MappedComponentTypes[T]; +export function createComponent(data: APIMessageComponent): Component { switch (data.type) { case ComponentType.ActionRow: return new ActionRow(data); @@ -22,5 +26,8 @@ export function createComponent(data: APIButtonComponent | APISelectMenuComponen return new InteractionButtonComponent(data); case ComponentType.SelectMenu: return new SelectMenuComponent(data); + default: + // @ts-expect-error + throw new Error(`Cannot serialize component type: ${data.type as number}`); } } diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index 49de0c19..748b63dd 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -1,11 +1,15 @@ -import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types'; +import { + APIButtonComponent, + APIMessageComponent, + APIMessageComponentEmoji, + ButtonStyle, + ComponentType, +} from 'discord-api-types'; import { z } from 'zod'; import { disabledValidator, emojiValidator } from '../Assertions'; -import { Component } from '../Component'; +import type { Component } from '../Component'; -export type BuilderButtonBaseData = Omit & { style: T }; - -export function validateButtonFields(button: BaseButtonComponent) { +export function validateButtonFields(button: BaseButtonComponent) { if (button.emoji && button.label) { throw new TypeError('Cannot construct a button with both a label and an emoji field present.'); } @@ -13,15 +17,14 @@ export function validateButtonFields(button: BaseButtonComponent) { export const buttonLabelValidator = z.string().nonempty().max(80); -export abstract class BaseButtonComponent extends Component { - public style!: T; - public label?: string; - public emoji?: APIMessageComponentEmoji; - public disabled?: boolean; +export abstract class BaseButtonComponent implements Component { + public readonly type: ComponentType.Button = ComponentType.Button; + public abstract readonly style: ButtonStyle; + public readonly label?: string; + public readonly emoji?: APIMessageComponentEmoji; + public readonly disabled?: boolean; public constructor(data?: APIButtonComponent) { - super(ComponentType.Button); - if (!data) { return; } @@ -37,7 +40,7 @@ export abstract class BaseButtonComponent extends Compone */ public setEmoji(emoji: APIMessageComponentEmoji): Omit { emojiValidator.parse(emoji); - this.emoji = emoji; + Reflect.set(this, 'emoji', emoji); return this; } @@ -47,7 +50,7 @@ export abstract class BaseButtonComponent extends Compone */ public setDisabled(disabled: boolean) { disabledValidator.parse(disabled); - this.disabled = disabled; + Reflect.set(this, 'disabled', disabled); return this; } @@ -57,18 +60,18 @@ export abstract class BaseButtonComponent extends Compone */ public setLabel(label: string): Omit { buttonLabelValidator.parse(label); - this.label = label; + Reflect.set(this, 'label', label); return this; } - public override toJSON(): BuilderButtonBaseData { - validateButtonFields(this); + public toPartialJSON() { return { - ...super.toJSON(), - style: this.style, + type: this.type, label: this.label, emoji: this.emoji, disabled: this.disabled, }; } + + public abstract toJSON(): APIMessageComponent; } diff --git a/src/components/button/InteractionButton.ts b/src/components/button/InteractionButton.ts index b5654340..67109e94 100644 --- a/src/components/button/InteractionButton.ts +++ b/src/components/button/InteractionButton.ts @@ -8,8 +8,9 @@ export const styleValidator = z.number().min(ButtonStyle.Primary).max(ButtonStyl /** * Represents the a button that can send interactions whenever clicked. */ -export class InteractionButtonComponent extends BaseButtonComponent> { - public customId!: string; +export class InteractionButtonComponent extends BaseButtonComponent { + public readonly customId!: string; + public readonly style!: APIButtonComponentWithCustomId['style']; public constructor(data?: APIButtonComponentWithCustomId) { super(data); @@ -27,7 +28,7 @@ export class InteractionButtonComponent extends BaseButtonComponent) { - this.style = style; + Reflect.set(this, 'style', style); return this; } @@ -37,7 +38,7 @@ export class InteractionButtonComponent extends BaseButtonComponent { - public url!: string; +export class LinkButtonComponent extends BaseButtonComponent { + public readonly url!: string; + public readonly style: ButtonStyle.Link = ButtonStyle.Link; public constructor(data?: APIButtonComponentWithURL) { super(data); @@ -27,7 +28,7 @@ export class LinkButtonComponent extends BaseButtonComponent { */ public setURL(url: string) { urlValidator.parse(url); - this.url = url; + Reflect.set(this, 'url', url); return this; } @@ -35,7 +36,8 @@ export class LinkButtonComponent extends BaseButtonComponent { // url is required. urlValidator.parse(this.url); return { - ...super.toJSON(), + ...super.toPartialJSON(), + style: this.style, url: this.url, }; } diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index 9e74e0fc..3ee67d87 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,6 +1,6 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; import { customIdValidator, disabledValidator } from '../Assertions'; -import { Component } from '../Component'; +import type { Component } from '../Component'; import { z } from 'zod'; import { SelectMenuOption } from './SelectMenuOption'; @@ -17,23 +17,22 @@ function validateRequiredParameters(customId: string, options: SelectMenuOption[ /** * Represents a select menu component */ -export class SelectMenuComponent extends Component { - public options!: SelectMenuOption[]; - public placeholder?: string; - public minValues?: number; - public maxValues?: number; - public customId!: string; - public disabled?: boolean; +export class SelectMenuComponent implements Component { + public readonly type = ComponentType.SelectMenu; + public readonly options!: SelectMenuOption[]; + public readonly placeholder?: string; + public readonly minValues?: number; + public readonly maxValues?: number; + public readonly customId!: string; + public readonly disabled?: boolean; public constructor(data?: APISelectMenuComponent) { - super(ComponentType.SelectMenu); - if (!data) { this.options = []; return; } - this.options = data.options!.map((option) => new SelectMenuOption(option)); + this.options = data.options.map((option) => new SelectMenuOption(option)); this.placeholder = data.placeholder; this.minValues = data.min_values; this.maxValues = data.max_values; @@ -47,7 +46,7 @@ export class SelectMenuComponent extends Component { */ public setPlaceholder(placeholder: string) { placeholderValidator.parse(placeholder); - this.placeholder = placeholder; + Reflect.set(this, 'placeholder', placeholder); return this; } @@ -57,7 +56,7 @@ export class SelectMenuComponent extends Component { */ public setMinValues(minValues: number) { minMaxValidator.parse(minValues); - this.minValues = minValues; + Reflect.set(this, 'minValues', minValues); return this; } @@ -67,7 +66,7 @@ export class SelectMenuComponent extends Component { */ public setMaxValues(maxValues: number) { minMaxValidator.parse(maxValues); - this.maxValues = maxValues; + Reflect.set(this, 'maxValues', maxValues); return this; } @@ -77,7 +76,7 @@ export class SelectMenuComponent extends Component { */ public setCustomId(customId: string) { customIdValidator.parse(customId); - this.customId = customId; + Reflect.set(this, 'customId', customId); return this; } @@ -87,7 +86,7 @@ export class SelectMenuComponent extends Component { */ public setDisabled(disabled: boolean) { disabledValidator.parse(disabled); - this.disabled = disabled; + Reflect.set(this, 'disabled', disabled); return this; } @@ -106,14 +105,14 @@ export class SelectMenuComponent extends Component { * @param options The options to set on this select menu */ public setOptions(options: SelectMenuOption[]) { - this.options = options; + Reflect.set(this, 'options', options); return this; } - public override toJSON(): APISelectMenuComponent { + public toJSON(): APISelectMenuComponent { validateRequiredParameters(this.customId, this.options); return { - ...super.toJSON(), + type: this.type, custom_id: this.customId, options: this.options.map((option) => option.toJSON()), placeholder: this.placeholder, diff --git a/src/components/selectMenu/SelectMenuOption.ts b/src/components/selectMenu/SelectMenuOption.ts index b8872723..e243afba 100644 --- a/src/components/selectMenu/SelectMenuOption.ts +++ b/src/components/selectMenu/SelectMenuOption.ts @@ -14,11 +14,11 @@ export function validateRequiredParameters(label: string, value: string) { * Represents an option within a select menu component */ export class SelectMenuOption { - public label!: string; - public value!: string; - public description?: string; - public emoji?: APIMessageComponentEmoji; - public default?: boolean; + public readonly label!: string; + public readonly value!: string; + public readonly description?: string; + public readonly emoji?: APIMessageComponentEmoji; + public readonly default?: boolean; public constructor(data?: APISelectMenuOption) { if (!data) { @@ -37,7 +37,7 @@ export class SelectMenuOption { * @param label The label to show on this option */ public setLabel(label: string) { - this.label = label; + Reflect.set(this, 'label', label); return this; } @@ -46,7 +46,7 @@ export class SelectMenuOption { * @param value The value of this option */ public setValue(value: string) { - this.value = value; + Reflect.set(this, 'value', value); return this; } @@ -56,7 +56,7 @@ export class SelectMenuOption { */ public setDescription(description: string) { labelValueValidator.parse(description); - this.description = description; + Reflect.set(this, 'description', description); return this; } @@ -66,7 +66,7 @@ export class SelectMenuOption { */ public setDefault(isDefault: boolean) { defaultValidator.parse(isDefault); - this.default = isDefault; + Reflect.set(this, 'default', isDefault); return this; } @@ -76,7 +76,7 @@ export class SelectMenuOption { */ public setEmoji(emoji: APIMessageComponentEmoji) { emojiValidator.parse(emoji); - this.emoji = emoji; + Reflect.set(this, 'emoji', emoji); return this; } From 43750069708d1820d26912aaea498bf3de37356e Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 28 Dec 2021 10:59:49 -0700 Subject: [PATCH 16/21] fix: not validating properly --- src/components/button/BaseButton.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index 748b63dd..ec1fa171 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -65,6 +65,7 @@ export abstract class BaseButtonComponent implements Component { } public toPartialJSON() { + validateButtonFields(this); return { type: this.type, label: this.label, From 50c0ad861065c5f9d6ede00b550c0e0388b05fb6 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 28 Dec 2021 11:04:11 -0700 Subject: [PATCH 17/21] chore: update dapi-types --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f9f66a8..74bb2464 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@sindresorhus/is": "^4.2.0", - "discord-api-types": "^0.25.2", + "discord-api-types": "^0.26.0", "ts-mixer": "^6.0.0", "tslib": "^2.3.1", "zod": "^3.11.6" @@ -4949,9 +4949,9 @@ } }, "node_modules/discord-api-types": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.25.2.tgz", - "integrity": "sha512-O243LXxb5gLLxubu5zgoppYQuolapGVWPw3ll0acN0+O8TnPUE2kFp9Bt3sTRYodw8xFIknOVxjSeyWYBpVcEQ==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.26.0.tgz", + "integrity": "sha512-bnUltSHpQLzTVZTMjm+iNgVhAbtm5oAKHrhtiPaZoxprbm1UtuCZCsG0yXM61NamWfeSz7xnLvgFc50YzVJ5cQ==", "engines": { "node": ">=12" } @@ -15384,9 +15384,9 @@ } }, "discord-api-types": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.25.2.tgz", - "integrity": "sha512-O243LXxb5gLLxubu5zgoppYQuolapGVWPw3ll0acN0+O8TnPUE2kFp9Bt3sTRYodw8xFIknOVxjSeyWYBpVcEQ==" + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.26.0.tgz", + "integrity": "sha512-bnUltSHpQLzTVZTMjm+iNgVhAbtm5oAKHrhtiPaZoxprbm1UtuCZCsG0yXM61NamWfeSz7xnLvgFc50YzVJ5cQ==" }, "doctrine": { "version": "3.0.0", diff --git a/package.json b/package.json index b82cafa7..7aefb73c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "homepage": "https://github.com/discordjs/builders", "dependencies": { "@sindresorhus/is": "^4.2.0", - "discord-api-types": "^0.25.2", + "discord-api-types": "^0.26.0", "ts-mixer": "^6.0.0", "tslib": "^2.3.1", "zod": "^3.11.6" From 9575593a4dde5a7a63eece87422ac1e8f457545d Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 28 Dec 2021 11:14:18 -0700 Subject: [PATCH 18/21] fix: version dapi-types --- __tests__/components/actionRow.test.ts | 2 +- __tests__/components/button.test.ts | 2 +- __tests__/components/selectMenu.test.ts | 2 +- package-lock.json | 1088 +++++++++++------ src/components/ActionRow.ts | 2 +- src/components/Component.ts | 2 +- src/components/Components.ts | 2 +- src/components/button/BaseButton.ts | 2 +- src/components/button/InteractionButton.ts | 2 +- src/components/button/LinkButton.ts | 2 +- src/components/selectMenu/SelectMenu.ts | 2 +- src/components/selectMenu/SelectMenuOption.ts | 2 +- 12 files changed, 746 insertions(+), 364 deletions(-) diff --git a/__tests__/components/actionRow.test.ts b/__tests__/components/actionRow.test.ts index d1f17de8..175fdfd2 100644 --- a/__tests__/components/actionRow.test.ts +++ b/__tests__/components/actionRow.test.ts @@ -1,4 +1,4 @@ -import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types'; +import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types/v9'; import { ActionRow, createComponent, LinkButtonComponent } from '../../src'; describe('Action Row Components', () => { diff --git a/__tests__/components/button.test.ts b/__tests__/components/button.test.ts index 941fa823..df6034e3 100644 --- a/__tests__/components/button.test.ts +++ b/__tests__/components/button.test.ts @@ -3,7 +3,7 @@ import { APIButtonComponentWithURL, ButtonStyle, ComponentType, -} from 'discord-api-types'; +} from 'discord-api-types/v9'; import { buttonLabelValidator, InteractionButtonComponent, LinkButtonComponent, styleValidator } from '../../src/index'; const interactionButtonComponent = () => new InteractionButtonComponent(); diff --git a/__tests__/components/selectMenu.test.ts b/__tests__/components/selectMenu.test.ts index a5abd302..0e00f6bc 100644 --- a/__tests__/components/selectMenu.test.ts +++ b/__tests__/components/selectMenu.test.ts @@ -1,4 +1,4 @@ -import { APISelectMenuComponent, APISelectMenuOption, ComponentType } from 'discord-api-types'; +import { APISelectMenuComponent, APISelectMenuOption, ComponentType } from 'discord-api-types/v9'; import { SelectMenuComponent, SelectMenuOption } from '../../src/index'; const selectMenu = () => new SelectMenuComponent(); diff --git a/package-lock.json b/package-lock.json index 74bb2464..4454de12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -990,12 +990,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz", - "integrity": "sha512-Xv6mEXqVdaqCBfJFyeab0fH2DnUoMsDmhamxsSi4j8nLd4Vtw213WMJr55xxqipC/YVWyPY3K0blJncPYji+dQ==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz", + "integrity": "sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.16.5" }, "engines": { "node": ">=6.9.0" @@ -3124,9 +3124,9 @@ } }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, "node_modules/@types/istanbul-lib-report": { @@ -3170,9 +3170,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", + "version": "16.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.17.tgz", + "integrity": "sha512-C1vTZME8cFo8uxY2ui41xcynEotVkczIVI5AjLmy5pkpBv/FtG+jhtOlfcPysI8VRVwoOMv6NJm44LGnoMSWkw==", "dev": true }, "node_modules/@types/normalize-package-data": { @@ -3215,13 +3215,13 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.0.tgz", - "integrity": "sha512-spu1UW7QuBn0nJ6+psnfCc3iVoQAifjKORgBngKOmC8U/1tbe2YJMzYQqDGYB4JCss7L8+RM2kKLb1B1Aw9BNA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", + "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.8.0", - "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/experimental-utils": "5.8.1", + "@typescript-eslint/scope-manager": "5.8.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -3262,15 +3262,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.0.tgz", - "integrity": "sha512-KN5FvNH71bhZ8fKtL+lhW7bjm7cxs1nt+hrDZWIqb6ViCffQcWyLunGrgvISgkRojIDcXIsH+xlFfI4RCDA0xA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", + "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -3286,14 +3286,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.0.tgz", - "integrity": "sha512-Gleacp/ZhRtJRYs5/T8KQR3pAQjQI89Dn/k+OzyCKOsLiZH2/Vh60cFBTnFsHNI6WAD+lNUo/xGZ4NeA5u0Ipw==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", + "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "debug": "^4.3.2" }, "engines": { @@ -3313,13 +3313,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.0.tgz", - "integrity": "sha512-x82CYJsLOjPCDuFFEbS6e7K1QEWj7u5Wk1alw8A+gnJiYwNnDJk0ib6PCegbaPMjrfBvFKa7SxE3EOnnIQz2Gg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", + "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0" + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3330,9 +3330,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.0.tgz", - "integrity": "sha512-LdCYOqeqZWqCMOmwFnum6YfW9F3nKuxJiR84CdIRN5nfHJ7gyvGpXWqL/AaW0k3Po0+wm93ARAsOdzlZDPCcXg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", + "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3343,13 +3343,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.0.tgz", - "integrity": "sha512-srfeZ3URdEcUsSLbkOFqS7WoxOqn8JNil2NSLO9O+I2/Uyc85+UlfpEvQHIpj5dVts7KKOZnftoJD/Fdv0L7nQ==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", + "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -3385,12 +3385,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.0.tgz", - "integrity": "sha512-+HDIGOEMnqbxdAHegxvnOqESUH6RWFRR2b8qxP1W9CZnnYh4Usz6MBL+2KMAgPk/P0o9c1HqnYtwzVH6GTIqug==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", + "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/types": "5.8.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -3408,9 +3408,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4004,9 +4004,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001292", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", - "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==", + "version": "1.0.30001293", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001293.tgz", + "integrity": "sha512-A4I5fB8Kxo0p/H3aXlaOkBp9mD0GspKmXfWHXOzcl0iHLi07EEVJdJeQPT4Yv3/dffRnrwluYZyXvMjbr6WhIA==", "dev": true, "funding": { "type": "opencollective", @@ -4070,9 +4070,9 @@ } }, "node_modules/ci-info": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", - "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", "dev": true }, "node_modules/cjs-module-lexer": { @@ -4118,77 +4118,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.0.1.tgz", - "integrity": "sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==", + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "emoji-regex": "^9.2.2", - "is-fullwidth-code-point": "^4.0.0", - "strip-ansi": "^7.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "node": ">=8" } }, "node_modules/co": { @@ -5094,9 +5061,9 @@ } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "node_modules/enquirer": { @@ -6227,6 +6194,21 @@ "node": ">=6.9.0" } }, + "node_modules/get-pkg-repo/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/get-pkg-repo/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/get-pkg-repo/node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -6251,6 +6233,20 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/get-pkg-repo/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/get-pkg-repo/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -6593,9 +6589,9 @@ } }, "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -6758,12 +6754,15 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-generator-fn": { @@ -7179,6 +7178,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/jest-cli/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7188,6 +7193,29 @@ "node": ">=8" } }, + "node_modules/jest-cli/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-cli/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8202,6 +8230,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-runtime/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/jest-runtime/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8211,6 +8245,29 @@ "node": ">=8" } }, + "node_modules/jest-runtime/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-runtime/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8687,9 +8744,9 @@ } }, "node_modules/joycon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz", - "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "dev": true, "engines": { "node": ">=10" @@ -8909,9 +8966,9 @@ } }, "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "node_modules/lint-staged": { @@ -9032,6 +9089,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/listr2/node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -9046,6 +9118,20 @@ "node": ">=8" } }, + "node_modules/listr2/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -9188,6 +9274,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -9205,6 +9306,20 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/log-update/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -9471,15 +9586,6 @@ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", "dev": true }, - "node_modules/node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", @@ -9798,13 +9904,10 @@ } }, "node_modules/pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", + "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", "dev": true, - "dependencies": { - "node-modules-regexp": "^1.0.0" - }, "engines": { "node": ">= 6" } @@ -10413,9 +10516,9 @@ } }, "node_modules/rollup": { - "version": "2.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz", - "integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==", + "version": "2.62.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.62.0.tgz", + "integrity": "sha512-cJEQq2gwB0GWMD3rYImefQTSjrPYaC6s4J9pYqnstVLJ1CHa/aZNVkD4Epuvg4iLeMA4KRiq7UM7awKK6j7jcw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -10451,20 +10554,14 @@ } }, "node_modules/rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.1.tgz", + "integrity": "sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==", "dev": true, "dependencies": { - "tslib": "~2.1.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -10531,9 +10628,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, "node_modules/sisteransi": { @@ -10579,22 +10676,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true, "engines": { "node": ">=0.10.0" @@ -10728,6 +10813,21 @@ "node": ">=10" } }, + "node_modules/standard-version/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/standard-version/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/standard-version/node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -10743,6 +10843,20 @@ "node": ">=10" } }, + "node_modules/standard-version/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/standard-version/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -10813,17 +10927,47 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.0.1.tgz", + "integrity": "sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "emoji-regex": "^9.2.2", + "is-fullwidth-code-point": "^4.0.0", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/stringify-package": { @@ -11198,9 +11342,9 @@ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "node_modules/tsup": { - "version": "5.11.8", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-5.11.8.tgz", - "integrity": "sha512-MB348Kkip7NlHkISq/1YxNauj6qc8yiOL5mslUifLZtjBKEbSasaGdQRTRcHeM+cZ31sA+59XAnNyEIAjZajCQ==", + "version": "5.11.9", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-5.11.9.tgz", + "integrity": "sha512-APFcd9qKblMVO35O5OyIcfa4lwBrVNinwtifUojO78I6j0aDhS4fQ06gq6vF1b/+Z83pYDWNb0R2fmO9PX3IVQ==", "dev": true, "dependencies": { "bundle-require": "^2.1.8", @@ -11223,7 +11367,7 @@ "tsup-node": "dist/cli-node.js" }, "peerDependencies": { - "typescript": "^4.5.4" + "typescript": "^4.1.0" }, "peerDependenciesMeta": { "typescript": { @@ -11345,9 +11489,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", + "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", "dev": true, "optional": true, "bin": { @@ -11620,6 +11764,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -11705,18 +11878,18 @@ } }, "node_modules/yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" }, "engines": { "node": ">=12" @@ -11731,6 +11904,44 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -12425,12 +12636,12 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz", - "integrity": "sha512-Xv6mEXqVdaqCBfJFyeab0fH2DnUoMsDmhamxsSi4j8nLd4Vtw213WMJr55xxqipC/YVWyPY3K0blJncPYji+dQ==", + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz", + "integrity": "sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.16.5" } }, "@babel/plugin-transform-arrow-functions": { @@ -13990,9 +14201,9 @@ } }, "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, "@types/istanbul-lib-report": { @@ -14036,9 +14247,9 @@ "dev": true }, "@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", + "version": "16.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.17.tgz", + "integrity": "sha512-C1vTZME8cFo8uxY2ui41xcynEotVkczIVI5AjLmy5pkpBv/FtG+jhtOlfcPysI8VRVwoOMv6NJm44LGnoMSWkw==", "dev": true }, "@types/normalize-package-data": { @@ -14081,13 +14292,13 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.0.tgz", - "integrity": "sha512-spu1UW7QuBn0nJ6+psnfCc3iVoQAifjKORgBngKOmC8U/1tbe2YJMzYQqDGYB4JCss7L8+RM2kKLb1B1Aw9BNA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", + "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.8.0", - "@typescript-eslint/scope-manager": "5.8.0", + "@typescript-eslint/experimental-utils": "5.8.1", + "@typescript-eslint/scope-manager": "5.8.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -14108,55 +14319,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.0.tgz", - "integrity": "sha512-KN5FvNH71bhZ8fKtL+lhW7bjm7cxs1nt+hrDZWIqb6ViCffQcWyLunGrgvISgkRojIDcXIsH+xlFfI4RCDA0xA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", + "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.0.tgz", - "integrity": "sha512-Gleacp/ZhRtJRYs5/T8KQR3pAQjQI89Dn/k+OzyCKOsLiZH2/Vh60cFBTnFsHNI6WAD+lNUo/xGZ4NeA5u0Ipw==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", + "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.8.0", - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/typescript-estree": "5.8.0", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.0.tgz", - "integrity": "sha512-x82CYJsLOjPCDuFFEbS6e7K1QEWj7u5Wk1alw8A+gnJiYwNnDJk0ib6PCegbaPMjrfBvFKa7SxE3EOnnIQz2Gg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", + "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0" + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1" } }, "@typescript-eslint/types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.0.tgz", - "integrity": "sha512-LdCYOqeqZWqCMOmwFnum6YfW9F3nKuxJiR84CdIRN5nfHJ7gyvGpXWqL/AaW0k3Po0+wm93ARAsOdzlZDPCcXg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", + "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.0.tgz", - "integrity": "sha512-srfeZ3URdEcUsSLbkOFqS7WoxOqn8JNil2NSLO9O+I2/Uyc85+UlfpEvQHIpj5dVts7KKOZnftoJD/Fdv0L7nQ==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", + "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.0", - "@typescript-eslint/visitor-keys": "5.8.0", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -14176,12 +14387,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.0.tgz", - "integrity": "sha512-+HDIGOEMnqbxdAHegxvnOqESUH6RWFRR2b8qxP1W9CZnnYh4Usz6MBL+2KMAgPk/P0o9c1HqnYtwzVH6GTIqug==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", + "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.8.0", + "@typescript-eslint/types": "5.8.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -14192,9 +14403,9 @@ "dev": true }, "acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-globals": { @@ -14648,9 +14859,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001292", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", - "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==", + "version": "1.0.30001293", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001293.tgz", + "integrity": "sha512-A4I5fB8Kxo0p/H3aXlaOkBp9mD0GspKmXfWHXOzcl0iHLi07EEVJdJeQPT4Yv3/dffRnrwluYZyXvMjbr6WhIA==", "dev": true }, "chalk": { @@ -14698,9 +14909,9 @@ } }, "ci-info": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", - "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", "dev": true }, "cjs-module-lexer": { @@ -14732,59 +14943,44 @@ "requires": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" }, "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.0.1.tgz", - "integrity": "sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "emoji-regex": "^9.2.2", - "is-fullwidth-code-point": "^4.0.0", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } } } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -15491,9 +15687,9 @@ "dev": true }, "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "enquirer": { @@ -16281,6 +16477,18 @@ "yargs": "^16.2.0" }, "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -16305,6 +16513,17 @@ "safe-buffer": "~5.1.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -16553,9 +16772,9 @@ } }, "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-cwd": { @@ -16678,9 +16897,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true }, "is-generator-fn": { @@ -16984,12 +17203,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -17758,12 +18000,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -18123,9 +18388,9 @@ } }, "joycon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.0.1.tgz", - "integrity": "sha512-SJcJNBg32dGgxhPtM0wQqxqV0ax9k/9TaUskGDSJkSFSQOEWWvQ3zzWdGQRIUry2j1zA5+ReH13t0Mf3StuVZA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "dev": true }, "js-tokens": { @@ -18290,9 +18555,9 @@ "dev": true }, "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "lint-staged": { @@ -18374,6 +18639,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -18384,6 +18661,17 @@ "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } } } }, @@ -18498,6 +18786,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -18509,6 +18809,17 @@ "is-fullwidth-code-point": "^3.0.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -18716,12 +19027,6 @@ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", "dev": true }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, "node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", @@ -18949,13 +19254,10 @@ "dev": true }, "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", + "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", + "dev": true }, "pkg-dir": { "version": "4.2.0", @@ -19396,9 +19698,9 @@ } }, "rollup": { - "version": "2.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz", - "integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==", + "version": "2.62.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.62.0.tgz", + "integrity": "sha512-cJEQq2gwB0GWMD3rYImefQTSjrPYaC6s4J9pYqnstVLJ1CHa/aZNVkD4Epuvg4iLeMA4KRiq7UM7awKK6j7jcw==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -19414,20 +19716,12 @@ } }, "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.1.tgz", + "integrity": "sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ==", "dev": true, "requires": { - "tslib": "~2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - } + "tslib": "^2.1.0" } }, "safe-buffer": { @@ -19484,9 +19778,9 @@ } }, "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, "sisteransi": { @@ -19516,12 +19810,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", "dev": true - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true } } }, @@ -19645,6 +19933,18 @@ "yargs": "^16.0.0" }, "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -19654,6 +19954,17 @@ "lru-cache": "^6.0.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -19705,14 +20016,31 @@ } }, "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.0.1.tgz", + "integrity": "sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "emoji-regex": "^9.2.2", + "is-fullwidth-code-point": "^4.0.0", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } } }, "stringify-package": { @@ -19999,9 +20327,9 @@ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "tsup": { - "version": "5.11.8", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-5.11.8.tgz", - "integrity": "sha512-MB348Kkip7NlHkISq/1YxNauj6qc8yiOL5mslUifLZtjBKEbSasaGdQRTRcHeM+cZ31sA+59XAnNyEIAjZajCQ==", + "version": "5.11.9", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-5.11.9.tgz", + "integrity": "sha512-APFcd9qKblMVO35O5OyIcfa4lwBrVNinwtifUojO78I6j0aDhS4fQ06gq6vF1b/+Z83pYDWNb0R2fmO9PX3IVQ==", "dev": true, "requires": { "bundle-require": "^2.1.8", @@ -20101,9 +20429,9 @@ "dev": true }, "uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", + "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", "dev": true, "optional": true }, @@ -20317,6 +20645,29 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } } } }, @@ -20381,18 +20732,49 @@ "dev": true }, "yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true + } } }, "yargs-parser": { diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index 533ac654..0e0e8f01 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -1,4 +1,4 @@ -import { APIActionRowComponent, ComponentType } from 'discord-api-types'; +import { APIActionRowComponent, ComponentType } from 'discord-api-types/v9'; import type { LinkButtonComponent, InteractionButtonComponent, SelectMenuComponent } from '..'; import type { Component } from './Component'; import { createComponent } from './Components'; diff --git a/src/components/Component.ts b/src/components/Component.ts index b878a5c4..af85aed3 100644 --- a/src/components/Component.ts +++ b/src/components/Component.ts @@ -1,4 +1,4 @@ -import type { APIMessageComponent, ComponentType } from 'discord-api-types'; +import type { APIMessageComponent, ComponentType } from 'discord-api-types/v9'; /** * Represents a discord component diff --git a/src/components/Components.ts b/src/components/Components.ts index 9046e62d..bfee567b 100644 --- a/src/components/Components.ts +++ b/src/components/Components.ts @@ -1,4 +1,4 @@ -import { APIMessageComponent, ButtonStyle, ComponentType } from 'discord-api-types'; +import { APIMessageComponent, ButtonStyle, ComponentType } from 'discord-api-types/v9'; import { ActionRow, Component, InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '../index'; import type { ActionRowComponent } from './ActionRow'; diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index ec1fa171..ce634262 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -4,7 +4,7 @@ import { APIMessageComponentEmoji, ButtonStyle, ComponentType, -} from 'discord-api-types'; +} from 'discord-api-types/v9'; import { z } from 'zod'; import { disabledValidator, emojiValidator } from '../Assertions'; import type { Component } from '../Component'; diff --git a/src/components/button/InteractionButton.ts b/src/components/button/InteractionButton.ts index 67109e94..7b578a15 100644 --- a/src/components/button/InteractionButton.ts +++ b/src/components/button/InteractionButton.ts @@ -1,4 +1,4 @@ -import { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types'; +import { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types/v9'; import { z } from 'zod'; import { customIdValidator } from '../Assertions'; import { BaseButtonComponent } from './BaseButton'; diff --git a/src/components/button/LinkButton.ts b/src/components/button/LinkButton.ts index 26f2b1b5..2bd371f0 100644 --- a/src/components/button/LinkButton.ts +++ b/src/components/button/LinkButton.ts @@ -1,4 +1,4 @@ -import { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types'; +import { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types/v9'; import { BaseButtonComponent } from './BaseButton'; import { z } from 'zod'; diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index 3ee67d87..f0cd81d1 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,4 +1,4 @@ -import { APISelectMenuComponent, ComponentType } from 'discord-api-types'; +import { APISelectMenuComponent, ComponentType } from 'discord-api-types/v9'; import { customIdValidator, disabledValidator } from '../Assertions'; import type { Component } from '../Component'; import { z } from 'zod'; diff --git a/src/components/selectMenu/SelectMenuOption.ts b/src/components/selectMenu/SelectMenuOption.ts index e243afba..7447841c 100644 --- a/src/components/selectMenu/SelectMenuOption.ts +++ b/src/components/selectMenu/SelectMenuOption.ts @@ -1,4 +1,4 @@ -import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types'; +import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v9'; import { z } from 'zod'; import { emojiValidator } from '../Assertions'; From fde9d7afc896fd5285bcdd1dac45426a0c18d0e6 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 28 Dec 2021 11:39:07 -0700 Subject: [PATCH 19/21] types: make toPartialJSON protected --- src/components/button/BaseButton.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index ce634262..c930f1d3 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -64,7 +64,7 @@ export abstract class BaseButtonComponent implements Component { return this; } - public toPartialJSON() { + protected toPartialJSON() { validateButtonFields(this); return { type: this.type, From 13f016eaeacde16265adf53c452d6d41b1d6e5df Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 28 Dec 2021 17:28:35 -0700 Subject: [PATCH 20/21] chore: make requested changes --- src/components/ActionRow.ts | 10 ++-- src/components/Assertions.ts | 33 +++++++++++++ src/components/button/BaseButton.ts | 37 +++------------ src/components/button/InteractionButton.ts | 32 ++++++------- src/components/button/LinkButton.ts | 21 +++------ src/components/selectMenu/SelectMenu.ts | 46 ++++++++----------- src/components/selectMenu/SelectMenuOption.ts | 18 +++----- 7 files changed, 88 insertions(+), 109 deletions(-) diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index 0e0e8f01..d5d6cf38 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -15,11 +15,7 @@ export class ActionRow implements Component { public readonly type = ComponentType.ActionRow; public constructor(data?: APIActionRowComponent) { - if (!data) { - return; - } - - this.components = data.components.map(createComponent) as T[]; + this.components = (data?.components.map(createComponent) ?? []) as T[]; } /** @@ -37,13 +33,13 @@ export class ActionRow implements Component { * @param components The components to set this row to */ public setComponents(components: T[]) { - Reflect.set(this, 'components', components); + Reflect.set(this, 'components', [...components]); return this; } public toJSON(): APIActionRowComponent { return { - type: this.type, + ...this, components: this.components.map((component) => component.toJSON()), }; } diff --git a/src/components/Assertions.ts b/src/components/Assertions.ts index 79868fef..a2082681 100644 --- a/src/components/Assertions.ts +++ b/src/components/Assertions.ts @@ -1,4 +1,7 @@ +import { ButtonStyle } from 'discord-api-types'; import { z } from 'zod'; +import type { BaseButtonComponent } from './button/BaseButton'; +import type { SelectMenuOption } from './selectMenu/SelectMenuOption'; export const customIdValidator = z.string().min(1).max(100); @@ -12,3 +15,33 @@ export const emojiValidator = z .strict(); export const disabledValidator = z.boolean(); + +export function validateButtonFields(button: BaseButtonComponent) { + if (button.emoji && button.label) { + throw new TypeError('Cannot construct a button with both a label and an emoji field present.'); + } +} + +export const buttonLabelValidator = z.string().nonempty().max(80); + +export const buttonStyleValidator = z.number().min(ButtonStyle.Primary).max(ButtonStyle.Danger); + +export const placeholderValidator = z.string().max(100); +export const minMaxValidator = z.number().max(25).min(0); + +export const optionsValidator = z.object({}).array().nonempty(); + +export function validateRequiredSelectMenuParameters(options: SelectMenuOption[], customId?: string) { + customIdValidator.parse(customId); + optionsValidator.parse(options); +} + +export const labelValueValidator = z.string().min(1).max(100); +export const defaultValidator = z.boolean(); + +export function validateRequiredSelectMenuOptionParameters(label: string, value: string) { + labelValueValidator.parse(label); + labelValueValidator.parse(value); +} + +export const urlValidator = z.string().url(); diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts index c930f1d3..70b9cf7c 100644 --- a/src/components/button/BaseButton.ts +++ b/src/components/button/BaseButton.ts @@ -5,40 +5,27 @@ import { ButtonStyle, ComponentType, } from 'discord-api-types/v9'; -import { z } from 'zod'; -import { disabledValidator, emojiValidator } from '../Assertions'; +import { buttonLabelValidator, disabledValidator, emojiValidator } from '../Assertions'; import type { Component } from '../Component'; -export function validateButtonFields(button: BaseButtonComponent) { - if (button.emoji && button.label) { - throw new TypeError('Cannot construct a button with both a label and an emoji field present.'); - } -} - -export const buttonLabelValidator = z.string().nonempty().max(80); - export abstract class BaseButtonComponent implements Component { - public readonly type: ComponentType.Button = ComponentType.Button; - public abstract readonly style: ButtonStyle; + public readonly type = ComponentType.Button as const; + public abstract readonly style?: ButtonStyle; public readonly label?: string; public readonly emoji?: APIMessageComponentEmoji; public readonly disabled?: boolean; public constructor(data?: APIButtonComponent) { - if (!data) { - return; - } - - this.label = data.label; - this.emoji = data.emoji; - this.disabled = data.disabled; + this.label = data?.label; + this.emoji = data?.emoji; + this.disabled = data?.disabled; } /** * Sets the emoji to display on this button * @param emoji The emoji to display on this button */ - public setEmoji(emoji: APIMessageComponentEmoji): Omit { + public setEmoji(emoji: APIMessageComponentEmoji) { emojiValidator.parse(emoji); Reflect.set(this, 'emoji', emoji); return this; @@ -64,15 +51,5 @@ export abstract class BaseButtonComponent implements Component { return this; } - protected toPartialJSON() { - validateButtonFields(this); - return { - type: this.type, - label: this.label, - emoji: this.emoji, - disabled: this.disabled, - }; - } - public abstract toJSON(): APIMessageComponent; } diff --git a/src/components/button/InteractionButton.ts b/src/components/button/InteractionButton.ts index 7b578a15..199dc233 100644 --- a/src/components/button/InteractionButton.ts +++ b/src/components/button/InteractionButton.ts @@ -1,33 +1,27 @@ -import { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types/v9'; -import { z } from 'zod'; -import { customIdValidator } from '../Assertions'; +import type { APIButtonComponentWithCustomId } from 'discord-api-types/v9'; +import { customIdValidator, buttonStyleValidator } from '../Assertions'; import { BaseButtonComponent } from './BaseButton'; -export const styleValidator = z.number().min(ButtonStyle.Primary).max(ButtonStyle.Danger); +export type InteractionButtonStyle = APIButtonComponentWithCustomId['style']; /** * Represents the a button that can send interactions whenever clicked. */ export class InteractionButtonComponent extends BaseButtonComponent { - public readonly customId!: string; - public readonly style!: APIButtonComponentWithCustomId['style']; + public readonly customId?: string; + public readonly style?: InteractionButtonStyle; public constructor(data?: APIButtonComponentWithCustomId) { super(data); - - if (!data) { - return; - } - - this.customId = data.custom_id; - this.style = data.style; + this.customId = data?.custom_id; + this.style = data?.style; } /** * Sets the style of this button * @param style The style to use for this button */ - public setStyle(style: Exclude) { + public setStyle(style: InteractionButtonStyle) { Reflect.set(this, 'style', style); return this; } @@ -42,13 +36,13 @@ export class InteractionButtonComponent extends BaseButtonComponent { return this; } - public override toJSON(): APIButtonComponentWithCustomId { + public toJSON(): APIButtonComponentWithCustomId { // Style is required - styleValidator.parse(this.style); + buttonStyleValidator.parse(this.style); return { - ...super.toPartialJSON(), - style: this.style, - custom_id: this.customId, + ...this, + style: this.style!, + custom_id: this.customId!, }; } } diff --git a/src/components/button/LinkButton.ts b/src/components/button/LinkButton.ts index 2bd371f0..957597d4 100644 --- a/src/components/button/LinkButton.ts +++ b/src/components/button/LinkButton.ts @@ -1,25 +1,18 @@ import { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types/v9'; +import { urlValidator } from '../Assertions'; import { BaseButtonComponent } from './BaseButton'; -import { z } from 'zod'; - -export const urlValidator = z.string().url(); /** * Represents a button that opens a specified URL when clicked. */ export class LinkButtonComponent extends BaseButtonComponent { - public readonly url!: string; - public readonly style: ButtonStyle.Link = ButtonStyle.Link; + public readonly url?: string; + public readonly style = ButtonStyle.Link as const; public constructor(data?: APIButtonComponentWithURL) { super(data); this.style = ButtonStyle.Link; - - if (!data) { - return; - } - - this.url = data.url; + this.url = data?.url; } /** @@ -32,13 +25,13 @@ export class LinkButtonComponent extends BaseButtonComponent { return this; } - public override toJSON(): APIButtonComponentWithURL { + public toJSON(): APIButtonComponentWithURL { // url is required. urlValidator.parse(this.url); return { - ...super.toPartialJSON(), + ...this, style: this.style, - url: this.url, + url: this.url!, }; } } diff --git a/src/components/selectMenu/SelectMenu.ts b/src/components/selectMenu/SelectMenu.ts index f0cd81d1..786a4b8c 100644 --- a/src/components/selectMenu/SelectMenu.ts +++ b/src/components/selectMenu/SelectMenu.ts @@ -1,43 +1,33 @@ import { APISelectMenuComponent, ComponentType } from 'discord-api-types/v9'; -import { customIdValidator, disabledValidator } from '../Assertions'; +import { + customIdValidator, + disabledValidator, + minMaxValidator, + placeholderValidator, + validateRequiredSelectMenuParameters, +} from '../Assertions'; import type { Component } from '../Component'; -import { z } from 'zod'; import { SelectMenuOption } from './SelectMenuOption'; -export const placeholderValidator = z.string().max(100); -export const minMaxValidator = z.number().max(25).min(0); - -export const optionsValidator = z.object({}).array().nonempty(); - -function validateRequiredParameters(customId: string, options: SelectMenuOption[]) { - customIdValidator.parse(customId); - optionsValidator.parse(options); -} - /** * Represents a select menu component */ export class SelectMenuComponent implements Component { public readonly type = ComponentType.SelectMenu; - public readonly options!: SelectMenuOption[]; + public readonly options: SelectMenuOption[]; public readonly placeholder?: string; public readonly minValues?: number; public readonly maxValues?: number; - public readonly customId!: string; + public readonly customId?: string; public readonly disabled?: boolean; public constructor(data?: APISelectMenuComponent) { - if (!data) { - this.options = []; - return; - } - - this.options = data.options.map((option) => new SelectMenuOption(option)); - this.placeholder = data.placeholder; - this.minValues = data.min_values; - this.maxValues = data.max_values; - this.customId = data.custom_id; - this.disabled = data.disabled; + this.options = data?.options.map((option) => new SelectMenuOption(option)) ?? []; + this.placeholder = data?.placeholder; + this.minValues = data?.min_values; + this.maxValues = data?.max_values; + this.customId = data?.custom_id; + this.disabled = data?.disabled; } /** @@ -105,15 +95,15 @@ export class SelectMenuComponent implements Component { * @param options The options to set on this select menu */ public setOptions(options: SelectMenuOption[]) { - Reflect.set(this, 'options', options); + Reflect.set(this, 'options', [...options]); return this; } public toJSON(): APISelectMenuComponent { - validateRequiredParameters(this.customId, this.options); + validateRequiredSelectMenuParameters(this.options, this.customId); return { type: this.type, - custom_id: this.customId, + custom_id: this.customId!, options: this.options.map((option) => option.toJSON()), placeholder: this.placeholder, min_values: this.minValues, diff --git a/src/components/selectMenu/SelectMenuOption.ts b/src/components/selectMenu/SelectMenuOption.ts index 7447841c..d0551eb7 100644 --- a/src/components/selectMenu/SelectMenuOption.ts +++ b/src/components/selectMenu/SelectMenuOption.ts @@ -1,14 +1,10 @@ import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v9'; -import { z } from 'zod'; -import { emojiValidator } from '../Assertions'; - -export const labelValueValidator = z.string().min(1).max(100); -export const defaultValidator = z.boolean(); - -export function validateRequiredParameters(label: string, value: string) { - labelValueValidator.parse(label); - labelValueValidator.parse(value); -} +import { + defaultValidator, + emojiValidator, + labelValueValidator, + validateRequiredSelectMenuOptionParameters, +} from '../Assertions'; /** * Represents an option within a select menu component @@ -81,7 +77,7 @@ export class SelectMenuOption { } public toJSON(): APISelectMenuOption { - validateRequiredParameters(this.label, this.value); + validateRequiredSelectMenuOptionParameters(this.label, this.value); return { label: this.label, value: this.value, From d0b22b01636400a1f371e3325a4fc5a2c759c611 Mon Sep 17 00:00:00 2001 From: suneettipirneni Date: Tue, 28 Dec 2021 23:36:42 -0700 Subject: [PATCH 21/21] test: fix tests --- __tests__/components/actionRow.test.ts | 8 +- __tests__/components/button.test.ts | 76 ++++++++++----- src/components/ActionRow.ts | 4 +- src/components/Assertions.ts | 35 +++++-- src/components/Button.ts | 108 +++++++++++++++++++++ src/components/Components.ts | 11 +-- src/components/button/BaseButton.ts | 55 ----------- src/components/button/InteractionButton.ts | 48 --------- src/components/button/LinkButton.ts | 37 ------- src/index.ts | 5 +- 10 files changed, 199 insertions(+), 188 deletions(-) create mode 100644 src/components/Button.ts delete mode 100644 src/components/button/BaseButton.ts delete mode 100644 src/components/button/InteractionButton.ts delete mode 100644 src/components/button/LinkButton.ts diff --git a/__tests__/components/actionRow.test.ts b/__tests__/components/actionRow.test.ts index 175fdfd2..b1dbc088 100644 --- a/__tests__/components/actionRow.test.ts +++ b/__tests__/components/actionRow.test.ts @@ -1,11 +1,11 @@ import { APIActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types/v9'; -import { ActionRow, createComponent, LinkButtonComponent } from '../../src'; +import { ActionRow, ButtonComponent, createComponent } from '../../src'; describe('Action Row Components', () => { describe('Assertion Tests', () => { test('GIVEN valid components THEN do not throw', () => { - expect(() => new ActionRow().addComponents(new LinkButtonComponent())).not.toThrowError(); - expect(() => new ActionRow().setComponents([new LinkButtonComponent()])).not.toThrowError(); + expect(() => new ActionRow().addComponents(new ButtonComponent())).not.toThrowError(); + expect(() => new ActionRow().setComponents([new ButtonComponent()])).not.toThrowError(); }); test('GIVEN valid JSON input THEN valid JSON output is given', () => { @@ -41,6 +41,8 @@ describe('Action Row Components', () => { expect(new ActionRow(actionRowData).toJSON()).toEqual(actionRowData); expect(new ActionRow().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] }); expect(() => createComponent({ type: ComponentType.ActionRow, components: [] })).not.toThrowError(); + // @ts-expect-error + expect(() => createComponent({ type: 42, components: [] })).toThrowError(); }); }); }); diff --git a/__tests__/components/button.test.ts b/__tests__/components/button.test.ts index df6034e3..190ed26f 100644 --- a/__tests__/components/button.test.ts +++ b/__tests__/components/button.test.ts @@ -4,10 +4,10 @@ import { ButtonStyle, ComponentType, } from 'discord-api-types/v9'; -import { buttonLabelValidator, InteractionButtonComponent, LinkButtonComponent, styleValidator } from '../../src/index'; +import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions'; +import { ButtonComponent } from '../../src/components/Button'; -const interactionButtonComponent = () => new InteractionButtonComponent(); -const linkButtonComponent = () => new LinkButtonComponent(); +const buttonComponent = () => new ButtonComponent(); const longStr = 'looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong'; @@ -26,24 +26,21 @@ describe('Button Components', () => { }); test('GIVEN valid style then validator does not throw', () => { - expect(() => styleValidator.parse(3)).not.toThrowError(); - expect(() => styleValidator.parse(ButtonStyle.Secondary)).not.toThrowError(); + expect(() => buttonStyleValidator.parse(3)).not.toThrowError(); + expect(() => buttonStyleValidator.parse(ButtonStyle.Secondary)).not.toThrowError(); }); test('GIVEN invalid style then validator does not throw', () => { - expect(() => styleValidator.parse(7)).toThrowError(); - - // Note: this throws because link styles are always set for you. - expect(() => styleValidator.parse(ButtonStyle.Link)).toThrowError(); + expect(() => buttonStyleValidator.parse(7)).toThrowError(); }); test('GIVEN valid fields THEN builder does not throw', () => { expect(() => - interactionButtonComponent().setCustomId('custom').setStyle(ButtonStyle.Primary).setLabel('test'), + buttonComponent().setCustomId('custom').setStyle(ButtonStyle.Primary).setLabel('test'), ).not.toThrowError(); expect(() => { - const button = interactionButtonComponent() + const button = buttonComponent() .setCustomId('custom') .setStyle(ButtonStyle.Primary) .setDisabled(true) @@ -52,34 +49,65 @@ describe('Button Components', () => { button.toJSON(); }).not.toThrowError(); - expect(() => linkButtonComponent().setURL('https://google.com')).not.toThrowError(); + expect(() => buttonComponent().setURL('https://google.com')).not.toThrowError(); }); test('GIVEN invalid fields THEN build does throw', () => { expect(() => { - interactionButtonComponent().setCustomId(longStr); + buttonComponent().setCustomId(longStr); }).toThrowError(); expect(() => { - const button = interactionButtonComponent() + const button = buttonComponent() .setCustomId('custom') .setStyle(ButtonStyle.Primary) .setDisabled(true) .setLabel('test') - // @ts-expect-error + .setURL('https://google.com') .setEmoji({ name: 'test' }); button.toJSON(); }).toThrowError(); - expect(() => interactionButtonComponent().setStyle(24)); - expect(() => interactionButtonComponent().setLabel(longStr)); + expect(() => { + // @ts-expect-error + const button = buttonComponent().setEmoji('test'); + button.toJSON(); + }).toThrowError(); + + expect(() => { + const button = buttonComponent().setStyle(ButtonStyle.Primary); + button.toJSON(); + }).toThrowError(); + + expect(() => { + const button = buttonComponent().setStyle(ButtonStyle.Primary).setCustomId('test'); + button.toJSON(); + }).toThrowError(); + + expect(() => { + const button = buttonComponent().setStyle(ButtonStyle.Link); + button.toJSON(); + }).toThrowError(); + + expect(() => { + const button = buttonComponent().setStyle(ButtonStyle.Primary).setLabel('test').setURL('https://google.com'); + button.toJSON(); + }).toThrowError(); + + expect(() => { + const button = buttonComponent().setStyle(ButtonStyle.Link).setLabel('test'); + button.toJSON(); + }).toThrowError(); + + expect(() => buttonComponent().setStyle(24)).toThrowError(); + expect(() => buttonComponent().setLabel(longStr)).toThrowError(); // @ts-ignore - expect(() => interactionButtonComponent().setDisabled(0)); + expect(() => buttonComponent().setDisabled(0)).toThrowError(); // @ts-ignore - expect(() => interactionButtonComponent().setEmoji('foo')); + expect(() => buttonComponent().setEmoji('foo')).toThrowError(); - expect(() => linkButtonComponent().setURL('foobar')).toThrowError(); + expect(() => buttonComponent().setURL('foobar')).toThrowError(); }); test('GiVEN valid input THEN valid JSON outputs are given', () => { @@ -91,10 +119,10 @@ describe('Button Components', () => { disabled: true, }; - expect(new InteractionButtonComponent(interactionData).toJSON()).toEqual(interactionData); + expect(new ButtonComponent(interactionData).toJSON()).toEqual(interactionData); expect( - interactionButtonComponent() + buttonComponent() .setCustomId(interactionData.custom_id) .setLabel(interactionData.label) .setStyle(interactionData.style) @@ -110,9 +138,9 @@ describe('Button Components', () => { url: 'https://google.com', }; - expect(new LinkButtonComponent(linkData).toJSON()).toEqual(linkData); + expect(new ButtonComponent(linkData).toJSON()).toEqual(linkData); - expect(linkButtonComponent().setLabel(linkData.label).setDisabled(true).setURL(linkData.url)); + expect(buttonComponent().setLabel(linkData.label).setDisabled(true).setURL(linkData.url)); }); }); }); diff --git a/src/components/ActionRow.ts b/src/components/ActionRow.ts index d5d6cf38..d3eec578 100644 --- a/src/components/ActionRow.ts +++ b/src/components/ActionRow.ts @@ -1,9 +1,9 @@ import { APIActionRowComponent, ComponentType } from 'discord-api-types/v9'; -import type { LinkButtonComponent, InteractionButtonComponent, SelectMenuComponent } from '..'; +import type { ButtonComponent, SelectMenuComponent } from '..'; import type { Component } from './Component'; import { createComponent } from './Components'; -export type ActionRowComponent = LinkButtonComponent | InteractionButtonComponent | SelectMenuComponent; +export type ActionRowComponent = ButtonComponent | SelectMenuComponent; // TODO: Add valid form component types diff --git a/src/components/Assertions.ts b/src/components/Assertions.ts index a2082681..acd14f19 100644 --- a/src/components/Assertions.ts +++ b/src/components/Assertions.ts @@ -1,6 +1,5 @@ -import { ButtonStyle } from 'discord-api-types'; +import { APIMessageComponentEmoji, ButtonStyle } from 'discord-api-types/v9'; import { z } from 'zod'; -import type { BaseButtonComponent } from './button/BaseButton'; import type { SelectMenuOption } from './selectMenu/SelectMenuOption'; export const customIdValidator = z.string().min(1).max(100); @@ -16,15 +15,9 @@ export const emojiValidator = z export const disabledValidator = z.boolean(); -export function validateButtonFields(button: BaseButtonComponent) { - if (button.emoji && button.label) { - throw new TypeError('Cannot construct a button with both a label and an emoji field present.'); - } -} - export const buttonLabelValidator = z.string().nonempty().max(80); -export const buttonStyleValidator = z.number().min(ButtonStyle.Primary).max(ButtonStyle.Danger); +export const buttonStyleValidator = z.number().min(ButtonStyle.Primary).max(ButtonStyle.Link); export const placeholderValidator = z.string().max(100); export const minMaxValidator = z.number().max(25).min(0); @@ -45,3 +38,27 @@ export function validateRequiredSelectMenuOptionParameters(label: string, value: } export const urlValidator = z.string().url(); + +export function validateRequiredButtonParameters( + style: ButtonStyle, + label?: string, + emoji?: APIMessageComponentEmoji, + customId?: string, + url?: string, +) { + if (url && customId) { + throw new RangeError('url and custom ID are mutually exlcusive'); + } + + if (!label && !emoji) { + throw new RangeError('Buttons must have a label and/or an emoji'); + } + + if (style === ButtonStyle.Link) { + if (!url) { + throw new RangeError('Link buttons must have a url'); + } + } else if (url) { + throw new RangeError('Interaction buttons cannot have a url'); + } +} diff --git a/src/components/Button.ts b/src/components/Button.ts new file mode 100644 index 00000000..9442a624 --- /dev/null +++ b/src/components/Button.ts @@ -0,0 +1,108 @@ +import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types/v9'; +import { + buttonLabelValidator, + buttonStyleValidator, + customIdValidator, + disabledValidator, + emojiValidator, + urlValidator, + validateRequiredButtonParameters, +} from './Assertions'; +import type { Component } from './Component'; + +export class ButtonComponent implements Component { + public readonly type = ComponentType.Button as const; + public readonly style?: ButtonStyle; + public readonly label?: string; + public readonly emoji?: APIMessageComponentEmoji; + public readonly disabled?: boolean; + public readonly customId?: string; + public readonly url?: string; + + public constructor(data?: APIButtonComponent) { + this.style = data?.style; + this.label = data?.label; + this.emoji = data?.emoji; + this.disabled = data?.disabled; + + // This if/else makes typescript happy + if (data?.style === ButtonStyle.Link) { + this.url = data.url; + } else { + this.customId = data?.custom_id; + } + } + + /** + * Sets the style of this button + * @param style The style of the button + */ + public setStyle(style: ButtonStyle) { + buttonStyleValidator.parse(style); + Reflect.set(this, 'style', style); + return this; + } + + /** + * Sets the URL for this button + * @param url The URL to open when this button is clicked + */ + public setURL(url: string) { + urlValidator.parse(url); + Reflect.set(this, 'url', url); + return this; + } + + /** + * Sets the custom Id for this button + * @param customId The custom ID to use for this button + */ + public setCustomId(customId: string) { + customIdValidator.parse(customId); + Reflect.set(this, 'customId', customId); + return this; + } + + /** + * Sets the emoji to display on this button + * @param emoji The emoji to display on this button + */ + public setEmoji(emoji: APIMessageComponentEmoji) { + emojiValidator.parse(emoji); + Reflect.set(this, 'emoji', emoji); + return this; + } + + /** + * Sets whether this button is disable or not + * @param disabled Whether or not to disable this button or not + */ + public setDisabled(disabled: boolean) { + disabledValidator.parse(disabled); + Reflect.set(this, 'disabled', disabled); + return this; + } + + /** + * Sets the label for this button + * @param label The label to display on this button + */ + public setLabel(label: string) { + buttonLabelValidator.parse(label); + Reflect.set(this, 'label', label); + return this; + } + + public toJSON(): APIButtonComponent { + validateRequiredButtonParameters(this.style!, this.label, this.emoji, this.customId, this.url); + return { + type: this.type, + style: this.style!, + label: this.label, + url: this.url!, + emoji: this.emoji, + disabled: this.disabled, + custom_id: this.customId!, + }; + } +} diff --git a/src/components/Components.ts b/src/components/Components.ts index bfee567b..7060fe24 100644 --- a/src/components/Components.ts +++ b/src/components/Components.ts @@ -1,10 +1,10 @@ -import { APIMessageComponent, ButtonStyle, ComponentType } from 'discord-api-types/v9'; -import { ActionRow, Component, InteractionButtonComponent, LinkButtonComponent, SelectMenuComponent } from '../index'; +import { APIMessageComponent, ComponentType } from 'discord-api-types/v9'; +import { ActionRow, ButtonComponent, Component, SelectMenuComponent } from '../index'; import type { ActionRowComponent } from './ActionRow'; export interface MappedComponentTypes { [ComponentType.ActionRow]: ActionRow; - [ComponentType.Button]: LinkButtonComponent | InteractionButtonComponent; + [ComponentType.Button]: ButtonComponent; [ComponentType.SelectMenu]: SelectMenuComponent; } @@ -20,10 +20,7 @@ export function createComponent(data: APIMessageComponent): Component { case ComponentType.ActionRow: return new ActionRow(data); case ComponentType.Button: - if (data.style === ButtonStyle.Link) { - return new LinkButtonComponent(data); - } - return new InteractionButtonComponent(data); + return new ButtonComponent(data); case ComponentType.SelectMenu: return new SelectMenuComponent(data); default: diff --git a/src/components/button/BaseButton.ts b/src/components/button/BaseButton.ts deleted file mode 100644 index 70b9cf7c..00000000 --- a/src/components/button/BaseButton.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - APIButtonComponent, - APIMessageComponent, - APIMessageComponentEmoji, - ButtonStyle, - ComponentType, -} from 'discord-api-types/v9'; -import { buttonLabelValidator, disabledValidator, emojiValidator } from '../Assertions'; -import type { Component } from '../Component'; - -export abstract class BaseButtonComponent implements Component { - public readonly type = ComponentType.Button as const; - public abstract readonly style?: ButtonStyle; - public readonly label?: string; - public readonly emoji?: APIMessageComponentEmoji; - public readonly disabled?: boolean; - - public constructor(data?: APIButtonComponent) { - this.label = data?.label; - this.emoji = data?.emoji; - this.disabled = data?.disabled; - } - - /** - * Sets the emoji to display on this button - * @param emoji The emoji to display on this button - */ - public setEmoji(emoji: APIMessageComponentEmoji) { - emojiValidator.parse(emoji); - Reflect.set(this, 'emoji', emoji); - return this; - } - - /** - * Sets whether this button is disable or not - * @param disabled Whether or not to disable this button or not - */ - public setDisabled(disabled: boolean) { - disabledValidator.parse(disabled); - Reflect.set(this, 'disabled', disabled); - return this; - } - - /** - * Sets the label for this button - * @param label The label to display on this button - */ - public setLabel(label: string): Omit { - buttonLabelValidator.parse(label); - Reflect.set(this, 'label', label); - return this; - } - - public abstract toJSON(): APIMessageComponent; -} diff --git a/src/components/button/InteractionButton.ts b/src/components/button/InteractionButton.ts deleted file mode 100644 index 199dc233..00000000 --- a/src/components/button/InteractionButton.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { APIButtonComponentWithCustomId } from 'discord-api-types/v9'; -import { customIdValidator, buttonStyleValidator } from '../Assertions'; -import { BaseButtonComponent } from './BaseButton'; - -export type InteractionButtonStyle = APIButtonComponentWithCustomId['style']; - -/** - * Represents the a button that can send interactions whenever clicked. - */ -export class InteractionButtonComponent extends BaseButtonComponent { - public readonly customId?: string; - public readonly style?: InteractionButtonStyle; - - public constructor(data?: APIButtonComponentWithCustomId) { - super(data); - this.customId = data?.custom_id; - this.style = data?.style; - } - - /** - * Sets the style of this button - * @param style The style to use for this button - */ - public setStyle(style: InteractionButtonStyle) { - Reflect.set(this, 'style', style); - return this; - } - - /** - * Sets the custom Id for this button - * @param customId The custom ID to use for this button - */ - public setCustomId(customId: string) { - customIdValidator.parse(customId); - Reflect.set(this, 'customId', customId); - return this; - } - - public toJSON(): APIButtonComponentWithCustomId { - // Style is required - buttonStyleValidator.parse(this.style); - return { - ...this, - style: this.style!, - custom_id: this.customId!, - }; - } -} diff --git a/src/components/button/LinkButton.ts b/src/components/button/LinkButton.ts deleted file mode 100644 index 957597d4..00000000 --- a/src/components/button/LinkButton.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types/v9'; -import { urlValidator } from '../Assertions'; -import { BaseButtonComponent } from './BaseButton'; - -/** - * Represents a button that opens a specified URL when clicked. - */ -export class LinkButtonComponent extends BaseButtonComponent { - public readonly url?: string; - public readonly style = ButtonStyle.Link as const; - - public constructor(data?: APIButtonComponentWithURL) { - super(data); - this.style = ButtonStyle.Link; - this.url = data?.url; - } - - /** - * Sets the URL for this button - * @param url The URL to open when this button is clicked - */ - public setURL(url: string) { - urlValidator.parse(url); - Reflect.set(this, 'url', url); - return this; - } - - public toJSON(): APIButtonComponentWithURL { - // url is required. - urlValidator.parse(this.url); - return { - ...this, - style: this.style, - url: this.url!, - }; - } -} diff --git a/src/index.ts b/src/index.ts index b94d7abd..f5db03c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,11 +4,10 @@ export * from './messages/formatters'; export * as ComponentAssertions from './components/Assertions'; export * from './components/ActionRow'; -export * from './components/button/BaseButton'; +export * from './components/Button'; export * from './components/Component'; export * from './components/Components'; -export * from './components/button/InteractionButton'; -export * from './components/button/LinkButton'; +export * from './components/Button'; export * from './components/selectMenu/SelectMenu'; export * from './components/selectMenu/SelectMenuOption';