Compare commits

...

16 Commits

Author SHA1 Message Date
Vlad Frangu
f7bc2d0db8 chore(rest): release @discordjs/rest@2.6.1 2026-03-23 01:32:17 +02:00
Jiralite
baeeb38185 build: upgrade Undici 2026-03-19 08:03:53 +00:00
Jiralite
1a0da18b36 fix: Remove manage messages check for pinnable (#11453)
fix: remove manage messages check
2026-03-18 19:40:12 +00:00
faceboy
ca7719e822 feat(builders): add checkbox, checkboxgroup, and radiogroup builders (#11410)
* feat(builders): add checkbox, checkboxgroup, and radiogroup builders

* Update packages/builders/src/components/checkbox/Assertions.ts

fix incorrect wording about default option count in radio groups

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* fix(builders): remove length validators from add/splice options

* chore: remove directives

* fix(builders): documentation fixes

* fix(builders): return Result.err instead of throw in validators

---------

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2026-03-11 13:39:16 +01:00
faceboy
ec5d921b75 docs(builders): edited docs to correctly link to splice (#11430)
fix(builders): edited docs to correctly link to splice

StringSelectMenuBuilder.splice()'s docs incorrectly linked to slice instead of splice in MDN docs, fixed that
2026-03-11 08:14:49 +00:00
Naiyar
b42e499410 feat: modal radio group and checkbox components for v14 (#11437)
feat: modal radio group and checkbox components

* feat: radio group and checkbox component in modal

* chore: some doc and type fixes

* chore: missing types

* chore: update component names

* docs: radio group value now returns null if not selected

* Remove empty line at the beginning of ModalSubmitInteraction.js

* Change value property to be nullable in RadioGroupModalData

* chore: review

* chore: missing "type"

* chore: suggestion



* chore: review



---------

Co-authored-by: Almeida <github@almeidx.dev>
2026-03-11 09:11:29 +01:00
Qjuh
374a6785ae ci: fix docs workflow on v14 tag push (#11438) 2026-03-05 15:14:09 +01:00
Jiralite
319a73f8e6 build: Upgrade v14 dependencies (#11427)
* build: upgrade dependencies

* build: please Undici please

* build: regenerate file

* chore: fix docs by using bundler

* fix: revert upgrade for package

* build: fix versions
2026-02-24 23:18:54 +00:00
Qjuh
16d70b9232 types: broadcastEval overload order (#11422)
types: broadcastEval overload order (#11421)

* types: broadcastEval overload order

* types: also apply to ShardClientUtil
2026-02-16 17:59:58 +01:00
Jiralite
4476fadd19 chore(builders): release @discordjs/builders@1.13.1 2025-11-30 19:35:11 +00:00
Jiralite
61251cfcb8 fix: Adjust label predicates (#11318)
* fix: labels in 14

* test: add variable for at limit

* docs(jsonOptionValidator): deprecate

* fix: export `selectMenuStringOptionPredicate`

* fix: export everything

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

---------

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
2025-11-30 18:38:46 +00:00
Jiralite
e77793898a docs(Container): update example usage
Resolves #11297.
2025-11-28 10:24:58 +00:00
Jiralite
f5b3f842e3 fix(DJSError): Differentiate error type (#11295)
* fix(DJSError): differentiate error type

* fix: remove `?.`
2025-11-22 20:23:09 +00:00
Jiralite
e32f0c141a refactor(DJSError): Prefer this.constructor.name (#11294)
refactor(DJSError): prefer `this.constructor.name`
2025-11-22 12:50:54 +00:00
Jiralite
fdac8c5bdd chore(discord.js): release discord.js@14.25.1 2025-11-21 16:23:04 +00:00
Jiralite
0d64ea0528 fix(GuildEmojiManager): Allow CreateGuildExpressions for retrieving author data (#11288)
* fix(GuildEmojiManager)!: Allow `CreateGuildExpressions` for retrieving author data (#11283)

* fix(GuildEmojiManager): allow `CreateGuildExpressions`

* fix: tests
2025-11-21 16:22:20 +00:00
89 changed files with 6480 additions and 4473 deletions

View File

@@ -1,8 +1,6 @@
name: Documentation
on:
push:
branches:
- 'main'
paths:
- 'packages/*/src/**'
- '!packages/create-discord-bot/**'
@@ -13,14 +11,7 @@ on:
workflow_dispatch:
inputs:
ref:
description: 'The branch, tag or SHA to checkout'
required: true
ref_type:
type: choice
description: 'Branch or tag'
options:
- branch
- tag
description: 'The tag to checkout'
required: true
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
@@ -32,7 +23,6 @@ jobs:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
REF_TYPE: ${{ inputs.ref_type || github.ref_type }}
if: github.repository_owner == 'discordjs'
steps:
- name: Checkout repository
@@ -52,13 +42,11 @@ jobs:
run: pnpm run build
- name: Checkout main repository
if: ${{ inputs.ref && inputs.ref != 'main' }}
uses: actions/checkout@v4
with:
path: 'main'
- name: Build main
if: ${{ inputs.ref && inputs.ref != 'main' }}
shell: bash
run: |
cd main
@@ -67,26 +55,20 @@ jobs:
cd ..
- name: Extract package and semver from tag
if: ${{ env.REF_TYPE == 'tag' }}
id: extract-tag
uses: ./packages/actions/src/formatTag
with:
tag: ${{ inputs.ref || github.ref_name }}
- name: Apply tag to api-extractor config
if: ${{ env.REF_TYPE == 'tag' && !inputs.ref }}
run: sed -i 's!https://github.com/discordjs/discord.js/tree/main!https://github.com/discordjs/discord.js/tree/${{ github.ref_name }}!' "packages/${{ steps.extract-tag.outputs.package}}/api-extractor.json"
- name: Build docs
run: pnpm run docs
- name: Build docs with main api-extractor
if: ${{ inputs.ref && inputs.ref != 'main' }}
run: |
declare -a PACKAGES=("brokers" "builders" "collection" "core" "discord.js" "formatters" "next" "proxy" "rest" "util" "voice" "ws")
for PACKAGE in "${PACKAGES[@]}"; do
cd "packages/${PACKAGE}"
sed -i 's!https://github.com/discordjs/discord.js/tree/main!https://github.com/discordjs/discord.js/tree/${{ inputs.ref }}!' api-extractor.json
sed -i 's!https://github.com/discordjs/discord.js/tree/main!https://github.com/discordjs/discord.js/tree/${{ inputs.ref || github.ref_name }}!' api-extractor.json
../../main/packages/api-extractor/bin/api-extractor run --local --minify
../../main/packages/scripts/bin/generateSplitDocumentation.js
cd ../..
@@ -100,25 +82,6 @@ jobs:
path: 'out'
- name: Upload documentation to database
if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./packages/actions/src/uploadDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
version: ${{ steps.extract-tag.outputs.semver }}
- name: Upload documentation to database
if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
@@ -136,20 +99,6 @@ jobs:
version: ${{ steps.extract-tag.outputs.semver }}
- name: Upload split documentation to blob storage
if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./packages/actions/src/uploadSplitDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
version: ${{ steps.extract-tag.outputs.semver }}
- name: Upload split documentation to blob storage
if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
@@ -162,7 +111,6 @@ jobs:
version: ${{ steps.extract-tag.outputs.semver }}
- name: Move docs to correct directory
if: ${{ env.REF_TYPE == 'tag' }}
env:
PACKAGE: ${{ steps.extract-tag.outputs.package }}
SEMVER: ${{ steps.extract-tag.outputs.semver }}
@@ -175,71 +123,6 @@ jobs:
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${SEMVER}.api.json"
fi
- name: Upload documentation to database
if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./packages/actions/src/uploadDocumentation
- name: Upload documentation to database
if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./main/packages/actions/src/uploadDocumentation
- name: Upload split documentation to blob storage
if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./packages/actions/src/uploadSplitDocumentation
- name: Upload split documentation to blob storage
if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./main/packages/actions/src/uploadSplitDocumentation
- name: Move docs to correct directory
if: ${{ env.REF_TYPE == 'branch' }}
run: |
declare -a PACKAGES=("brokers" "builders" "collection" "core" "discord.js" "formatters" "next" "proxy" "rest" "util" "voice" "ws")
for PACKAGE in "${PACKAGES[@]}"; do
if [[ "${PACKAGE}" == "discord.js" ]]; then
mkdir -p "out/${PACKAGE}"
mv "packages/${PACKAGE}/docs/docs.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.json"
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.api.json"
else
mkdir -p "out/${PACKAGE}"
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.api.json"
fi
done
- name: Commit and push
run: |
cd out

View File

@@ -107,7 +107,7 @@
"allowArbitraryExtensions": false,
"allowImportingTsExtensions": false,
"module": "ESNext",
"moduleResolution": "nodenext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"resolvePackageJsonExports": false,
"resolvePackageJsonImports": false,

View File

@@ -56,17 +56,17 @@
"@unocss/eslint-plugin": "^0.59.4",
"@vitest/coverage-v8": "^2.0.5",
"conventional-changelog-cli": "^4.1.0",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"husky": "^9.1.5",
"is-ci": "^3.0.1",
"lint-staged": "^15.2.9",
"lodash.merge": "^4.6.2",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"typescript-eslint": "^8.2.0",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"typescript-eslint": "^8.56.0",
"unocss": "^0.60.4",
"vercel": "^37.0.0",
"vitest": "^2.0.5"

View File

@@ -42,8 +42,8 @@
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/glob": "^0.5.0",
"@aws-sdk/client-s3": "^3.787.0",
"@actions/glob": "^0.5.1",
"@aws-sdk/client-s3": "^3.995.0",
"@discordjs/scripts": "workspace:^",
"@vercel/blob": "^0.27.3",
"@vercel/postgres": "^0.9.0",
@@ -52,19 +52,19 @@
"p-limit": "^6.2.0",
"p-queue": "^8.1.0",
"tslib": "^2.8.1",
"undici": "7.8.0"
"undici": "7.22.0"
},
"devDependencies": {
"@types/node": "^22.14.0",
"@types/node": "^22.19.11",
"@vitest/coverage-v8": "^3.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.5.3",
"terser": "^5.37.0",
"tsup": "^8.4.0",
"turbo": "^2.5.0",
"prettier": "^3.8.1",
"terser": "^5.46.0",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^3.1.1"
},

View File

@@ -37,15 +37,15 @@
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4"
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3"
}
}

View File

@@ -15,7 +15,8 @@ import { type IApiDeclaredItemOptions, ApiDeclaredItem, type IApiDeclaredItemJso
* @public
*/
export interface IApiPropertyItemOptions
extends IApiNameMixinOptions,
extends
IApiNameMixinOptions,
IApiReleaseTagMixinOptions,
IApiOptionalMixinOptions,
IApiReadonlyMixinOptions,

View File

@@ -18,7 +18,8 @@ import {
* @public
*/
export interface IApiCallSignatureOptions
extends IApiTypeParameterListMixinOptions,
extends
IApiTypeParameterListMixinOptions,
IApiParameterListMixinOptions,
IApiReleaseTagMixinOptions,
IApiReturnTypeMixinOptions,

View File

@@ -32,7 +32,8 @@ import { HeritageType } from './HeritageType.js';
* @public
*/
export interface IApiClassOptions
extends IApiItemContainerMixinOptions,
extends
IApiItemContainerMixinOptions,
IApiNameMixinOptions,
IApiAbstractMixinOptions,
IApiReleaseTagMixinOptions,
@@ -48,10 +49,7 @@ export interface IExcerptTokenRangeWithTypeParameters extends IExcerptTokenRange
}
export interface IApiClassJson
extends IApiDeclaredItemJson,
IApiAbstractMixinJson,
IApiTypeParameterListMixinJson,
IApiExportedMixinJson {
extends IApiDeclaredItemJson, IApiAbstractMixinJson, IApiTypeParameterListMixinJson, IApiExportedMixinJson {
extendsTokenRange?: IExcerptTokenRangeWithTypeParameters | undefined;
implementsTokenRanges: IExcerptTokenRangeWithTypeParameters[];
}

View File

@@ -18,7 +18,8 @@ import {
* @public
*/
export interface IApiConstructSignatureOptions
extends IApiTypeParameterListMixinOptions,
extends
IApiTypeParameterListMixinOptions,
IApiParameterListMixinOptions,
IApiReleaseTagMixinOptions,
IApiReturnTypeMixinOptions,

View File

@@ -14,7 +14,8 @@ import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/A
* @public
*/
export interface IApiConstructorOptions
extends IApiParameterListMixinOptions,
extends
IApiParameterListMixinOptions,
IApiProtectedMixinOptions,
IApiReleaseTagMixinOptions,
IApiDeclaredItemOptions {}

View File

@@ -16,7 +16,8 @@ import type { ApiEnumMember } from './ApiEnumMember.js';
* @public
*/
export interface IApiEnumOptions
extends IApiItemContainerMixinOptions,
extends
IApiItemContainerMixinOptions,
IApiNameMixinOptions,
IApiReleaseTagMixinOptions,
IApiDeclaredItemOptions,

View File

@@ -14,10 +14,7 @@ import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/A
* @public
*/
export interface IApiEnumMemberOptions
extends IApiNameMixinOptions,
IApiReleaseTagMixinOptions,
IApiDeclaredItemOptions,
IApiInitializerMixinOptions {}
extends IApiNameMixinOptions, IApiReleaseTagMixinOptions, IApiDeclaredItemOptions, IApiInitializerMixinOptions {}
/**
* Options for customizing the sort order of {@link ApiEnum} members.

View File

@@ -14,10 +14,7 @@ import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/A
* @public
*/
export interface IApiEventOptions
extends IApiNameMixinOptions,
IApiParameterListMixinOptions,
IApiReleaseTagMixinOptions,
IApiDeclaredItemOptions {}
extends IApiNameMixinOptions, IApiParameterListMixinOptions, IApiReleaseTagMixinOptions, IApiDeclaredItemOptions {}
/**
* Represents a TypeScript event declaration that belongs to an `ApiClass`.

View File

@@ -20,7 +20,8 @@ import {
* @public
*/
export interface IApiFunctionOptions
extends IApiNameMixinOptions,
extends
IApiNameMixinOptions,
IApiTypeParameterListMixinOptions,
IApiParameterListMixinOptions,
IApiReleaseTagMixinOptions,

View File

@@ -15,7 +15,8 @@ import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/A
* @public
*/
export interface IApiIndexSignatureOptions
extends IApiParameterListMixinOptions,
extends
IApiParameterListMixinOptions,
IApiReleaseTagMixinOptions,
IApiReturnTypeMixinOptions,
IApiReadonlyMixinOptions,

View File

@@ -35,7 +35,8 @@ import { HeritageType } from './HeritageType.js';
* @public
*/
export interface IApiInterfaceOptions
extends IApiItemContainerMixinOptions,
extends
IApiItemContainerMixinOptions,
IApiNameMixinOptions,
IApiTypeParameterListMixinOptions,
IApiReleaseTagMixinOptions,
@@ -45,7 +46,8 @@ export interface IApiInterfaceOptions
}
export interface IApiInterfaceJson
extends IApiItemContainerJson,
extends
IApiItemContainerJson,
IApiNameMixinJson,
IApiTypeParameterListMixinJson,
IApiReleaseTagMixinJson,

View File

@@ -23,7 +23,8 @@ import {
* @public
*/
export interface IApiMethodOptions
extends IApiNameMixinOptions,
extends
IApiNameMixinOptions,
IApiAbstractMixinOptions,
IApiOptionalMixinOptions,
IApiParameterListMixinOptions,

View File

@@ -18,7 +18,8 @@ import {
* @public
*/
export interface IApiMethodSignatureOptions
extends IApiNameMixinOptions,
extends
IApiNameMixinOptions,
IApiTypeParameterListMixinOptions,
IApiParameterListMixinOptions,
IApiReleaseTagMixinOptions,

View File

@@ -15,7 +15,8 @@ import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/A
* @public
*/
export interface IApiNamespaceOptions
extends IApiItemContainerMixinOptions,
extends
IApiItemContainerMixinOptions,
IApiNameMixinOptions,
IApiReleaseTagMixinOptions,
IApiDeclaredItemOptions,

View File

@@ -28,9 +28,7 @@ import { DeserializerContext, ApiJsonSchemaVersion } from './DeserializerContext
* @public
*/
export interface IApiPackageOptions
extends IApiItemContainerMixinOptions,
IApiNameMixinOptions,
IApiDocumentedItemOptions {
extends IApiItemContainerMixinOptions, IApiNameMixinOptions, IApiDocumentedItemOptions {
dependencies?: Record<string, string> | undefined;
projectFolderUrl?: string | undefined;
tsdocConfiguration: TSDocConfiguration;

View File

@@ -15,7 +15,8 @@ import { ApiStaticMixin, type IApiStaticMixinOptions } from '../mixins/ApiStatic
* @public
*/
export interface IApiPropertyOptions
extends IApiPropertyItemOptions,
extends
IApiPropertyItemOptions,
IApiAbstractMixinOptions,
IApiProtectedMixinOptions,
IApiStaticMixinOptions,

View File

@@ -25,7 +25,8 @@ import type { DeserializerContext } from './DeserializerContext.js';
* @public
*/
export interface IApiTypeAliasOptions
extends IApiNameMixinOptions,
extends
IApiNameMixinOptions,
IApiReleaseTagMixinOptions,
IApiDeclaredItemOptions,
IApiTypeParameterListMixinOptions,

View File

@@ -22,7 +22,8 @@ import type { DeserializerContext } from './DeserializerContext.js';
* @public
*/
export interface IApiVariableOptions
extends IApiNameMixinOptions,
extends
IApiNameMixinOptions,
IApiReleaseTagMixinOptions,
IApiReadonlyMixinOptions,
IApiDeclaredItemOptions,

View File

@@ -267,7 +267,8 @@ function mapParam(
}
interface IApiMethodJson
extends IApiAbstractMixinJson,
extends
IApiAbstractMixinJson,
IApiDeclaredItemJson,
IApiNameMixinJson,
IApiOptionalMixinJson,
@@ -279,10 +280,7 @@ interface IApiMethodJson
IApiTypeParameterListMixinJson {}
interface IApiConstructorJson
extends IApiParameterListJson,
IApiProtectedMixinJson,
IApiReleaseTagMixinJson,
IApiDeclaredItemJson {}
extends IApiParameterListJson, IApiProtectedMixinJson, IApiReleaseTagMixinJson, IApiDeclaredItemJson {}
function mapMethod(method: DocgenMethodJson, _package: string, parent?: DocgenClassJson): IApiMethodJson {
const excerptTokens: IExcerptToken[] = [];

View File

@@ -50,15 +50,15 @@
"@microsoft/tsdoc": "0.14.2"
},
"devDependencies": {
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4"
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3"
},
"engines": {
"node": ">=18"

View File

@@ -77,10 +77,7 @@ export interface ApiParameterListJSON {
}
export interface ApiMethodSignatureJSON
extends ApiItemJSON,
ApiTypeParameterListJSON,
ApiParameterListJSON,
ApiInheritableJSON {
extends ApiItemJSON, ApiTypeParameterListJSON, ApiParameterListJSON, ApiInheritableJSON {
mergedSiblings: ApiMethodSignatureJSON[];
optional: boolean;
overloadIndex: number;

View File

@@ -65,18 +65,18 @@
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.4",
"@types/node": "^18.19.45",
"@types/lodash": "^4.17.23",
"@types/node": "^18.19.130",
"@types/resolve": "^1.20.6",
"@types/semver": "^7.5.8",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14"
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10"
}
}

View File

@@ -67,25 +67,25 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@msgpack/msgpack": "^3.0.0-beta2",
"@msgpack/msgpack": "^3.1.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"ioredis": "^5.4.1"
"ioredis": "^5.9.3"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -52,8 +52,7 @@ export interface IBaseBroker<TEvents extends {}> {
}
export interface IPubSubBroker<TEvents extends {}>
extends IBaseBroker<TEvents>,
AsyncEventEmitter<ToEventMap<TEvents>> {
extends IBaseBroker<TEvents>, AsyncEventEmitter<ToEventMap<TEvents>> {
/**
* Publishes an event
*/
@@ -61,8 +60,7 @@ export interface IPubSubBroker<TEvents extends {}>
}
export interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>
extends IBaseBroker<TEvents>,
AsyncEventEmitter<ToEventMap<TEvents, TResponses>> {
extends IBaseBroker<TEvents>, AsyncEventEmitter<ToEventMap<TEvents, TResponses>> {
/**
* Makes an RPC call
*/

View File

@@ -58,9 +58,9 @@ export const DefaultRedisBrokerOptions = {
* Helper class with shared Redis logic
*/
export abstract class BaseRedisBroker<
TEvents extends Record<string, any[]>,
TResponses extends Record<keyof TEvents, any> | undefined = undefined,
>
TEvents extends Record<string, any[]>,
TResponses extends Record<keyof TEvents, any> | undefined = undefined,
>
extends AsyncEventEmitter<ToEventMap<TEvents, TResponses>>
implements IBaseBroker<TEvents>
{

View File

@@ -2,6 +2,16 @@
All notable changes to this project will be documented in this file.
# [@discordjs/builders@1.13.1](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.13.0...@discordjs/builders@1.13.1) - (2025-11-30)
## Bug Fixes
- Adjust label predicates (#11318) ([61251cf](https://github.com/discordjs/discord.js/commit/61251cfcb842cc468aca7a581f54be4fd28c2721))
## Documentation
- **Container:** Update example usage ([e777938](https://github.com/discordjs/discord.js/commit/e77793898ac36320eeee8b999c618918526ab9c2))
# [@discordjs/builders@1.13.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.12.2...@discordjs/builders@1.13.0) - (2025-10-24)
## Features

View File

@@ -0,0 +1,415 @@
import { ComponentType, type APICheckboxComponent } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { LabelBuilder } from '../../src';
import { CheckboxBuilder } from '../../src/components/checkbox/Checkbox';
import { CheckboxGroupBuilder } from '../../src/components/checkbox/CheckboxGroup';
import { CheckboxGroupOptionBuilder } from '../../src/components/checkbox/CheckboxGroupOption';
import { RadioGroupBuilder } from '../../src/components/checkbox/RadioGroup';
import { RadioGroupOptionBuilder } from '../../src/components/checkbox/RadioGroupOption';
const longStr = ':3'.repeat(5_000);
const fiveCheckboxOptions = [
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
new CheckboxGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
new CheckboxGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
];
const elevenCheckboxOptions = [
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
new CheckboxGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
new CheckboxGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
new CheckboxGroupOptionBuilder().setLabel('Option 6').setValue('option_6'),
new CheckboxGroupOptionBuilder().setLabel('Option 7').setValue('option_7'),
new CheckboxGroupOptionBuilder().setLabel('Option 8').setValue('option_8'),
new CheckboxGroupOptionBuilder().setLabel('Option 9').setValue('option_9'),
new CheckboxGroupOptionBuilder().setLabel('Option 10').setValue('option_10'),
new CheckboxGroupOptionBuilder().setLabel('Option 11').setValue('option_11'),
];
const fiveRadioOptions = [
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
new RadioGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
new RadioGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
new RadioGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
];
const elevenRadioOptions = [
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
new RadioGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
new RadioGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
new RadioGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
new RadioGroupOptionBuilder().setLabel('Option 6').setValue('option_6'),
new RadioGroupOptionBuilder().setLabel('Option 7').setValue('option_7'),
new RadioGroupOptionBuilder().setLabel('Option 8').setValue('option_8'),
new RadioGroupOptionBuilder().setLabel('Option 9').setValue('option_9'),
new RadioGroupOptionBuilder().setLabel('Option 10').setValue('option_10'),
new RadioGroupOptionBuilder().setLabel('Option 11').setValue('option_11'),
];
describe('Checkbox Components', () => {
describe('CheckboxBuilder', () => {
test('Valid builder does not throw.', () => {
expect(() => new CheckboxBuilder().setCustomId('checkbox').toJSON()).not.toThrowError();
expect(() => new CheckboxBuilder().setCustomId('checkbox').setDefault(true).toJSON()).not.toThrowError();
});
test('Invalid builder does throw.', () => {
expect(() => new CheckboxBuilder().toJSON()).toThrowError();
expect(() => new CheckboxBuilder().setDefault(true).toJSON()).toThrowError();
expect(() => new CheckboxBuilder().setCustomId(longStr).toJSON()).toThrowError();
});
test('API data equals toJSON().', () => {
const checkboxData = {
type: ComponentType.Checkbox,
custom_id: 'checkbox',
default: true,
} satisfies APICheckboxComponent;
expect(new CheckboxBuilder(checkboxData).toJSON()).toEqual(checkboxData);
expect(new CheckboxBuilder().setCustomId('checkbox').setDefault(true).toJSON()).toEqual(checkboxData);
});
});
describe('CheckboxGroupBuilder', () => {
test('Valid builder does not throw.', () => {
expect(() =>
new CheckboxGroupBuilder({
custom_id: 'checkbox_group',
options: fiveCheckboxOptions.map((option) => option.toJSON()),
}).toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions(fiveCheckboxOptions).toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions([
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
])
.toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupBuilder().setCustomId('checkbox_group').addOptions(fiveCheckboxOptions).toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setMinValues(1)
.setMaxValues(2)
.setOptions([
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
])
.toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.spliceOptions(2, 1, ...elevenCheckboxOptions.slice(7, 9))
.spliceOptions(0, 1, { label: 'New Option', value: 'new_option' }),
).not.toThrowError();
});
test('Invalid builder does throw.', () => {
expect(() => new CheckboxGroupBuilder().toJSON()).toThrowError();
expect(() => new CheckboxGroupBuilder().addOptions([]).toJSON()).toThrowError();
expect(() => new CheckboxGroupBuilder().setMinValues(2).setMaxValues(1).toJSON()).toThrowError();
expect(() =>
new CheckboxGroupBuilder().setMinValues(2).setMaxValues(1).addOptions(fiveCheckboxOptions).toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setMinValues(2)
.setMaxValues(1)
.addOptions(fiveCheckboxOptions)
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder().setCustomId('checkbox_group').setMinValues(5).setMaxValues(11).toJSON(),
).toThrowError();
expect(() => new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions([]).toJSON()).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.setMinValues(6)
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.setMaxValues(6)
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions(elevenCheckboxOptions).toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(elevenCheckboxOptions)
.setMaxValues(12)
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder().setCustomId(longStr).setOptions(fiveCheckboxOptions).toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.setMinValues(0)
.setRequired(true)
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setMaxValues(4)
.setOptions([
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1').setDefault(true),
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2').setDefault(true),
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3').setDefault(true),
new CheckboxGroupOptionBuilder().setLabel('Option 4').setValue('option_4').setDefault(true),
new CheckboxGroupOptionBuilder().setLabel('Option 5').setValue('option_5').setDefault(true),
])
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.addOptions(fiveCheckboxOptions)
.toJSON(),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(elevenCheckboxOptions.slice(0, 5))
.addOptions(elevenCheckboxOptions.slice(5, 11))
.toJSON(),
).toThrowError();
expect(
() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.spliceOptions(2, 1, new CheckboxGroupOptionBuilder().setLabel('Option 6')), // no value
).toThrowError();
expect(() =>
new CheckboxGroupBuilder()
.setCustomId('checkbox_group')
.setOptions(fiveCheckboxOptions)
.spliceOptions(2, 1, { value: 'hi', label: longStr }),
).toThrowError();
expect(() =>
new CheckboxGroupBuilder().setCustomId('checkbox_group').addOptions({ value: 'hi', label: longStr }),
).toThrowError();
});
});
describe('CheckboxGroupOptionBuilder', () => {
test('Valid builder does not throw.', () => {
expect(() =>
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1').toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupOptionBuilder()
.setLabel('Option 2')
.setValue('option_2')
.setDescription('This is option 2')
.toJSON(),
).not.toThrowError();
expect(() =>
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3').setDefault(true).toJSON(),
).not.toThrowError();
});
test('Invalid builder does throw.', () => {
expect(() => new CheckboxGroupOptionBuilder().toJSON()).toThrowError();
expect(() => new CheckboxGroupOptionBuilder().setValue('option_1').toJSON()).toThrowError();
expect(() => new CheckboxGroupOptionBuilder().setLabel('Option 1').toJSON()).toThrowError();
expect(() => new CheckboxGroupOptionBuilder().setLabel(longStr).setValue('option_1').toJSON()).toThrowError();
expect(() => new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue(longStr).toJSON()).toThrowError();
});
test('toJSON returns correct data.', () => {
const option = new CheckboxGroupOptionBuilder()
.setLabel('Option 1')
.setValue('option_1')
.setDescription('This is option 1')
.setDefault(true);
expect(option.toJSON()).toEqual({
label: 'Option 1',
value: 'option_1',
description: 'This is option 1',
default: true,
});
});
});
describe('RadioGroupBuilder', () => {
test('Valid builder does not throw.', () => {
expect(() =>
new RadioGroupBuilder().setCustomId('radio_group').addOptions(fiveRadioOptions).toJSON(),
).not.toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.setOptions([
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
])
.toJSON(),
).not.toThrowError();
expect(() =>
new RadioGroupBuilder().setCustomId('radio_group').addOptions(fiveRadioOptions).setRequired(false),
).not.toThrowError();
expect(() =>
new RadioGroupBuilder().setCustomId('radio_group').addOptions(fiveRadioOptions).setRequired(true),
).not.toThrowError();
expect(() => new RadioGroupBuilder().setCustomId('radio_group').setOptions(fiveRadioOptions)).not.toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.setOptions(fiveRadioOptions)
.spliceOptions(2, 1, elevenRadioOptions.slice(7, 9)),
).not.toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.addOptions(elevenRadioOptions.slice(0, 5))
.spliceOptions(0, 1, { label: 'New Option', value: 'new_option' }),
).not.toThrowError();
expect(() =>
new RadioGroupBuilder({
custom_id: 'radio_group',
options: fiveRadioOptions.map((option) => option.toJSON()),
}).toJSON(),
).not.toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.addOptions(fiveRadioOptions.map((option) => option.toJSON()))
.toJSON(),
).not.toThrowError();
});
test('Invalid builder does throw.', () => {
expect(() => new RadioGroupBuilder().toJSON()).toThrowError();
expect(() => new RadioGroupBuilder().addOptions([]).toJSON()).toThrowError();
// needs at least 2 options
expect(() => new RadioGroupBuilder().addOptions([fiveRadioOptions[0]]).toJSON()).toThrowError();
expect(() =>
new RadioGroupBuilder().setCustomId('radio_group').setOptions([fiveRadioOptions[0]]).toJSON(),
).toThrowError();
expect(() => new RadioGroupBuilder().setCustomId('radio_group').setOptions([]).toJSON()).toThrowError();
expect(() =>
new RadioGroupBuilder().setCustomId('radio_group').setOptions(elevenRadioOptions).toJSON(),
).toThrowError();
expect(() => new RadioGroupBuilder().setCustomId(longStr).setOptions(fiveRadioOptions).toJSON()).toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.setOptions([
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1').setDefault(true),
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2').setDefault(true),
])
.toJSON(),
).toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.addOptions(fiveRadioOptions)
.addOptions(fiveRadioOptions)
.toJSON(),
).toThrowError();
expect(() =>
new RadioGroupBuilder()
.setCustomId('radio_group')
.setOptions(fiveRadioOptions)
.spliceOptions(2, 1, { value: 'hi', label: longStr }),
).toThrowError();
expect(() =>
new RadioGroupBuilder().setCustomId('radio_group').addOptions({ value: 'hi', label: longStr }),
).toThrowError();
});
});
describe('RadioGroupOptionBuilder', () => {
test('Valid builder does not throw.', () => {
expect(() => new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1').toJSON()).not.toThrowError();
expect(() =>
new RadioGroupOptionBuilder()
.setLabel('Option 2')
.setValue('option_2')
.setDescription('This is option 2')
.toJSON(),
).not.toThrowError();
expect(() =>
new RadioGroupOptionBuilder().setLabel('Option 3').setValue('option_3').setDefault(true).toJSON(),
).not.toThrowError();
});
test('Invalid builder does throw.', () => {
expect(() => new RadioGroupOptionBuilder().toJSON()).toThrowError();
expect(() => new RadioGroupOptionBuilder().setValue('option_1').toJSON()).toThrowError();
expect(() => new RadioGroupOptionBuilder().setLabel('Option 1').toJSON()).toThrowError();
expect(() => new RadioGroupOptionBuilder().setLabel(longStr).setValue('option_1').toJSON()).toThrowError();
expect(() => new RadioGroupOptionBuilder().setLabel('Option 1').setValue(longStr).toJSON()).toThrowError();
});
test('toJSON returns correct data.', () => {
const option = new RadioGroupOptionBuilder()
.setLabel('Option 1')
.setValue('option_1')
.setDescription('This is option 1')
.setDefault(true);
expect(option.toJSON()).toEqual({
label: 'Option 1',
value: 'option_1',
description: 'This is option 1',
default: true,
});
});
});
});
describe('LabelBuilder with Checkbox Components', () => {
test('LabelBuilder can set Checkbox component.', () => {
const checkbox = new CheckboxBuilder().setCustomId('checkbox').setDefault(true);
const label = new LabelBuilder().setLabel('Checkbox Label').setCheckboxComponent(checkbox);
expect(() => label.toJSON()).not.toThrowError();
expect(label.toJSON().component).toEqual(checkbox.toJSON());
});
test('LabelBuilder can set CheckboxGroup component.', () => {
const checkboxGroup = new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions(fiveCheckboxOptions);
const label = new LabelBuilder().setLabel('Checkbox Group Label').setCheckboxGroupComponent(checkboxGroup);
expect(() => label.toJSON()).not.toThrowError();
expect(label.toJSON().component).toEqual(checkboxGroup.toJSON());
});
test('LabelBuilder can set RadioGroup component.', () => {
const radioGroup = new RadioGroupBuilder().setCustomId('radio_group').setOptions(fiveRadioOptions);
const label = new LabelBuilder().setLabel('Radio Group Label').setRadioGroupComponent(radioGroup);
expect(() => label.toJSON()).not.toThrowError();
expect(label.toJSON().component).toEqual(radioGroup.toJSON());
});
});

View File

@@ -6,6 +6,10 @@ const selectMenu = () => new StringSelectMenuBuilder();
const selectMenuOption = () => new StringSelectMenuOptionBuilder();
const longStr = 'a'.repeat(256);
const selectMenuOptionLabelAtLimit = 'a'.repeat(100);
const selectMenuOptionLabelAboveLimit = 'a'.repeat(101);
const selectMenuOptionValueAboveLimit = 'a'.repeat(101);
const selectMenuOptionDescriptionAboveLimit = 'a'.repeat(101);
const selectMenuOptionData: APISelectMenuOption = {
label: 'test',
@@ -53,12 +57,12 @@ describe('Select Menu Components', () => {
expect(() => selectMenu().setDisabled()).not.toThrowError();
expect(() => selectMenu().setPlaceholder('description')).not.toThrowError();
const option = selectMenuOption()
.setLabel('test')
.setLabel(selectMenuOptionLabelAtLimit)
.setValue('test')
.setDefault(true)
.setEmoji({ name: 'test' })
.setDescription('description');
expect(() => selectMenu().addOptions(option)).not.toThrowError();
expect(() => selectMenu().setCustomId('customId').addOptions(option).toJSON()).not.toThrowError();
expect(() => selectMenu().setOptions(option)).not.toThrowError();
expect(() => selectMenu().setOptions({ label: 'test', value: 'test' })).not.toThrowError();
expect(() => selectMenu().addOptions([option])).not.toThrowError();
@@ -156,13 +160,13 @@ describe('Select Menu Components', () => {
expect(() => {
selectMenuOption()
.setLabel(longStr)
.setValue(longStr)
.setLabel(selectMenuOptionLabelAboveLimit)
.setValue(selectMenuOptionValueAboveLimit)
// @ts-expect-error: Invalid default value
.setDefault(-1)
// @ts-expect-error: Invalid emoji
.setEmoji({ name: 1 })
.setDescription(longStr);
.setDescription(selectMenuOptionDescriptionAboveLimit);
}).toThrowError();
});

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/builders",
"version": "1.13.0",
"version": "1.13.1",
"description": "A set of builders that you can use when creating your bot",
"scripts": {
"test": "vitest run",
@@ -68,7 +68,7 @@
"@discordjs/formatters": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "^0.38.33",
"discord-api-types": "^0.38.40",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"
@@ -77,17 +77,17 @@
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^16.18.105",
"@types/node": "^16.18.126",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -50,6 +50,9 @@ export const labelValueDescriptionValidator = s
.lengthLessThanOrEqual(100)
.setValidationEnabled(isValidationEnabled);
/**
* @deprecated Replaced with selectMenuStringOptionPredicate.
*/
export const jsonOptionValidator = s
.object({
label: labelValueDescriptionValidator,

View File

@@ -25,8 +25,7 @@ export type AnyAPIActionRowComponent =
*/
export abstract class ComponentBuilder<
DataType extends Partial<APIBaseComponent<ComponentType>> = APIBaseComponent<ComponentType>,
> implements JSONEncodable<AnyAPIActionRowComponent>
{
> implements JSONEncodable<AnyAPIActionRowComponent> {
/**
* The API data associated with this component.
*/

View File

@@ -8,6 +8,9 @@ import {
} from './ActionRow.js';
import { ComponentBuilder } from './Component.js';
import { ButtonBuilder } from './button/Button.js';
import { CheckboxBuilder } from './checkbox/Checkbox.js';
import { CheckboxGroupBuilder } from './checkbox/CheckboxGroup.js';
import { RadioGroupBuilder } from './checkbox/RadioGroup.js';
import { FileUploadBuilder } from './fileUpload/FileUpload.js';
import { LabelBuilder } from './label/Label.js';
import { ChannelSelectMenuBuilder } from './selectMenu/ChannelSelectMenu.js';
@@ -110,6 +113,18 @@ export interface MappedComponentTypes {
* The file upload component type is associated with a {@link FileUploadBuilder}.
*/
[ComponentType.FileUpload]: FileUploadBuilder;
/**
* The checkbox component type is associated with a {@link CheckboxBuilder}.
*/
[ComponentType.Checkbox]: CheckboxBuilder;
/**
* The checkbox group component type is associated with a {@link CheckboxGroupBuilder}.
*/
[ComponentType.CheckboxGroup]: CheckboxGroupBuilder;
/**
* The radio group component type is associated with a {@link RadioGroupBuilder}.
*/
[ComponentType.RadioGroup]: RadioGroupBuilder;
}
/**
@@ -175,6 +190,12 @@ export function createComponentBuilder(
return new LabelBuilder(data);
case ComponentType.FileUpload:
return new FileUploadBuilder(data);
case ComponentType.Checkbox:
return new CheckboxBuilder(data);
case ComponentType.CheckboxGroup:
return new CheckboxGroupBuilder(data);
case ComponentType.RadioGroup:
return new RadioGroupBuilder(data);
default:
// @ts-expect-error This case can still occur if we get a newer unsupported component type
throw new Error(`Cannot properly serialize component type: ${data.type}`);

View File

@@ -0,0 +1,98 @@
import { Result, s } from '@sapphire/shapeshift';
import { ComponentType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation';
import { customIdValidator, idValidator } from '../Assertions';
export const checkboxPredicate = s
.object({
type: s.literal(ComponentType.Checkbox),
custom_id: customIdValidator,
id: idValidator.optional(),
default: s.boolean().optional(),
})
.setValidationEnabled(isValidationEnabled);
export const checkboxGroupOptionPredicate = s
.object({
label: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
value: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
description: s.string().lengthLessThanOrEqual(100).optional(),
default: s.boolean().optional(),
})
.setValidationEnabled(isValidationEnabled);
export const checkboxGroupPredicate = s
.object({
type: s.literal(ComponentType.CheckboxGroup),
custom_id: customIdValidator,
id: idValidator.optional(),
options: s.array(checkboxGroupOptionPredicate).lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(10),
min_values: s.number().int().greaterThanOrEqual(0).lessThanOrEqual(10).optional(),
max_values: s.number().int().greaterThanOrEqual(1).lessThanOrEqual(10).optional(),
required: s.boolean().optional(),
})
.reshape((data) => {
// Ensure min_values is not greater than max_values
if (data.min_values !== undefined && data.max_values !== undefined && data.min_values > data.max_values) {
return Result.err(new RangeError('min_values cannot be greater than max_values'));
}
// Ensure max_values is not greater than the number of options
if (data.max_values !== undefined && data.max_values > data.options.length) {
return Result.err(new RangeError('max_values cannot be greater than the number of options'));
}
// Ensure min_values is not greater than the number of options
if (data.min_values !== undefined && data.min_values > data.options.length) {
return Result.err(new RangeError('min_values cannot be greater than the number of options'));
}
// Ensure required is consistent with min_values
if (data.required === true && data.min_values === 0) {
return Result.err(new RangeError('If required is true, min_values must be at least 1'));
}
// Ensure there are not more default values than max_values
const defaultCount = data.options.filter((option) => option.default === true).length;
if (data.max_values !== undefined && defaultCount > data.max_values) {
return Result.err(new RangeError('The number of default options cannot be greater than max_values'));
}
// Ensure each option's value is unique
const values = data.options.map((option) => option.value);
const uniqueValues = new Set(values);
if (uniqueValues.size !== values.length) {
return Result.err(new RangeError('Each option in a checkbox group must have a unique value'));
}
return Result.ok(data);
})
.setValidationEnabled(isValidationEnabled);
export const radioGroupOptionPredicate = checkboxGroupOptionPredicate;
export const radioGroupPredicate = s
.object({
type: s.literal(ComponentType.RadioGroup),
custom_id: customIdValidator,
id: idValidator.optional(),
options: s.array(radioGroupOptionPredicate).lengthGreaterThanOrEqual(2).lengthLessThanOrEqual(10),
required: s.boolean().optional(),
})
.reshape((data) => {
// Ensure there is exactly one default option
const defaultCount = data.options.filter((option) => option.default === true).length;
if (defaultCount > 1) {
return Result.err(new RangeError('There can be at most one default option in a radio group'));
}
// Ensure each option's value is unique
const values = data.options.map((option) => option.value);
const uniqueValues = new Set(values);
if (uniqueValues.size !== values.length) {
return Result.err(new RangeError('Each option in a radio group must have a unique value'));
}
return Result.ok(data);
})
.setValidationEnabled(isValidationEnabled);

View File

@@ -0,0 +1,63 @@
import type { APICheckboxComponent } from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import { ComponentBuilder } from '../Component';
import { checkboxPredicate } from './Assertions';
/**
* A builder that creates API-compatible JSON data for checkboxes.
*/
export class CheckboxBuilder extends ComponentBuilder<APICheckboxComponent> {
/**
* Creates a new checkbox from API data.
*
* @param data - The API data to create this checkbox with
* @example
* Creating a checkbox from an API data object:
* ```ts
* const checkbox = new CheckboxBuilder({
* custom_id: 'accept_terms',
* default: false,
* });
* ```
* @example
* Creating a checkbox using setters and API data:
* ```ts
* const checkbox = new CheckboxBuilder()
* .setCustomId('subscribe_newsletter')
* .setDefault(true);
* ```
*/
public constructor(data?: Partial<APICheckboxComponent>) {
super({ type: ComponentType.Checkbox, ...data });
}
/**
* Sets the custom id of this checkbox.
*
* @param customId - The custom id to use
*/
public setCustomId(customId: string) {
this.data.custom_id = customId;
return this;
}
/**
* Sets whether this checkbox is checked by default.
*
* @param isDefault - Whether the checkbox should be checked by default
*/
public setDefault(isDefault: boolean) {
this.data.default = isDefault;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public override toJSON(): APICheckboxComponent {
checkboxPredicate.parse(this.data);
return {
...this.data,
} as APICheckboxComponent;
}
}

View File

@@ -0,0 +1,174 @@
import type { APICheckboxGroupComponent, APICheckboxGroupOption } from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import type { RestOrArray } from '../../util/normalizeArray';
import { normalizeArray } from '../../util/normalizeArray';
import { ComponentBuilder } from '../Component';
import { checkboxGroupOptionPredicate, checkboxGroupPredicate } from './Assertions';
import { CheckboxGroupOptionBuilder } from './CheckboxGroupOption';
/**
* A builder that creates API-compatible JSON data for checkbox groups.
*/
export class CheckboxGroupBuilder extends ComponentBuilder<APICheckboxGroupComponent> {
/**
* The options within this checkbox group.
*/
public readonly options: CheckboxGroupOptionBuilder[];
/**
* Creates a new checkbox group from API data.
*
* @param data - The API data to create this checkbox group with
* @example
* Creating a checkbox group from an API data object:
* ```ts
* const checkboxGroup = new CheckboxGroupBuilder({
* custom_id: 'select_options',
* options: [
* { label: 'Option 1', value: 'option_1' },
* { label: 'Option 2', value: 'option_2' },
* ],
* });
* ```
* @example
* Creating a checkbox group using setters and API data:
* ```ts
* const checkboxGroup = new CheckboxGroupBuilder()
* .setCustomId('choose_items')
* .setOptions([
* { label: 'Item A', value: 'item_a' },
* { label: 'Item B', value: 'item_b' },
* ])
* .setMinValues(1)
* .setMaxValues(2);
* ```
*/
public constructor(data?: Partial<APICheckboxGroupComponent>) {
const { options, ...initData } = data ?? {};
super({ ...initData, type: ComponentType.CheckboxGroup });
this.options = options?.map((option: APICheckboxGroupOption) => new CheckboxGroupOptionBuilder(option)) ?? [];
}
/**
* Sets the custom id of this checkbox group.
*
* @param customId - The custom id to use
*/
public setCustomId(customId: string) {
this.data.custom_id = customId;
return this;
}
/**
* Adds options to this checkbox group.
*
* @param options - The options to add
*/
public addOptions(...options: RestOrArray<APICheckboxGroupOption | CheckboxGroupOptionBuilder>) {
const normalizedOptions = normalizeArray(options);
this.options.push(
...normalizedOptions.map((normalizedOption) => {
// I do this because TS' duck typing causes issues,
// if I put in a RadioGroupOption, TS lets it pass but
// it fails to convert to a checkbox group option at runtime
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
const option = new CheckboxGroupOptionBuilder(json);
checkboxGroupOptionPredicate.parse(option.toJSON());
return option;
}),
);
return this;
}
/**
* Sets the options for this checkbox group.
*
* @param options - The options to use
*/
public setOptions(options: RestOrArray<APICheckboxGroupOption | CheckboxGroupOptionBuilder>) {
return this.spliceOptions(0, this.options.length, ...options);
}
/**
* Removes, replaces, or inserts options for this checkbox group.
*
* @remarks
* This method behaves similarly
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
* It's useful for modifying and adjusting the order of existing options.
* @param index - The index to start at
* @param deleteCount - The number of options to remove
* @param options - The replacing option objects or builders
*/
public spliceOptions(
index: number,
deleteCount: number,
...options: RestOrArray<APICheckboxGroupOption | CheckboxGroupOptionBuilder>
) {
const normalizedOptions = normalizeArray(options);
const clone = [...this.options];
clone.splice(
index,
deleteCount,
...normalizedOptions.map((normalizedOption) => {
// I do this because TS' duck typing causes issues,
// if I put in a RadioGroupOption, TS lets it pass but
// it fails to convert to a checkbox group option at runtime
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
const option = new CheckboxGroupOptionBuilder(json);
checkboxGroupOptionPredicate.parse(option.toJSON());
return option;
}),
);
this.options.splice(0, this.options.length, ...clone);
return this;
}
/**
* Sets the minimum number of options that must be selected.
*
* @param minValues - The minimum number of options that must be selected
*/
public setMinValues(minValues: number) {
this.data.min_values = minValues;
return this;
}
/**
* Sets the maximum number of options that can be selected.
*
* @param maxValues - The maximum number of options that can be selected
*/
public setMaxValues(maxValues: number) {
this.data.max_values = maxValues;
return this;
}
/**
* Sets whether selecting options is required.
*
* @param required - Whether selecting options is required
*/
public setRequired(required: boolean) {
this.data.required = required;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public override toJSON(): APICheckboxGroupComponent {
const data = {
...this.data,
options: this.options.map((option) => option.toJSON()),
};
checkboxGroupPredicate.parse(data);
return data as APICheckboxGroupComponent;
}
}

View File

@@ -0,0 +1,81 @@
import type { JSONEncodable } from '@discordjs/util';
import type { APICheckboxGroupOption } from 'discord-api-types/v10';
import { checkboxGroupOptionPredicate } from './Assertions';
/**
* A builder that creates API-compatible JSON data for checkbox group options.
*/
export class CheckboxGroupOptionBuilder implements JSONEncodable<APICheckboxGroupOption> {
/**
* Creates a new checkbox group option from API data.
*
* @param data - The API data to create this checkbox group option with
* @example
* Creating a checkbox group option from an API data object:
* ```ts
* const option = new CheckboxGroupOptionBuilder({
* label: 'Option 1',
* value: 'option_1',
* });
* ```
* @example
* Creating a checkbox group option using setters and API data:
* ```ts
* const option = new CheckboxGroupOptionBuilder()
* .setLabel('Option 2')
* .setValue('option_2');
* ```
*/
public constructor(public data: Partial<APICheckboxGroupOption> = {}) {}
/**
* Sets the label for this option.
*
* @param label - The label to use
*/
public setLabel(label: string) {
this.data.label = label;
return this;
}
/**
* Sets the value for this option.
*
* @param value - The value to use
*/
public setValue(value: string) {
this.data.value = value;
return this;
}
/**
* Sets the description for this option.
*
* @param description - The description to use
*/
public setDescription(description: string) {
this.data.description = description;
return this;
}
/**
* Sets whether this option is selected by default.
*
* @param isDefault - Whether the option should be selected by default
*/
public setDefault(isDefault: boolean) {
this.data.default = isDefault;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APICheckboxGroupOption {
checkboxGroupOptionPredicate.parse(this.data);
return {
...this.data,
} as APICheckboxGroupOption;
}
}

View File

@@ -0,0 +1,152 @@
import type { APIRadioGroupComponent, APIRadioGroupOption } from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import type { RestOrArray } from '../../util/normalizeArray';
import { normalizeArray } from '../../util/normalizeArray';
import { ComponentBuilder } from '../Component';
import { radioGroupOptionPredicate, radioGroupPredicate } from './Assertions';
import { RadioGroupOptionBuilder } from './RadioGroupOption';
/**
* A builder that creates API-compatible JSON data for radio groups.
*/
export class RadioGroupBuilder extends ComponentBuilder<APIRadioGroupComponent> {
/**
* The options within this radio group.
*/
public readonly options: RadioGroupOptionBuilder[];
/**
* Creates a new radio group from API data.
*
* @param data - The API data to create this radio group with
* @example
* Creating a radio group from an API data object:
* ```ts
* const radioGroup = new RadioGroupBuilder({
* custom_id: 'select_options',
* options: [
* { label: 'Option 1', value: 'option_1' },
* { label: 'Option 2', value: 'option_2' },
* ],
* });
* ```
* @example
* Creating a radio group using setters and API data:
* ```ts
* const radioGroup = new RadioGroupBuilder()
* .setCustomId('choose_items')
* .setOptions([
* { label: 'Item A', value: 'item_a' },
* { label: 'Item B', value: 'item_b' },
* ])
* ```
*/
public constructor(data?: Partial<APIRadioGroupComponent>) {
const { options, ...initData } = data ?? {};
super({ ...initData, type: ComponentType.RadioGroup });
this.options = options?.map((option: APIRadioGroupOption) => new RadioGroupOptionBuilder(option)) ?? [];
}
/**
* Sets the custom id of this radio group.
*
* @param customId - The custom id to use
*/
public setCustomId(customId: string) {
this.data.custom_id = customId;
return this;
}
/**
* Adds options to this radio group.
*
* @param options - The options to add
*/
public addOptions(...options: RestOrArray<APIRadioGroupOption | RadioGroupOptionBuilder>) {
const normalizedOptions = normalizeArray(options);
this.options.push(
...normalizedOptions.map((normalizedOption) => {
// I do this because TS' duck typing causes issues,
// if I put in a CheckboxGroupOption, TS lets it pass but
// it fails to convert to a checkbox group option at runtime
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
const option = new RadioGroupOptionBuilder(json);
radioGroupOptionPredicate.parse(option.toJSON());
return option;
}),
);
return this;
}
/**
* Sets the options for this radio group.
*
* @param options - The options to use
*/
public setOptions(options: RestOrArray<APIRadioGroupOption | RadioGroupOptionBuilder>) {
return this.spliceOptions(0, this.options.length, ...options);
}
/**
* Removes, replaces, or inserts options for this radio group.
*
* @remarks
* This method behaves similarly
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
* It's useful for modifying and adjusting the order of existing options.
* @param index - The index to start at
* @param deleteCount - The number of options to remove
* @param options - The replacing option objects or builders
*/
public spliceOptions(
index: number,
deleteCount: number,
...options: RestOrArray<APIRadioGroupOption | RadioGroupOptionBuilder>
) {
const normalizedOptions = normalizeArray(options);
const clone = [...this.options];
clone.splice(
index,
deleteCount,
...normalizedOptions.map((normalizedOption) => {
// I do this because TS' duck typing causes issues,
// if I put in a CheckboxGroupOption, TS lets it pass but
// it fails to convert to a radio group option at runtime
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
const option = new RadioGroupOptionBuilder(json);
radioGroupOptionPredicate.parse(option.toJSON());
return option;
}),
);
this.options.splice(0, this.options.length, ...clone);
return this;
}
/**
* Sets whether selecting options is required.
*
* @param required - Whether selecting options is required
*/
public setRequired(required: boolean) {
this.data.required = required;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public override toJSON(): APIRadioGroupComponent {
const data = {
...this.data,
options: this.options.map((option) => option.toJSON()),
};
radioGroupPredicate.parse(data);
return data as APIRadioGroupComponent;
}
}

View File

@@ -0,0 +1,81 @@
import type { JSONEncodable } from '@discordjs/util';
import type { APIRadioGroupOption } from 'discord-api-types/v10';
import { radioGroupOptionPredicate } from './Assertions';
/**
* A builder that creates API-compatible JSON data for radio group options.
*/
export class RadioGroupOptionBuilder implements JSONEncodable<APIRadioGroupOption> {
/**
* Creates a new radio group option from API data.
*
* @param data - The API data to create this radio group option with
* @example
* Creating a radio group option from an API data object:
* ```ts
* const option = new RadioGroupOptionBuilder({
* label: 'Option 1',
* value: 'option_1',
* });
* ```
* @example
* Creating a radio group option using setters and API data:
* ```ts
* const option = new RadioGroupOptionBuilder()
* .setLabel('Option 2')
* .setValue('option_2');
* ```
*/
public constructor(public data: Partial<APIRadioGroupOption> = {}) {}
/**
* Sets the label for this option.
*
* @param label - The label to use
*/
public setLabel(label: string) {
this.data.label = label;
return this;
}
/**
* Sets the value for this option.
*
* @param value - The value to use
*/
public setValue(value: string) {
this.data.value = value;
return this;
}
/**
* Sets the description for this option.
*
* @param description - The description to use
*/
public setDescription(description: string) {
this.data.description = description;
return this;
}
/**
* Sets whether this option is selected by default.
*
* @param isDefault - Whether the option should be selected by default
*/
public setDefault(isDefault: boolean) {
this.data.default = isDefault;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIRadioGroupOption {
radioGroupOptionPredicate.parse(this.data);
return {
...this.data,
} as APIRadioGroupOption;
}
}

View File

@@ -2,6 +2,7 @@ import { s } from '@sapphire/shapeshift';
import { ComponentType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { idValidator } from '../Assertions.js';
import { checkboxGroupPredicate, checkboxPredicate, radioGroupPredicate } from '../checkbox/Assertions.js';
import { fileUploadPredicate } from '../fileUpload/Assertions.js';
import {
selectMenuChannelPredicate,
@@ -26,6 +27,9 @@ export const labelPredicate = s
selectMenuChannelPredicate,
selectMenuStringPredicate,
fileUploadPredicate,
checkboxPredicate,
checkboxGroupPredicate,
radioGroupPredicate,
]),
})
.setValidationEnabled(isValidationEnabled);

View File

@@ -1,8 +1,11 @@
import type {
APIChannelSelectComponent,
APICheckboxComponent,
APICheckboxGroupComponent,
APIFileUploadComponent,
APILabelComponent,
APIMentionableSelectComponent,
APIRadioGroupComponent,
APIRoleSelectComponent,
APIStringSelectComponent,
APITextInputComponent,
@@ -11,6 +14,9 @@ import type {
import { ComponentType } from 'discord-api-types/v10';
import { ComponentBuilder } from '../Component.js';
import { createComponentBuilder, resolveBuilder } from '../Components.js';
import { CheckboxBuilder } from '../checkbox/Checkbox.js';
import { CheckboxGroupBuilder } from '../checkbox/CheckboxGroup.js';
import { RadioGroupBuilder } from '../checkbox/RadioGroup.js';
import { FileUploadBuilder } from '../fileUpload/FileUpload.js';
import { ChannelSelectMenuBuilder } from '../selectMenu/ChannelSelectMenu.js';
import { MentionableSelectMenuBuilder } from '../selectMenu/MentionableSelectMenu.js';
@@ -23,8 +29,11 @@ import { labelPredicate } from './Assertions.js';
export interface LabelBuilderData extends Partial<Omit<APILabelComponent, 'component'>> {
component?:
| ChannelSelectMenuBuilder
| CheckboxBuilder
| CheckboxGroupBuilder
| FileUploadBuilder
| MentionableSelectMenuBuilder
| RadioGroupBuilder
| RoleSelectMenuBuilder
| StringSelectMenuBuilder
| TextInputBuilder
@@ -194,6 +203,42 @@ export class LabelBuilder extends ComponentBuilder<LabelBuilderData> {
return this;
}
/**
* Sets a checkbox component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setCheckboxComponent(
input: APICheckboxComponent | CheckboxBuilder | ((builder: CheckboxBuilder) => CheckboxBuilder),
): this {
this.data.component = resolveBuilder(input, CheckboxBuilder);
return this;
}
/**
* Sets a checkbox group component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setCheckboxGroupComponent(
input: APICheckboxGroupComponent | CheckboxGroupBuilder | ((builder: CheckboxGroupBuilder) => CheckboxGroupBuilder),
): this {
this.data.component = resolveBuilder(input, CheckboxGroupBuilder);
return this;
}
/**
* Sets a radio group component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setRadioGroupComponent(
input: APIRadioGroupComponent | RadioGroupBuilder | ((builder: RadioGroupBuilder) => RadioGroupBuilder),
): this {
this.data.component = resolveBuilder(input, RadioGroupBuilder);
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/

View File

@@ -2,7 +2,6 @@ import { Result, s } from '@sapphire/shapeshift';
import { ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { customIdValidator, emojiValidator, idValidator } from '../Assertions.js';
import { labelValidator } from '../textInput/Assertions.js';
const selectMenuBasePredicate = s.object({
id: idValidator.optional(),
@@ -63,7 +62,7 @@ export const selectMenuUserPredicate = selectMenuBasePredicate
export const selectMenuStringOptionPredicate = s
.object({
label: labelValidator,
label: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
value: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
description: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100).optional(),
emoji: emojiValidator.optional(),

View File

@@ -1,7 +1,8 @@
import { ComponentType } from 'discord-api-types/v10';
import type { APIStringSelectComponent, APISelectMenuOption } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { jsonOptionValidator, optionsLengthValidator, validateRequiredSelectMenuParameters } from '../Assertions.js';
import { optionsLengthValidator, validateRequiredSelectMenuParameters } from '../Assertions.js';
import { selectMenuStringOptionPredicate } from './Assertions.js';
import { BaseSelectMenuBuilder } from './BaseSelectMenu.js';
import { StringSelectMenuOptionBuilder } from './StringSelectMenuOption.js';
@@ -63,7 +64,7 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
...normalizedOptions.map((normalizedOption) =>
normalizedOption instanceof StringSelectMenuOptionBuilder
? normalizedOption
: new StringSelectMenuOptionBuilder(jsonOptionValidator.parse(normalizedOption)),
: new StringSelectMenuOptionBuilder(selectMenuStringOptionPredicate.parse(normalizedOption)),
),
);
return this;
@@ -83,7 +84,7 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
*
* @remarks
* This method behaves similarly
* to {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice | Array.prototype.splice()}.
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
* It's useful for modifying and adjusting the order of existing options.
* @example
* Remove the first option:
@@ -120,7 +121,7 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
...normalizedOptions.map((normalizedOption) =>
normalizedOption instanceof StringSelectMenuOptionBuilder
? normalizedOption
: new StringSelectMenuOptionBuilder(jsonOptionValidator.parse(normalizedOption)),
: new StringSelectMenuOptionBuilder(selectMenuStringOptionPredicate.parse(normalizedOption)),
),
);

View File

@@ -71,7 +71,8 @@ export class ContainerBuilder extends ComponentBuilder<APIContainerComponent> {
* },
* ],
* })
* .addComponents(separator, section);
* .addSeparatorComponents(separator)
* .addSectionComponents(section);
* ```
*/
public constructor({ components, ...data }: Partial<APIContainerComponent> = {}) {

View File

@@ -31,6 +31,7 @@ export {
*/
StringSelectMenuOptionBuilder as SelectMenuOptionBuilder,
} from './components/selectMenu/StringSelectMenuOption.js';
export * as SelectMenuAssertions from './components/selectMenu/Assertions.js';
export * from './components/selectMenu/StringSelectMenuOption.js';
export * from './components/selectMenu/UserSelectMenu.js';
@@ -50,6 +51,13 @@ export * from './components/v2/Separator.js';
export * from './components/v2/TextDisplay.js';
export * from './components/v2/Thumbnail.js';
export * from './components/checkbox/Checkbox.js';
export * from './components/checkbox/CheckboxGroup.js';
export * from './components/checkbox/CheckboxGroupOption.js';
export * from './components/checkbox/RadioGroup.js';
export * from './components/checkbox/RadioGroupOption.js';
export * as CheckboxAssertions from './components/checkbox/Assertions.js';
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions.js';
export * from './interactions/slashCommands/SlashCommandBuilder.js';
export * from './interactions/slashCommands/SlashCommandSubcommands.js';

View File

@@ -80,7 +80,8 @@ export class SlashCommandBuilder {
}
export interface SlashCommandBuilder
extends SharedNameAndDescription,
extends
SharedNameAndDescription,
SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>,
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder>,
SharedSlashCommand {}
@@ -89,7 +90,8 @@ export interface SlashCommandBuilder
* An interface specifically for slash command subcommands.
*/
export interface SlashCommandSubcommandsOnlyBuilder
extends SharedNameAndDescription,
extends
SharedNameAndDescription,
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder>,
SharedSlashCommand {}
@@ -97,9 +99,7 @@ export interface SlashCommandSubcommandsOnlyBuilder
* An interface specifically for slash command options.
*/
export interface SlashCommandOptionsOnlyBuilder
extends SharedNameAndDescription,
SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>,
SharedSlashCommand {}
extends SharedNameAndDescription, SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>, SharedSlashCommand {}
/**
* An interface that ensures the `toJSON()` call will return something

View File

@@ -127,5 +127,4 @@ export class SlashCommandSubcommandBuilder implements ToAPIApplicationCommandOpt
}
export interface SlashCommandSubcommandBuilder
extends SharedNameAndDescription,
SharedSlashCommandOptions<SlashCommandSubcommandBuilder> {}
extends SharedNameAndDescription, SharedSlashCommandOptions<SlashCommandSubcommandBuilder> {}

View File

@@ -62,6 +62,7 @@ export class SlashCommandIntegerOption
}
export interface SlashCommandIntegerOption
extends ApplicationCommandNumericOptionMinMaxValueMixin,
extends
ApplicationCommandNumericOptionMinMaxValueMixin,
ApplicationCommandOptionWithChoicesMixin<number>,
ApplicationCommandOptionWithAutocompleteMixin {}

View File

@@ -62,6 +62,7 @@ export class SlashCommandNumberOption
}
export interface SlashCommandNumberOption
extends ApplicationCommandNumericOptionMinMaxValueMixin,
extends
ApplicationCommandNumericOptionMinMaxValueMixin,
ApplicationCommandOptionWithChoicesMixin<number>,
ApplicationCommandOptionWithAutocompleteMixin {}

View File

@@ -69,5 +69,4 @@ export class SlashCommandStringOption extends ApplicationCommandOptionBase {
}
export interface SlashCommandStringOption
extends ApplicationCommandOptionWithChoicesMixin<string>,
ApplicationCommandOptionWithAutocompleteMixin {}
extends ApplicationCommandOptionWithChoicesMixin<string>, ApplicationCommandOptionWithAutocompleteMixin {}

View File

@@ -64,17 +64,17 @@
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -68,25 +68,25 @@
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "workspace:^",
"@sapphire/snowflake": "^3.5.3",
"@sapphire/snowflake": "^3.5.5",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.33"
"discord-api-types": "^0.38.40"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -17,8 +17,7 @@ import {
import type { WebhooksAPI } from './webhook.js';
export interface CreateInteractionResponseOptions
extends APIInteractionResponseCallbackData,
RESTPostAPIInteractionCallbackQuery {
extends APIInteractionResponseCallbackData, RESTPostAPIInteractionCallbackQuery {
files?: RawFile[];
}

View File

@@ -58,18 +58,18 @@
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@types/prompts": "^2.4.9",
"@types/validate-npm-package-name": "^4.0.2",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"terser": "^5.31.0",
"tsup": "^8.2.4",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"terser": "^5.46.0",
"tsup": "^8.5.1",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -21,7 +21,7 @@
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.1.0",
"typescript": "~5.5.4",
"typescript": "~5.8.3",
"zod": "^3.23.8"
}
}

View File

@@ -23,7 +23,7 @@
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"typescript": "~5.5.4",
"typescript": "~5.8.3",
"zod": "^3.23.8"
}
}

View File

@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
# [14.25.1](https://github.com/discordjs/discord.js/compare/14.25.0...14.25.1) - (2025-11-21)
## Bug Fixes
- **GuildEmojiManager:** Allow `CreateGuildExpressions` for retrieving author data (#11288) ([0d64ea0](https://github.com/discordjs/discord.js/commit/0d64ea0528b1591ef9b856b9bcad52e88e2cbd05))
# [14.25.0](https://github.com/discordjs/discord.js/compare/14.24.2...14.25.0) - (2025-11-18)
## Bug Fixes

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "discord.js",
"version": "14.25.0",
"version": "14.25.1",
"description": "A powerful library for interacting with the Discord API",
"scripts": {
"test": "pnpm run docs:test && pnpm run test:typescript",
@@ -73,31 +73,31 @@
"@discordjs/util": "workspace:^",
"@discordjs/ws": "^1.2.3",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "^0.38.33",
"discord-api-types": "^0.38.40",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0",
"magic-bytes.js": "^1.13.0",
"tslib": "^2.6.3",
"undici": "6.21.3"
"undici": "6.24.1"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/docgen": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^16.18.105",
"@typescript-eslint/eslint-plugin": "^8.2.0",
"@typescript-eslint/parser": "^8.2.0",
"@types/node": "^16.18.126",
"@typescript-eslint/eslint-plugin": "^8.56.0",
"@typescript-eslint/parser": "^8.56.0",
"cross-env": "^7.0.3",
"dtslint": "4.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-formatter-pretty": "^5.0.0",
"jest": "29.7.0",
"prettier": "^3.3.3",
"prettier": "^3.8.1",
"tsd": "^0.31.1",
"tslint": "6.1.3",
"turbo": "^2.0.14",
"typescript": "~5.5.4"
"turbo": "^2.8.10",
"typescript": "~5.8.3"
},
"engines": {
"node": ">=18"

View File

@@ -11,15 +11,19 @@ const Messages = require('./Messages');
* @ignore
*/
function makeDiscordjsError(Base) {
return class DiscordjsError extends Base {
return class extends Base {
static {
Object.defineProperty(this, 'name', { value: `Discordjs${Base.name}` });
}
constructor(code, ...args) {
super(message(code, args));
this.code = code;
Error.captureStackTrace?.(this, DiscordjsError);
Error.captureStackTrace(this, this.constructor);
}
get name() {
return `${super.name} [${this.code}]`;
return `${this.constructor.name} [${this.code}]`;
}
};
}

View File

@@ -115,7 +115,8 @@ const Messages = {
[DjsErrorCodes.EmojiType]: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
[DjsErrorCodes.EmojiManaged]: 'Emoji is managed and has no Author.',
[DjsErrorCodes.MissingManageGuildExpressionsPermission]: guild =>
`Client must have Manage Guild Expressions permission in guild ${guild} to see emoji authors.`,
// eslint-disable-next-line max-len
`Client must have Create Guild Expressions or Manage Guild Expressions permission in guild ${guild} to see emoji authors.`,
[DjsErrorCodes.MissingManageEmojisAndStickersPermission]: guild =>
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,

View File

@@ -161,7 +161,7 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
const { me } = this.guild.members;
if (!me) throw new DiscordjsError(ErrorCodes.GuildUncachedMe);
if (!me.permissions.has(PermissionFlagsBits.ManageGuildExpressions)) {
if (!me.permissions.any(PermissionFlagsBits.CreateGuildExpressions | PermissionFlagsBits.ManageGuildExpressions)) {
throw new DiscordjsError(ErrorCodes.MissingManageGuildExpressionsPermission, this.guild);
}

View File

@@ -256,7 +256,7 @@ class Invite extends Base {
if (!guild.members.me) throw new DiscordjsError(ErrorCodes.GuildUncachedMe);
return Boolean(
this.channel?.permissionsFor(this.client.user).has(PermissionFlagsBits.ManageChannels, false) ||
guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild),
guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild),
);
}

View File

@@ -711,8 +711,8 @@ class Message extends Base {
get editable() {
const precheck = Boolean(
this.author.id === this.client.user.id &&
(!this.guild || this.channel?.viewable) &&
this.reference?.type !== MessageReferenceType.Forward,
(!this.guild || this.channel?.viewable) &&
this.reference?.type !== MessageReferenceType.Forward,
);
// Regardless of permissions thread messages cannot be edited if
@@ -788,10 +788,7 @@ class Message extends Base {
const permissions = channel?.permissionsFor(this.client.user);
if (!permissions) return false;
return (
permissions.has(PermissionFlagsBits.ReadMessageHistory | PermissionFlagsBits.PinMessages) ||
permissions.has(PermissionFlagsBits.ReadMessageHistory | PermissionFlagsBits.ManageMessages)
);
return permissions.has(PermissionFlagsBits.ReadMessageHistory | PermissionFlagsBits.PinMessages);
}
/**
@@ -820,12 +817,12 @@ class Message extends Base {
const { channel } = this;
return Boolean(
channel?.type === ChannelType.GuildAnnouncement &&
!this.flags.has(MessageFlags.Crossposted) &&
this.reference?.type !== MessageReferenceType.Forward &&
this.type === MessageType.Default &&
!this.poll &&
channel.viewable &&
channel.permissionsFor(this.client.user)?.has(bitfield, false),
!this.flags.has(MessageFlags.Crossposted) &&
this.reference?.type !== MessageReferenceType.Forward &&
this.type === MessageType.Default &&
!this.poll &&
channel.viewable &&
channel.permissionsFor(this.client.user)?.has(bitfield, false),
);
}

View File

@@ -227,6 +227,37 @@ class ModalSubmitFields {
getUploadedFiles(customId, required = false) {
return this._getTypedComponent(customId, [ComponentType.FileUpload], ['attachments'], required).attachments ?? null;
}
/**
* Get radio group component
*
* @param {string} customId The custom id of the component
* @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty
* @returns {?string} The selected radio group option value, or null if none were selected and not required
*/
getRadioGroup(customId, required = false) {
return this._getTypedComponent(customId, [ComponentType.RadioGroup], ['value'], required).value;
}
/**
* Get checkbox group component
*
* @param {string} customId The custom id of the component
* @returns {string[]} The selected checkbox group option values
*/
getCheckboxGroup(customId) {
return this._getTypedComponent(customId, [ComponentType.CheckboxGroup]).values;
}
/**
* Get checkbox component
*
* @param {string} customId The custom id of the component
* @returns {boolean} Whether this checkbox was selected
*/
getCheckbox(customId) {
return this._getTypedComponent(customId, [ComponentType.Checkbox]).value;
}
}
module.exports = ModalSubmitFields;

View File

@@ -24,6 +24,24 @@ const getAttachment = lazy(() => require('./Attachment'));
* @property {Collection<Snowflake, Attachment>} [attachments] The resolved attachments
*/
/**
* @typedef {BaseModalData} RadioGroupModalData
* @property {string} customId The custom id of the radio group
* @property {?string} value The value selected for the radio group
*/
/**
* @typedef {BaseModalData} CheckboxGroupModalData
* @property {string} customId The custom id of the checkbox group
* @property {string[]} values The values selected for the checkbox group
*/
/**
* @typedef {BaseModalData} CheckboxModalData
* @property {string} customId The custom id of the checkbox
* @property {boolean} value Whether this checkbox was selected
*/
/**
* @typedef {BaseModalData} TextInputModalData
* @property {string} customId The custom id of the field
@@ -45,7 +63,8 @@ const getAttachment = lazy(() => require('./Attachment'));
*/
/**
* @typedef {SelectMenuModalData|TextInputModalData|FileUploadModalData} ModalData
* @typedef {SelectMenuModalData|TextInputModalData|FileUploadModalData|RadioGroupModalData|
* CheckboxGroupModalData|CheckboxModalData} ModalData
*/
/**

View File

@@ -24,7 +24,8 @@ const { ComponentType } = require('discord-api-types/v10');
/**
* @typedef {StringSelectMenuComponentData|TextInputComponentData|UserSelectMenuComponentData|
* RoleSelectMenuComponentData|MentionableSelectMenuComponentData|ChannelSelectMenuComponentData|
* FileUploadComponentData} ComponentInLabelData
* FileUploadComponentData|RadioGroupComponentData|CheckboxGroupComponentData|
* CheckboxComponentData} ComponentInLabelData
*/
/**
@@ -52,6 +53,44 @@ const { ComponentType } = require('discord-api-types/v10');
* @property {boolean} [required] Whether this component is required in modals
*/
/**
* @typedef {Object} RadioGroupOption
* @property {string} value The value of the radio group option
* @property {string} label The label to use
* @property {string} [description] The optional description for the radio group option
* @property {boolean} [default] Whether this option is default selected
*/
/**
* @typedef {BaseComponentData} RadioGroupComponentData
* @property {string} customId The custom id of the radio group
* @property {RadioGroupOption[]} options The options in this radio group (2-10)
* @property {boolean} [required] Whether this component is required in modals
*/
/**
* @typedef {Object} CheckboxGroupOption
* @property {string} value The value of the checkbox group option
* @property {string} label The label to use
* @property {string} [description] The optional description for the checkbox group option
* @property {boolean} [default] Whether this option is default selected
*/
/**
* @typedef {BaseComponentData} CheckboxGroupComponentData
* @property {string} customId The custom id of the checkbox group
* @property {CheckboxGroupOption[]} options The options in this checkbox group
* @property {number} [minValues] The minimum number of options that must be selected (0-10)
* @property {number} [maxValues] The maximum number of options that can be selected (defaults to options length)
* @property {boolean} [required] Whether this component is required in modals
*/
/**
* @typedef {BaseComponentData} CheckboxComponentData
* @property {string} customId The custom id of the checkbox
* @property {boolean} [default] Whether this component is default selected in modals
*/
/**
* @typedef {BaseComponentData} BaseSelectMenuComponentData
* @property {string} customId The custom id of the select menu

View File

@@ -340,8 +340,9 @@ export type ActionRowComponentData = MessageActionRowComponentData | ModalAction
export type ActionRowComponent = MessageActionRowComponent | ModalActionRowComponent;
export interface ActionRowData<ComponentType extends JSONEncodable<APIComponentInActionRow> | ActionRowComponentData>
extends BaseComponentData {
export interface ActionRowData<
ComponentType extends JSONEncodable<APIComponentInActionRow> | ActionRowComponentData,
> extends BaseComponentData {
components: readonly ComponentType[];
}
@@ -362,6 +363,9 @@ export class ActionRowBuilder<
}
export type ComponentInLabelData =
| CheckboxComponentData
| CheckboxGroupComponentData
| RadioGroupComponentData
| StringSelectMenuComponentData
| TextInputComponentData
| UserSelectMenuComponentData
@@ -1097,8 +1101,7 @@ type AsyncEventIteratorDisposability =
ReturnType<typeof EventEmitter.on> extends AsyncDisposable ? AsyncDisposable : {};
/** @internal */
interface AsyncEventIterator<Params extends any[]>
extends AsyncIterableIterator<Params>,
AsyncEventIteratorDisposability {
extends AsyncIterableIterator<Params>, AsyncEventIteratorDisposability {
[Symbol.asyncIterator](): AsyncEventIterator<Params>;
}
@@ -1504,11 +1507,10 @@ export class PrimaryEntryPointCommandInteraction<
}
// tslint:disable-next-line no-empty-interface
export interface DMChannel
extends Omit<
TextBasedChannelFields<false, true>,
'bulkDelete' | 'fetchWebhooks' | 'createWebhook' | 'setRateLimitPerUser' | 'setNSFW'
> {}
export interface DMChannel extends Omit<
TextBasedChannelFields<false, true>,
'bulkDelete' | 'fetchWebhooks' | 'createWebhook' | 'setRateLimitPerUser' | 'setNSFW'
> {}
export class DMChannel extends BaseChannel {
private constructor(client: Client<true>, data?: RawDMChannelData);
public flags: Readonly<ChannelFlagsBitField>;
@@ -2393,9 +2395,9 @@ export interface AwaitMessageCollectorOptionsParams<
ComponentType extends MessageComponentType,
Cached extends boolean = boolean,
> extends Pick<
InteractionCollectorOptions<MappedInteractionTypes<Cached>[ComponentType]>,
keyof AwaitMessageComponentOptions<any>
> {
InteractionCollectorOptions<MappedInteractionTypes<Cached>[ComponentType]>,
keyof AwaitMessageComponentOptions<any>
> {
componentType?: ComponentType;
}
@@ -2805,14 +2807,13 @@ export interface TextInputModalData extends BaseModalData<ComponentType.TextInpu
value: string;
}
export interface SelectMenuModalData<Cached extends CacheType = CacheType>
extends BaseModalData<
| ComponentType.ChannelSelect
| ComponentType.MentionableSelect
| ComponentType.RoleSelect
| ComponentType.StringSelect
| ComponentType.UserSelect
> {
export interface SelectMenuModalData<Cached extends CacheType = CacheType> extends BaseModalData<
| ComponentType.ChannelSelect
| ComponentType.MentionableSelect
| ComponentType.RoleSelect
| ComponentType.StringSelect
| ComponentType.UserSelect
> {
channels?: ReadonlyCollection<
Snowflake,
CacheTypeReducer<Cached, GuildBasedChannel, APIInteractionDataResolvedChannel>
@@ -2830,7 +2831,28 @@ export interface FileUploadModalData extends BaseModalData<ComponentType.FileUpl
attachments: ReadonlyCollection<Snowflake, Attachment>;
}
export type ModalData = FileUploadModalData | SelectMenuModalData | TextInputModalData;
export interface RadioGroupModalData extends BaseModalData<ComponentType.RadioGroup> {
customId: string;
value: string | null;
}
export interface CheckboxGroupModalData extends BaseModalData<ComponentType.CheckboxGroup> {
customId: string;
values: readonly string[];
}
export interface CheckboxModalData extends BaseModalData<ComponentType.Checkbox> {
customId: string;
value: boolean;
}
export type ModalData =
| CheckboxGroupModalData
| CheckboxModalData
| FileUploadModalData
| RadioGroupModalData
| SelectMenuModalData
| TextInputModalData;
export interface LabelModalData extends BaseModalData<ComponentType.Label> {
component: ModalData;
@@ -2909,10 +2931,15 @@ export class ModalSubmitFields<Cached extends CacheType = CacheType> {
public getSelectedMentionables(customId: string, required?: boolean): ModalSelectedMentionables<Cached> | null;
public getUploadedFiles(customId: string, required: true): ReadonlyCollection<Snowflake, Attachment>;
public getUploadedFiles(customId: string, required?: boolean): ReadonlyCollection<Snowflake, Attachment> | null;
public getRadioGroup(customId: string, required: true): string;
public getRadioGroup(customId: string, required?: boolean): string | null;
public getCheckboxGroup(customId: string): readonly string[];
public getCheckbox(customId: string): boolean;
}
export interface ModalMessageModalSubmitInteraction<Cached extends CacheType = CacheType>
extends ModalSubmitInteraction<Cached> {
export interface ModalMessageModalSubmitInteraction<
Cached extends CacheType = CacheType,
> extends ModalSubmitInteraction<Cached> {
message: Message<BooleanCache<Cached>>;
channelId: Snowflake;
update(
@@ -2998,19 +3025,18 @@ export class OAuth2Guild extends BaseGuild {
public permissions: Readonly<PermissionsBitField>;
}
export interface PartialGroupDMChannel
extends Omit<
TextBasedChannelFields<false, false>,
| 'bulkDelete'
| 'send'
| 'sendTyping'
| 'createMessageCollector'
| 'awaitMessages'
| 'fetchWebhooks'
| 'createWebhook'
| 'setRateLimitPerUser'
| 'setNSFW'
> {}
export interface PartialGroupDMChannel extends Omit<
TextBasedChannelFields<false, false>,
| 'bulkDelete'
| 'send'
| 'sendTyping'
| 'createMessageCollector'
| 'awaitMessages'
| 'fetchWebhooks'
| 'createWebhook'
| 'setRateLimitPerUser'
| 'setNSFW'
> {}
export class PartialGroupDMChannel extends BaseChannel {
private constructor(client: Client<true>, data: RawPartialGroupDMChannelData);
public type: ChannelType.GroupDM;
@@ -3045,20 +3071,19 @@ export interface DefaultReactionEmoji {
name: string | null;
}
export interface ThreadOnlyChannel
extends Omit<
TextBasedChannelFields,
| 'send'
| 'lastMessage'
| 'lastPinAt'
| 'bulkDelete'
| 'sendTyping'
| 'createMessageCollector'
| 'awaitMessages'
| 'createMessageComponentCollector'
| 'awaitMessageComponent'
| 'messages'
> {}
export interface ThreadOnlyChannel extends Omit<
TextBasedChannelFields,
| 'send'
| 'lastMessage'
| 'lastPinAt'
| 'bulkDelete'
| 'sendTyping'
| 'createMessageCollector'
| 'awaitMessages'
| 'createMessageComponentCollector'
| 'awaitMessageComponent'
| 'messages'
> {}
export abstract class ThreadOnlyChannel extends GuildChannel {
public type: ChannelType.GuildForum | ChannelType.GuildMedia;
public threads: GuildForumThreadManager;
@@ -3518,17 +3543,17 @@ export class ShardClientUtil {
public mode: ShardingManagerMode;
public parentPort: MessagePort | null;
public broadcastEval<Result>(fn: (client: Client) => Awaitable<Result>): Promise<Serialized<Result>[]>;
public broadcastEval<Result>(
fn: (client: Client) => Awaitable<Result>,
options: { shard: number },
public broadcastEval<Result, Context>(
fn: (client: Client<true>, context: Serialized<Context>) => Awaitable<Result>,
options: { context: Context; shard: number },
): Promise<Serialized<Result>>;
public broadcastEval<Result, Context>(
fn: (client: Client<true>, context: Serialized<Context>) => Awaitable<Result>,
options: { context: Context },
): Promise<Serialized<Result>[]>;
public broadcastEval<Result, Context>(
fn: (client: Client<true>, context: Serialized<Context>) => Awaitable<Result>,
options: { context: Context; shard: number },
public broadcastEval<Result>(
fn: (client: Client) => Awaitable<Result>,
options: { shard: number },
): Promise<Serialized<Result>>;
public fetchClientValues(prop: string): Promise<unknown[]>;
public fetchClientValues(prop: string, shard: number): Promise<unknown>;
@@ -3554,17 +3579,17 @@ export class ShardingManager extends EventEmitter {
public shardList: number[] | 'auto';
public broadcast(message: unknown): Promise<Shard[]>;
public broadcastEval<Result>(fn: (client: Client) => Awaitable<Result>): Promise<Serialized<Result>[]>;
public broadcastEval<Result>(
fn: (client: Client) => Awaitable<Result>,
options: { shard: number },
public broadcastEval<Result, Context>(
fn: (client: Client<true>, context: Serialized<Context>) => Awaitable<Result>,
options: { context: Context; shard: number },
): Promise<Serialized<Result>>;
public broadcastEval<Result, Context>(
fn: (client: Client<true>, context: Serialized<Context>) => Awaitable<Result>,
options: { context: Context },
): Promise<Serialized<Result>[]>;
public broadcastEval<Result, Context>(
fn: (client: Client<true>, context: Serialized<Context>) => Awaitable<Result>,
options: { context: Context; shard: number },
public broadcastEval<Result>(
fn: (client: Client) => Awaitable<Result>,
options: { shard: number },
): Promise<Serialized<Result>>;
public createShard(id: number): Shard;
public fetchClientValues(prop: string): Promise<unknown[]>;
@@ -3838,8 +3863,10 @@ export interface PrivateThreadChannel extends ThreadChannel<false> {
}
// tslint:disable-next-line no-empty-interface
export interface ThreadChannel<ThreadOnly extends boolean = boolean>
extends Omit<TextBasedChannelFields<true>, 'fetchWebhooks' | 'createWebhook' | 'setNSFW'> {}
export interface ThreadChannel<ThreadOnly extends boolean = boolean> extends Omit<
TextBasedChannelFields<true>,
'fetchWebhooks' | 'createWebhook' | 'setNSFW'
> {}
export class ThreadChannel<ThreadOnly extends boolean = boolean> extends BaseChannel {
private constructor(guild: Guild, data: RawThreadChannelData, client?: Client<true>);
public archived: boolean | null;
@@ -5381,8 +5408,10 @@ export interface PartialTextBasedChannelFields<InGuild extends boolean = boolean
send(options: string | MessagePayload | MessageCreateOptions): Promise<Message<InGuild>>;
}
export interface TextBasedChannelFields<InGuild extends boolean = boolean, InDM extends boolean = boolean>
extends PartialTextBasedChannelFields<InGuild> {
export interface TextBasedChannelFields<
InGuild extends boolean = boolean,
InDM extends boolean = boolean,
> extends PartialTextBasedChannelFields<InGuild> {
lastMessageId: Snowflake | null;
get lastMessage(): Message | null;
lastPinTimestamp: number | null;
@@ -5583,24 +5612,30 @@ export interface ApplicationCommandAttachmentOption extends BaseApplicationComma
type: ApplicationCommandOptionType.Attachment;
}
export interface ApplicationCommandAutocompleteNumericOption
extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
export interface ApplicationCommandAutocompleteNumericOption extends Omit<
BaseApplicationCommandOptionsData,
'autocomplete'
> {
type: CommandOptionNumericResolvableType;
minValue?: number;
maxValue?: number;
autocomplete: true;
}
export interface ApplicationCommandAutocompleteStringOption
extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
export interface ApplicationCommandAutocompleteStringOption extends Omit<
BaseApplicationCommandOptionsData,
'autocomplete'
> {
type: ApplicationCommandOptionType.String;
minLength?: number;
maxLength?: number;
autocomplete: true;
}
export interface ApplicationCommandAutocompleteNumericOptionData
extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
export interface ApplicationCommandAutocompleteNumericOptionData extends Omit<
BaseApplicationCommandOptionsData,
'autocomplete'
> {
type: CommandOptionNumericResolvableType;
minValue?: number;
min_value?: number;
@@ -5609,8 +5644,10 @@ export interface ApplicationCommandAutocompleteNumericOptionData
autocomplete: true;
}
export interface ApplicationCommandAutocompleteStringOptionData
extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
export interface ApplicationCommandAutocompleteStringOptionData extends Omit<
BaseApplicationCommandOptionsData,
'autocomplete'
> {
type: ApplicationCommandOptionType.String;
minLength?: number;
min_length?: number;
@@ -5619,15 +5656,19 @@ export interface ApplicationCommandAutocompleteStringOptionData
autocomplete: true;
}
export interface ApplicationCommandChoicesData<Type extends string | number = string | number>
extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
export interface ApplicationCommandChoicesData<Type extends string | number = string | number> extends Omit<
BaseApplicationCommandOptionsData,
'autocomplete'
> {
type: CommandOptionChoiceResolvableType;
choices?: readonly ApplicationCommandOptionChoiceData<Type>[];
autocomplete?: false;
}
export interface ApplicationCommandChoicesOption<Type extends string | number = string | number>
extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
export interface ApplicationCommandChoicesOption<Type extends string | number = string | number> extends Omit<
BaseApplicationCommandOptionsData,
'autocomplete'
> {
type: CommandOptionChoiceResolvableType;
choices?: readonly ApplicationCommandOptionChoiceData<Type>[];
autocomplete?: false;
@@ -5805,14 +5846,20 @@ export interface AutoModerationTriggerMetadata {
mentionRaidProtectionEnabled: boolean;
}
export interface AwaitMessageComponentOptions<Interaction extends CollectedMessageInteraction>
extends Omit<MessageComponentCollectorOptions<Interaction>, 'max' | 'maxComponents' | 'maxUsers'> {}
export interface AwaitMessageComponentOptions<Interaction extends CollectedMessageInteraction> extends Omit<
MessageComponentCollectorOptions<Interaction>,
'max' | 'maxComponents' | 'maxUsers'
> {}
export interface ModalSubmitInteractionCollectorOptions<Interaction extends ModalSubmitInteraction>
extends Omit<InteractionCollectorOptions<Interaction>, 'channel' | 'message' | 'guild' | 'interactionType'> {}
export interface ModalSubmitInteractionCollectorOptions<Interaction extends ModalSubmitInteraction> extends Omit<
InteractionCollectorOptions<Interaction>,
'channel' | 'message' | 'guild' | 'interactionType'
> {}
export interface AwaitModalSubmitOptions<Interaction extends ModalSubmitInteraction>
extends Omit<ModalSubmitInteractionCollectorOptions<Interaction>, 'max' | 'maxComponents' | 'maxUsers'> {
export interface AwaitModalSubmitOptions<Interaction extends ModalSubmitInteraction> extends Omit<
ModalSubmitInteractionCollectorOptions<Interaction>,
'max' | 'maxComponents' | 'maxUsers'
> {
time: number;
}
@@ -6200,8 +6247,9 @@ export interface BaseInteractionResolvedData<Cached extends CacheType = CacheTyp
users?: ReadonlyCollection<Snowflake, User>;
}
export interface CommandInteractionResolvedData<Cached extends CacheType = CacheType>
extends BaseInteractionResolvedData<Cached> {
export interface CommandInteractionResolvedData<
Cached extends CacheType = CacheType,
> extends BaseInteractionResolvedData<Cached> {
messages?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Message, APIMessage>>;
}
@@ -7240,11 +7288,15 @@ export type CollectedMessageInteraction<Cached extends CacheType = CacheType> =
ModalSubmitInteraction
>;
export interface MessageComponentCollectorOptions<Interaction extends CollectedMessageInteraction>
extends Omit<InteractionCollectorOptions<Interaction>, 'channel' | 'message' | 'guild' | 'interactionType'> {}
export interface MessageComponentCollectorOptions<Interaction extends CollectedMessageInteraction> extends Omit<
InteractionCollectorOptions<Interaction>,
'channel' | 'message' | 'guild' | 'interactionType'
> {}
export interface MessageChannelComponentCollectorOptions<Interaction extends CollectedMessageInteraction>
extends Omit<InteractionCollectorOptions<Interaction>, 'channel' | 'guild' | 'interactionType'> {}
export interface MessageChannelComponentCollectorOptions<Interaction extends CollectedMessageInteraction> extends Omit<
InteractionCollectorOptions<Interaction>,
'channel' | 'guild' | 'interactionType'
> {}
export interface MessageInteractionMetadata {
id: Snowflake;
@@ -7280,25 +7332,24 @@ export interface MessageMentionOptions {
export type MessageMentionTypes = 'roles' | 'users' | 'everyone';
export interface MessageSnapshot
extends Partialize<
Message,
null,
Exclude<
keyof Message,
| 'attachments'
| 'client'
| 'components'
| 'content'
| 'createdTimestamp'
| 'editedTimestamp'
| 'embeds'
| 'flags'
| 'mentions'
| 'stickers'
| 'type'
>
> {}
export interface MessageSnapshot extends Partialize<
Message,
null,
Exclude<
keyof Message,
| 'attachments'
| 'client'
| 'components'
| 'content'
| 'createdTimestamp'
| 'editedTimestamp'
| 'embeds'
| 'flags'
| 'mentions'
| 'stickers'
| 'type'
>
> {}
export interface BaseMessageOptions {
content?: string;
@@ -7340,8 +7391,7 @@ export interface MessageCreateOptions extends BaseMessageOptionsWithPoll {
}
export interface GuildForumThreadMessageCreateOptions
extends BaseMessageOptions,
Pick<MessageCreateOptions, 'flags' | 'stickers'> {}
extends BaseMessageOptions, Pick<MessageCreateOptions, 'flags' | 'stickers'> {}
export interface MessageEditAttachmentData {
id: Snowflake;
@@ -7447,6 +7497,40 @@ export interface FileUploadComponentData extends BaseComponentData {
type: ComponentType.FileUpload;
}
export interface RadioGroupOption {
default?: boolean;
description?: string;
label: string;
value: string;
}
export interface RadioGroupComponentData extends BaseComponentData {
customId: string;
options: readonly RadioGroupOption[];
required?: boolean;
type: ComponentType.RadioGroup;
}
export interface CheckboxGroupOption {
default?: boolean;
description?: string;
label: string;
value: string;
}
export interface CheckboxGroupComponentData extends BaseComponentData {
customId: string;
maxValues?: number;
minValues?: number;
options: readonly CheckboxGroupOption[];
required?: boolean;
type: ComponentType.CheckboxGroup;
}
export interface CheckboxComponentData extends BaseComponentData {
customId: string;
default?: boolean;
type: ComponentType.Checkbox;
}
export type MessageTarget =
| Interaction
| InteractionWebhook
@@ -7549,18 +7633,20 @@ export interface PartialDMChannel extends Partialize<DMChannel, null, null, 'las
export interface PartialGuildMember extends Partialize<GuildMember, 'joinedAt' | 'joinedTimestamp' | 'pending'> {}
export interface PartialMessage<InGuild extends boolean = boolean>
extends Partialize<Message<InGuild>, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> {}
export interface PartialMessage<InGuild extends boolean = boolean> extends Partialize<
Message<InGuild>,
'type' | 'system' | 'pinned' | 'tts',
'content' | 'cleanContent' | 'author'
> {}
export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
export interface PartialPoll
extends Partialize<
Poll,
'allowMultiselect' | 'layoutType' | 'expiresTimestamp',
null,
'question' | 'message' | 'answers'
> {
export interface PartialPoll extends Partialize<
Poll,
'allowMultiselect' | 'layoutType' | 'expiresTimestamp',
null,
'question' | 'message' | 'answers'
> {
question: { text: null };
message: PartialMessage;
// eslint-disable-next-line no-restricted-syntax
@@ -7571,8 +7657,11 @@ export interface PartialPollAnswer extends Partialize<PollAnswer, 'emoji' | 'tex
readonly poll: PartialPoll;
}
export interface PartialGuildScheduledEvent
extends Partialize<GuildScheduledEvent, 'userCount', 'status' | 'privacyLevel' | 'name' | 'entityType'> {}
export interface PartialGuildScheduledEvent extends Partialize<
GuildScheduledEvent,
'userCount',
'status' | 'privacyLevel' | 'name' | 'entityType'
> {}
export interface PartialThreadMember extends Partialize<ThreadMember, 'flags' | 'joinedAt' | 'joinedTimestamp'> {}
@@ -7912,8 +8001,7 @@ export interface WebhookMessageEditOptions extends MessageEditOptions {
}
export interface InteractionEditReplyOptions
extends WebhookMessageEditOptions,
Pick<BaseMessageOptionsWithPoll, 'poll'> {
extends WebhookMessageEditOptions, Pick<BaseMessageOptionsWithPoll, 'poll'> {
message?: MessageResolvable | '@original';
}
@@ -7921,8 +8009,10 @@ export interface WebhookFetchMessageOptions {
threadId?: Snowflake;
}
export interface WebhookMessageCreateOptions
extends Omit<MessageCreateOptions, 'nonce' | 'reply' | 'stickers' | 'forward'> {
export interface WebhookMessageCreateOptions extends Omit<
MessageCreateOptions,
'nonce' | 'reply' | 'stickers' | 'forward'
> {
username?: string;
avatarURL?: string;
threadId?: Snowflake;

View File

@@ -68,15 +68,15 @@
"devDependencies": {
"@favware/cliff-jumper": "^4.1.0",
"@types/jsdoc-to-markdown": "^7.0.6",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4"
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3"
},
"engines": {
"node": ">=18"

View File

@@ -55,23 +55,23 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"discord-api-types": "^0.38.33"
"discord-api-types": "^0.38.40"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^16.18.105",
"@types/node": "^16.18.126",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -697,7 +697,6 @@ export function email<Email extends string>(
*/
export function email<Email extends string>(email: Email, headers?: Record<string, string | readonly string[]>) {
if (headers) {
// eslint-disable-next-line n/prefer-global/url-search-params
const searchParams = new URLSearchParams(
Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value])),
);

View File

@@ -72,23 +72,23 @@
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "workspace:^",
"discord-api-types": "^0.38.33"
"discord-api-types": "^0.38.40"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -50,15 +50,15 @@
"tslib": "^2.6.3"
},
"devDependencies": {
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4"
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3"
},
"engines": {
"node": ">=18"

View File

@@ -68,25 +68,25 @@
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"tslib": "^2.6.3",
"undici": "6.21.1"
"undici": "6.24.1"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@types/supertest": "^6.0.2",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"supertest": "^7.0.0",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"supertest": "^7.2.2",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -2,6 +2,8 @@
All notable changes to this project will be documented in this file.
# [@discordjs/rest@2.6.1](https://github.com/discordjs/discord.js/compare/@discordjs/rest@2.6.0...@discordjs/rest@2.6.1) - (2026-03-22)
# [@discordjs/rest@2.6.0](https://github.com/discordjs/discord.js/compare/@discordjs/rest@2.5.1...@discordjs/rest@2.6.0) - (2025-08-20)
## Documentation

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/rest",
"version": "2.6.0",
"version": "2.6.1",
"description": "The REST API for discord.js",
"scripts": {
"test": "vitest run",
@@ -86,12 +86,12 @@
"@discordjs/collection": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@sapphire/snowflake": "^3.5.5",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.33",
"magic-bytes.js": "^1.10.0",
"discord-api-types": "^0.38.40",
"magic-bytes.js": "^1.13.0",
"tslib": "^2.6.3",
"undici": "6.21.3"
"undici": "6.24.1"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
@@ -101,12 +101,12 @@
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.5.4",
"vitest": "^2.0.5"
},

View File

@@ -229,8 +229,10 @@ export interface APIRequest {
route: string;
}
export interface ResponseLike
extends Pick<Response, 'arrayBuffer' | 'bodyUsed' | 'headers' | 'json' | 'ok' | 'status' | 'statusText' | 'text'> {
export interface ResponseLike extends Pick<
Response,
'arrayBuffer' | 'bodyUsed' | 'headers' | 'json' | 'ok' | 'status' | 'statusText' | 'text'
> {
body: Readable | ReadableStream | null;
}

View File

@@ -59,7 +59,7 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@actions/glob": "^0.5.0",
"@actions/glob": "^0.5.1",
"@discordjs/api-extractor-model": "workspace:^",
"@discordjs/api-extractor-utils": "workspace:^",
"@microsoft/tsdoc": "0.14.2",
@@ -68,22 +68,22 @@
"@vercel/postgres": "^0.9.0",
"commander": "^12.1.0",
"tslib": "^2.6.3",
"undici": "6.21.1",
"yaml": "^2.5.0"
"undici": "6.24.1",
"yaml": "^2.8.2"
},
"devDependencies": {
"@turbo/gen": "^2.0.14",
"@types/node": "^18.19.45",
"@turbo/gen": "^2.8.10",
"@types/node": "^18.19.130",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"env-cmd": "^10.1.0",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -69,7 +69,7 @@
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -63,14 +63,14 @@
"@react-icons/all-files": "^4.1.0",
"@storybook/addon-essentials": "^8.1.5",
"@storybook/addon-interactions": "^8.1.5",
"@storybook/addon-links": "^8.1.5",
"@storybook/addon-links": "^8.6.17",
"@storybook/addon-styling": "^1.3.7",
"@storybook/blocks": "^8.1.5",
"@storybook/react": "^8.1.5",
"@storybook/react-vite": "^8.1.5",
"@storybook/react": "^8.6.17",
"@storybook/react-vite": "^8.6.17",
"@storybook/testing-library": "^0.2.2",
"@types/node": "^18.19.45",
"@types/react": "^18.3.3",
"@types/node": "^18.19.130",
"@types/react": "^18.3.28",
"@types/react-dom": "^18.3.0",
"@unocss/eslint-plugin": "^0.60.4",
"@unocss/reset": "^0.60.4",
@@ -78,14 +78,14 @@
"@vitest/coverage-v8": "^2.0.5",
"chromatic": "^11.5.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"prettier": "^3.8.1",
"prop-types": "^15.8.1",
"storybook": "^8.1.5",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"storybook": "^8.6.17",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"unocss": "^0.60.4",
"vite": "^5.2.12",
"vite-plugin-dts": "^3.9.1",

View File

@@ -62,24 +62,24 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"discord-api-types": "^0.38.33"
"discord-api-types": "^0.38.40"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^16.18.105",
"@types/node": "^16.18.126",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"prettier": "^3.8.1",
"tsd": "^0.31.1",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"vitest": "^2.0.5"
},
"engines": {

View File

@@ -64,14 +64,14 @@
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@types/ws": "^8.5.12",
"discord-api-types": "^0.38.33",
"discord-api-types": "^0.38.40",
"prism-media": "^1.3.5",
"tslib": "^2.6.3",
"ws": "^8.18.0"
"ws": "^8.19.0"
},
"devDependencies": {
"@babel/core": "^7.24.6",
"@babel/preset-env": "^7.24.6",
"@babel/core": "^7.29.0",
"@babel/preset-env": "^7.29.0",
"@babel/preset-typescript": "^7.24.6",
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
@@ -80,17 +80,17 @@
"@types/node": "^18.19.130",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"jest": "^29.7.0",
"jest-websocket-mock": "^2.5.0",
"mock-socket": "^9.3.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"prettier": "^3.8.1",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"tweetnacl": "^1.0.3",
"typescript": "~5.5.4"
"typescript": "~5.8.3"
},
"engines": {
"node": ">=18"

View File

@@ -79,28 +79,28 @@
"@sapphire/async-queue": "^1.5.3",
"@types/ws": "^8.5.12",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.33",
"discord-api-types": "^0.38.40",
"tslib": "^2.6.3",
"ws": "^8.18.0"
"ws": "^8.19.0"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^4.1.0",
"@types/node": "^18.19.45",
"@types/node": "^18.19.130",
"@vitest/coverage-v8": "^2.0.5",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"mock-socket": "^9.3.1",
"prettier": "^3.3.3",
"prettier": "^3.8.1",
"tsd": "^0.31.1",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"undici": "6.21.3",
"tsup": "^8.5.1",
"turbo": "^2.8.10",
"typescript": "~5.8.3",
"undici": "6.24.1",
"vitest": "^2.0.5",
"zlib-sync": "^0.1.10"
},

View File

@@ -2,22 +2,21 @@ import type { Awaitable } from '@discordjs/util';
import type { APIGatewayBotInfo } from 'discord-api-types/v10';
import type { SessionInfo, WebSocketManager, WebSocketManagerOptions } from '../../ws/WebSocketManager.js';
export interface FetchingStrategyOptions
extends Pick<
WebSocketManagerOptions,
| 'compression'
| 'encoding'
| 'handshakeTimeout'
| 'helloTimeout'
| 'identifyProperties'
| 'initialPresence'
| 'intents'
| 'largeThreshold'
| 'readyTimeout'
| 'token'
| 'useIdentifyCompression'
| 'version'
> {
export interface FetchingStrategyOptions extends Pick<
WebSocketManagerOptions,
| 'compression'
| 'encoding'
| 'handshakeTimeout'
| 'helloTimeout'
| 'identifyProperties'
| 'initialPresence'
| 'intents'
| 'largeThreshold'
| 'readyTimeout'
| 'token'
| 'useIdentifyCompression'
| 'version'
> {
readonly gatewayInformation: APIGatewayBotInfo;
readonly shardCount: number;
}

View File

@@ -195,8 +195,7 @@ export interface OptionalWebSocketManagerOptions {
export interface WebSocketManagerOptions extends OptionalWebSocketManagerOptions, RequiredWebSocketManagerOptions {}
export interface CreateWebSocketManagerOptions
extends Partial<OptionalWebSocketManagerOptions>,
RequiredWebSocketManagerOptions {}
extends Partial<OptionalWebSocketManagerOptions>, RequiredWebSocketManagerOptions {}
export interface ManagerShardEventsMap {
[WebSocketShardEvents.Closed]: [code: number, shardId: number];

8662
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff