Merge branch 'main' into feat/v-c-s-e

This commit is contained in:
Jiralite 2024-07-01 15:31:20 +01:00 committed by GitHub
commit b17a18b57f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 6658 additions and 4902 deletions

View file

@ -5,7 +5,7 @@
"type-enum": [
2,
"always",
["chore", "build", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test", "types", "typings"]
["chore", "build", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test", "types"]
],
"scope-case": [0]
}

View file

@ -7,7 +7,7 @@
Messages must be matched by the following regex:
```js
/^(revert: )?(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\(.+\))?: .{1,72}/;
/^(revert: )?(feat|fix|docs|style|refactor|perf|test|build|ci|chore|types)(\(.+\))?: .{1,72}/;
```
#### Examples

80
.github/labeler.yml vendored
View file

@ -1,60 +1,100 @@
apps:guide:
- changed-files:
- any-glob-to-any-file: ['apps/guide/*', 'apps/guide/**/*']
- any-glob-to-any-file:
- apps/guide/*
- apps/guide/**/*
apps:website:
- changed-files:
- any-glob-to-any-file: ['apps/website/*', 'apps/website/**/*']
- any-glob-to-any-file:
- apps/website/*
- apps/website/**/*
packages:api-extractor:
- changed-files:
- any-glob-to-any-file: ['packages/api-extractor/*', 'packages/api-extractor/**/*']
- any-glob-to-any-file:
- packages/api-extractor/*
- packages/api-extractor/**/*
packages:api-extractor-model:
- changed-files:
- any-glob-to-any-file: ['packages/api-extractor-model/*', 'packages/api-extractor-model/**/*']
- any-glob-to-any-file:
- packages/api-extractor-model/*
- packages/api-extractor-model/**/*
packages:brokers:
- changed-files:
- any-glob-to-any-file: ['packages/brokers/*', 'packages/brokers/**/*']
- any-glob-to-any-file:
- packages/brokers/*
- packages/brokers/**/*
packages:builders:
- changed-files:
- any-glob-to-any-file: ['packages/builders/*', 'packages/builders/**/*']
- any-glob-to-any-file:
- packages/builders/*
- packages/builders/**/*
packages:collection:
- changed-files:
- any-glob-to-any-file: ['packages/collection/*', 'packages/collection/**/*']
- any-glob-to-any-file:
- packages/collection/*
- packages/collection/**/*
packages:core:
- changed-files:
- any-glob-to-any-file: ['packages/core/*', 'packages/core/**/*']
- any-glob-to-any-file:
- packages/core/*
- packages/core/**/*
packages:create-discord-bot:
- changed-files:
- any-glob-to-any-file: ['packages/create-discord-bot/*', 'packages/create-discord-bot/**/*']
- any-glob-to-any-file:
- packages/create-discord-bot/*
- packages/create-discord-bot/**/*
packages:discord.js:
- changed-files:
- any-glob-to-any-file: ['packages/discord.js/*', 'packages/discord.js/**/*']
- any-glob-to-any-file:
- packages/discord.js/*
- packages/discord.js/**/*
packages:docgen:
- changed-files:
- any-glob-to-any-file: ['packages/docgen/*', 'packages/docgen/**/*']
- any-glob-to-any-file:
- packages/docgen/*
- packages/docgen/**/*
packages:formatters:
- changed-files:
- any-glob-to-any-file: ['packages/formatters/*', 'packages/formatters/**/*']
- any-glob-to-any-file:
- packages/formatters/*
- packages/formatters/**/*
packages:next:
- changed-files:
- any-glob-to-any-file: ['packages/next/*', 'packages/next/**/*']
- any-glob-to-any-file:
- packages/next/*
- packages/next/**/*
packages:proxy:
- changed-files:
- any-glob-to-any-file: ['packages/proxy/*', 'packages/proxy/**/*']
- any-glob-to-any-file:
- packages/proxy/*
- packages/proxy/**/*
packages:proxy-container:
- changed-files:
- any-glob-to-any-file: ['packages/proxy-container/*', 'packages/proxy-container/**/*']
- any-glob-to-any-file:
- packages/proxy-container/*
- packages/proxy-container/**/*
packages:rest:
- changed-files:
- any-glob-to-any-file: ['packages/rest/*', 'packages/rest/**/*']
- any-glob-to-any-file:
- packages/rest/*
- packages/rest/**/*
packages:ui:
- changed-files:
- any-glob-to-any-file: ['packages/ui/*', 'packages/ui/**/*']
- any-glob-to-any-file:
- packages/ui/*
- packages/ui/**/*
packages:util:
- changed-files:
- any-glob-to-any-file: ['packages/util/*', 'packages/util/**/*']
- any-glob-to-any-file:
- packages/util/*
- packages/util/**/*
packages:voice:
- changed-files:
- any-glob-to-any-file: ['packages/voice/*', 'packages/voice/**/*']
- any-glob-to-any-file:
- packages/voice/*
- packages/voice/**/*
packages:ws:
- changed-files:
- any-glob-to-any-file: ['packages/ws/*', 'packages/ws/**/*']
- any-glob-to-any-file:
- packages/ws/*
- packages/ws/**/*

View file

@ -1,13 +1,35 @@
name: 'PR Triage'
on:
pull_request_target:
types:
- opened
- edited
- reopened
- synchronize
jobs:
pr-triage:
name: PR Triage
label:
name: Label
if: github.event.action != 'edited'
runs-on: ubuntu-latest
steps:
- name: Automatically label PR
- name: Label pull request
uses: actions/labeler@v5
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
sync-labels: true
validate-title:
name: Validate title
if: github.event.action != 'synchronize'
runs-on: ubuntu-latest
steps:
- name: Validate pull request title
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
REGEX="^(revert: )?(feat|fix|docs|style|refactor|perf|test|build|ci|chore|types)(\\(.+\\))?: .{1,72}$"
echo "Title: \"$TITLE\""
if [[ ! "$TITLE" =~ $REGEX ]]; then
exit 1
fi

View file

@ -48,8 +48,8 @@
"@code-hike/mdx": "^0.9.0",
"@discordjs/ui": "workspace:^",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^1.2.2",
"@vercel/edge-config": "^1.1.0",
"@vercel/analytics": "^1.3.1",
"@vercel/edge-config": "^1.1.1",
"@vercel/og": "^0.6.2",
"ariakit": "2.0.0-next.44",
"cmdk": "^1.0.0",
@ -72,26 +72,26 @@
"@types/node": "18.18.8",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@unocss/eslint-plugin": "^0.60.2",
"@unocss/postcss": "^0.60.2",
"@unocss/reset": "^0.60.2",
"@vitejs/plugin-react": "^4.2.1",
"@unocss/eslint-plugin": "^0.60.4",
"@unocss/postcss": "^0.60.4",
"@unocss/reset": "^0.60.4",
"@vitejs/plugin-react": "^4.3.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"happy-dom": "^14.11.0",
"happy-dom": "^14.12.0",
"hast-util-to-string": "^2.0.0",
"hastscript": "^8.0.0",
"html-escaper": "^3.0.3",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"unocss": "^0.60.2",
"vercel": "^34.0.0",
"vitest": "^1.5.0"
"unocss": "^0.60.4",
"vercel": "^34.2.4",
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -134,8 +134,8 @@ collector.on('end', (collected) => {
### Await reactions
<DocsLink type="class" parent="Message" symbol="awaitReactions" brackets /> works almost the same as a reaction collector,
except it is Promise-based. The same differences apply as with channel collectors.
<DocsLink type="class" parent="Message" symbol="awaitReactions" brackets /> works almost the same as a reaction
collector, except it is Promise-based. The same differences apply as with channel collectors.
```js
const collectorFilter = (reaction, user) => {

View file

@ -158,21 +158,25 @@ Various _`create()`_ and _`edit()`_ methods on managers and objects have had the
- <DocsLink type="class" parent="Role" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="Sticker" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="ThreadChannel" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="create" brackets /> now takes _`name`_ in the _`options`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="create" brackets /> now takes _`name`_ in the _`options`_
parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="createWebhook" brackets /> (and other text-based channels)
now takes _`channel`_ and _`name`_ in the _`options`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildChannelManager" symbol="edit" brackets /> now takes _`reason`_ as a part of
_`data`_
- <DocsLink type="class" parent="GuildEmojiManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildManager" symbol="create" brackets /> now takes _`name`_ as a part of _`options`_
- <DocsLink type="class" parent="GuildMemberManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildMemberManager" symbol="edit" brackets /> now takes _`reason`_ as a part of
_`data`_
- <DocsLink type="class" parent="GuildMember" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="edit" brackets /> now takes _`reason`_ as a part of
_`data`_
- <DocsLink type="class" parent="RoleManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`options`_
- <DocsLink type="class" parent="Webhook" symbol="edit" brackets /> now takes _`reason`_ as a part of _`options`_
- <DocsLink type="class" parent="GuildEmojiManager" symbol="create" brackets /> now takes _`attachment`_ and _`name`_ as
a part of _`options`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="create" brackets /> now takes _`file`_, _`name`_, and _`tags`_
as a part of _`options`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="create" brackets /> now takes _`file`_, _`name`_, and
_`tags`_ as a part of _`options`_
### Activity
@ -236,9 +240,10 @@ Dynamic URLs use <DocsLink package="rest" type="Interface" parent="ImageURLOptio
### CategoryChannel
<DocsLink type="class" parent="CategoryChannel" symbol="children" /> is no longer a _`Collection`_ of channels the category
contains. It is now a <DocsLink type="class" parent="CategoryChannelChildManager" />. This also means
_`CategoryChannel#createChannel()`_ has been moved to the <DocsLink type="class" parent="CategoryChannelChildManager" />.
<DocsLink type="class" parent="CategoryChannel" symbol="children" /> is no longer a _`Collection`_ of channels the
category contains. It is now a <DocsLink type="class" parent="CategoryChannelChildManager" />. This also means
_`CategoryChannel#createChannel()`_ has been moved to the <DocsLink type="class" parent="CategoryChannelChildManager" />
.
### Channel
@ -262,8 +267,8 @@ The _`restWsBridgeTimeout`_ client option has been removed.
### CommandInteractionOptionResolver
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getMember" brackets /> no longer has a parameter
for _`required`_.[^1]
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getMember" brackets /> no longer has a
parameter for _`required`_.[^1]
### Constants
@ -357,7 +362,8 @@ The following properties & methods have been moved to the <DocsLink type="class"
### GuildMember
<DocsLink type="class" parent="GuildMember" symbol="pending" /> is now nullable to account for partial guild members.[^4]
<DocsLink type="class" parent="GuildMember" symbol="pending" /> is now nullable to account for partial guild
members.[^4]
### IntegrationApplication
@ -582,8 +588,8 @@ _`Role.comparePositions()`_ has been removed. Use <DocsLink type="class" parent=
### Sticker
<DocsLink type="class" parent="Sticker" symbol="tags" /> is now a nullable string (_`string | null`_). Previously, it was
a nullable array of strings (_`string[] | null`_).[^5]
<DocsLink type="class" parent="Sticker" symbol="tags" /> is now a nullable string (_`string | null`_). Previously, it
was a nullable array of strings (_`string[] | null`_).[^5]
### ThreadChannel
@ -668,8 +674,8 @@ Added support for <DocsLink type="class" parent="BaseChannel" symbol="flags" />.
Store channels have been removed as they are no longer part of the API.
<DocsLink type="class" parent="BaseChannel" symbol="url" /> has been added which is a link to a channel, just like in the
client.
<DocsLink type="class" parent="BaseChannel" symbol="url" /> has been added which is a link to a channel, just like in
the client.
Additionally, new typeguards have been added:
@ -713,13 +719,13 @@ Component collector options now use the <DiscordAPITypesLink type="enum" parent=
### CommandInteraction
<DocsLink type="class" parent="CommandInteraction" symbol="commandGuildId" /> has been added which is the id of the guild
the invoked application command is registered to.
<DocsLink type="class" parent="CommandInteraction" symbol="commandGuildId" /> has been added which is the id of the
guild the invoked application command is registered to.
### CommandInteractionOptionResolver
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getChannel" brackets /> now has a third parameter
which narrows the channel type.
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getChannel" brackets /> now has a third
parameter which narrows the channel type.
### Events
@ -814,9 +820,15 @@ Added the _`threadName`_ property in <DocsLink type="typedef" parent="WebhookMes
discord.js uses <DocsLink package="ws" /> internally.
[^1]: https://github.com/discordjs/discord.js/pull/7188
[^2]: https://github.com/discordjs/discord.js/pull/6492
[^3]: https://github.com/discordjs/discord.js/pull/7669
[^4]: https://github.com/discordjs/discord.js/issues/6546
[^5]: https://github.com/discordjs/discord.js/pull/8010
[^6]: https://github.com/discordjs/discord.js/issues/7091
[^7]: https://github.com/discord/discord-api-docs/pull/6017

View file

@ -49,37 +49,37 @@
"dependencies": {
"@radix-ui/react-collapsible": "^1.0.3",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^1.3.0",
"@vercel/analytics": "^1.3.1",
"@vercel/blob": "^0.23.3",
"@vercel/edge-config": "^1.1.1",
"@vercel/og": "^0.6.2",
"@vercel/postgres": "^0.8.0",
"cmdk": "^1.0.0",
"geist": "^1.3.0",
"jotai": "^2.8.1",
"jotai": "^2.8.2",
"lucide-react": "^0.379.0",
"meilisearch": "^0.40.0",
"next": "^15.0.0-rc.0",
"next-mdx-remote-client": "^1.0.3",
"next-themes": "^0.3.0",
"overlayscrollbars": "^2.8.2",
"overlayscrollbars": "^2.8.3",
"overlayscrollbars-react": "^0.5.6",
"react": "^19.0.0-rc-4c2e457c7c-20240522",
"react": "19.0.0-rc-f994737d14-20240522",
"react-aria-components": "^1.2.1",
"react-dom": "^19.0.0-rc-4c2e457c7c-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522",
"sharp": "^0.33.4",
"usehooks-ts": "^3.1.0",
"vaul": "^0.9.1"
},
"devDependencies": {
"@shikijs/rehype": "^1.6.0",
"@shikijs/rehype": "^1.6.2",
"@tailwindcss/typography": "^0.5.13",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^14.5.2",
"@types/node": "18.18.8",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.2.1",
"@vitejs/plugin-react": "^4.3.0",
"@vitest/coverage-v8": "^1.6.0",
"autoprefixer": "^10.4.19",
"babel-plugin-react-compiler": "0.0.0-experimental-592953e-20240517",
@ -88,18 +88,18 @@
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"happy-dom": "^14.11.0",
"happy-dom": "^14.12.0",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"prettier": "^3.3.0",
"prettier-plugin-tailwindcss": "^0.5.14",
"remark-gfm": "^4.0.0",
"remark-rehype": "^11.1.0",
"shiki": "^1.6.0",
"shiki": "^1.6.2",
"tailwindcss": "^3.4.3",
"turbo": "^1.13.2",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vercel": "^34.2.2",
"vitest": "^1.5.0"
"vercel": "^34.2.4",
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -30,7 +30,7 @@ export async function DocNode({ node, version }: { readonly node?: any; readonly
rel="external noreferrer noopener"
target="_blank"
>
{`${node.text}${node.members}`}
{`${node.text}${node.members ?? ''}`}
</a>
);
}

View file

@ -50,28 +50,28 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"devDependencies": {
"@commitlint/cli": "^19.2.2",
"@commitlint/config-angular": "^19.2.2",
"@favware/cliff-jumper": "^3.0.2",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-angular": "^19.3.0",
"@favware/cliff-jumper": "^3.0.3",
"@favware/npm-deprecate": "^1.0.7",
"@types/lodash.merge": "^4.6.9",
"@unocss/eslint-plugin": "^0.59.3",
"@vitest/coverage-v8": "^1.5.0",
"@unocss/eslint-plugin": "^0.59.4",
"@vitest/coverage-v8": "^1.6.0",
"conventional-changelog-cli": "^4.1.0",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"husky": "^9.0.11",
"is-ci": "^3.0.1",
"lint-staged": "^15.2.2",
"lint-staged": "^15.2.5",
"lodash.merge": "^4.6.2",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"typescript-eslint": "^7.7.0",
"unocss": "^0.60.2",
"vercel": "^34.0.0",
"vitest": "^1.5.0"
"typescript-eslint": "^7.11.0",
"unocss": "^0.60.4",
"vercel": "^34.2.4",
"vitest": "^1.6.0"
},
"pnpm": {
"peerDependencyRules": {
@ -97,5 +97,5 @@
"engines": {
"node": ">=18"
},
"packageManager": "pnpm@9.1.1"
"packageManager": "pnpm@9.1.4"
}

View file

@ -49,20 +49,20 @@
"meilisearch": "^0.38.0",
"p-limit": "^5.0.0",
"tslib": "^2.6.2",
"undici": "6.13.0"
"undici": "6.18.2"
},
"devDependencies": {
"@types/node": "18.18.8",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -11,6 +11,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./apps/guide/coverage/cobertura-coverage.xml
disable_search: true
flags: guide
token: ${{ inputs.CODECOV_TOKEN }}
@ -18,6 +19,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./apps/website/coverage/cobertura-coverage.xml
disable_search: true
flags: website
token: ${{ inputs.CODECOV_TOKEN }}
@ -25,6 +27,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/brokers/coverage/cobertura-coverage.xml
disable_search: true
flags: brokers
token: ${{ inputs.CODECOV_TOKEN }}
@ -32,6 +35,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/builders/coverage/cobertura-coverage.xml
disable_search: true
flags: builders
token: ${{ inputs.CODECOV_TOKEN }}
@ -39,6 +43,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/collection/coverage/cobertura-coverage.xml
disable_search: true
flags: collection
token: ${{ inputs.CODECOV_TOKEN }}
@ -46,6 +51,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/discord.js/coverage/cobertura-coverage.xml
disable_search: true
flags: discord.js
token: ${{ inputs.CODECOV_TOKEN }}
@ -53,6 +59,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/formatters/coverage/cobertura-coverage.xml
disable_search: true
flags: formatters
token: ${{ inputs.CODECOV_TOKEN }}
@ -60,6 +67,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/next/coverage/cobertura-coverage.xml
disable_search: true
flags: next
token: ${{ inputs.CODECOV_TOKEN }}
@ -67,6 +75,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/proxy/coverage/cobertura-coverage.xml
disable_search: true
flags: proxy
token: ${{ inputs.CODECOV_TOKEN }}
@ -74,6 +83,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/rest/coverage/cobertura-coverage.xml
disable_search: true
flags: rest
token: ${{ inputs.CODECOV_TOKEN }}
@ -81,6 +91,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/voice/coverage/cobertura-coverage.xml
disable_search: true
flags: voice
token: ${{ inputs.CODECOV_TOKEN }}
@ -88,6 +99,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/ws/coverage/cobertura-coverage.xml
disable_search: true
flags: ws
token: ${{ inputs.CODECOV_TOKEN }}
@ -95,6 +107,7 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/util/coverage/cobertura-coverage.xml
disable_search: true
flags: util
token: ${{ inputs.CODECOV_TOKEN }}
@ -102,5 +115,6 @@ runs:
uses: codecov/codecov-action@v4
with:
files: ./packages/actions/coverage/cobertura-coverage.xml, ./packages/scripts/coverage/cobertura-coverage.xml
disable_search: true
flags: utilities
token: ${{ inputs.CODECOV_TOKEN }}

View file

@ -37,14 +37,14 @@
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/node": "^18.19.22",
"@types/node": "^18.19.33",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2"
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3"
}
}

View file

@ -115,6 +115,7 @@ export class ModelReferenceResolver {
if (memberSelector === undefined) {
if (foundMembers.length > 1) {
const foundClass: ApiItem | undefined = foundMembers.find((member) => member.kind === ApiItemKind.Class);
const foundEvent: ApiItem | undefined = foundMembers.find((member) => member.kind === ApiItemKind.Event);
if (
foundClass &&
foundMembers.filter((member) => member.kind === ApiItemKind.Interface).length === foundMembers.length - 1
@ -124,6 +125,11 @@ export class ModelReferenceResolver {
foundMembers.every((member) => member.kind === ApiItemKind.Method && (member as ApiMethod).overloadIndex)
) {
currentItem = foundMembers.find((member) => (member as ApiMethod).overloadIndex === 1)!;
} else if (
foundEvent &&
foundMembers.filter((member) => member.kind === ApiItemKind.Method).length === foundMembers.length - 1
) {
currentItem = foundEvent;
} else {
result.errorMessage = `The member reference ${JSON.stringify(identifier)} was ambiguous`;
return result;

View file

@ -55,9 +55,9 @@
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5"
},
"engines": {

View file

@ -65,8 +65,8 @@
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.0",
"@types/node": "^18.19.22",
"@types/lodash": "^4.17.4",
"@types/node": "^18.19.33",
"@types/resolve": "^1.20.6",
"@types/semver": "^7.5.8",
"cpy-cli": "^5.0.0",
@ -75,8 +75,8 @@
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2"
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3"
}
}

View file

@ -69,24 +69,24 @@
"dependencies": {
"@msgpack/msgpack": "^3.0.0-beta2",
"@vladfrangu/async_event_emitter": "^2.2.4",
"ioredis": "^5.3.2"
"ioredis": "^5.4.1"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.18.8",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -1,4 +1,4 @@
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { ApplicationIntegrationType, InteractionContextType, PermissionFlagsBits } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { ContextMenuCommandAssertions, ContextMenuCommandBuilder } from '../../src/index.js';
@ -144,5 +144,51 @@ describe('Context Menu Commands', () => {
expect(() => getBuilder().setDefaultMemberPermissions(1.1)).toThrowError();
});
});
describe('contexts', () => {
test('GIVEN a builder with valid contexts THEN does not throw an error', () => {
expect(() =>
getBuilder().setContexts([InteractionContextType.Guild, InteractionContextType.BotDM]),
).not.toThrowError();
expect(() =>
getBuilder().setContexts(InteractionContextType.Guild, InteractionContextType.BotDM),
).not.toThrowError();
});
test('GIVEN a builder with invalid contexts THEN does throw an error', () => {
// @ts-expect-error: Invalid contexts
expect(() => getBuilder().setContexts(999)).toThrowError();
// @ts-expect-error: Invalid contexts
expect(() => getBuilder().setContexts([999, 998])).toThrowError();
});
});
describe('integration types', () => {
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
expect(() =>
getBuilder().setIntegrationTypes([
ApplicationIntegrationType.GuildInstall,
ApplicationIntegrationType.UserInstall,
]),
).not.toThrowError();
expect(() =>
getBuilder().setIntegrationTypes(
ApplicationIntegrationType.GuildInstall,
ApplicationIntegrationType.UserInstall,
),
).not.toThrowError();
});
test('GIVEN a builder with invalid integration types THEN does throw an error', () => {
// @ts-expect-error: Invalid integration types
expect(() => getBuilder().setIntegrationTypes(999)).toThrowError();
// @ts-expect-error: Invalid integration types
expect(() => getBuilder().setIntegrationTypes([999, 998])).toThrowError();
});
});
});
});

View file

@ -1,4 +1,10 @@
import { ChannelType, PermissionFlagsBits, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import {
ApplicationIntegrationType,
ChannelType,
InteractionContextType,
PermissionFlagsBits,
type APIApplicationCommandOptionChoice,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
SlashCommandAssertions,
@ -532,5 +538,51 @@ describe('Slash Commands', () => {
expect(() => getBuilder().addChannelOption(getChannelOption()).setDMPermission(false)).not.toThrowError();
});
});
describe('contexts', () => {
test('GIVEN a builder with valid contexts THEN does not throw an error', () => {
expect(() =>
getBuilder().setContexts([InteractionContextType.Guild, InteractionContextType.BotDM]),
).not.toThrowError();
expect(() =>
getBuilder().setContexts(InteractionContextType.Guild, InteractionContextType.BotDM),
).not.toThrowError();
});
test('GIVEN a builder with invalid contexts THEN does throw an error', () => {
// @ts-expect-error: Invalid contexts
expect(() => getBuilder().setContexts(999)).toThrowError();
// @ts-expect-error: Invalid contexts
expect(() => getBuilder().setContexts([999, 998])).toThrowError();
});
});
describe('integration types', () => {
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
expect(() =>
getBuilder().setIntegrationTypes([
ApplicationIntegrationType.GuildInstall,
ApplicationIntegrationType.UserInstall,
]),
).not.toThrowError();
expect(() =>
getBuilder().setIntegrationTypes(
ApplicationIntegrationType.GuildInstall,
ApplicationIntegrationType.UserInstall,
),
).not.toThrowError();
});
test('GIVEN a builder with invalid integration types THEN does throw an error', () => {
// @ts-expect-error: Invalid integration types
expect(() => getBuilder().setIntegrationTypes(999)).toThrowError();
// @ts-expect-error: Invalid integration types
expect(() => getBuilder().setIntegrationTypes([999, 998])).toThrowError();
});
});
});
});

View file

@ -68,7 +68,7 @@
"@discordjs/formatters": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/shapeshift": "^3.9.7",
"discord-api-types": "0.37.83",
"discord-api-types": "0.37.90",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.2"
@ -76,19 +76,19 @@
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "16.18.60",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=16.11.0"

View file

@ -1,9 +1,10 @@
import {
ComponentType,
type APIMessageComponentEmoji,
type APIButtonComponent,
type APIButtonComponentWithURL,
type APIButtonComponentWithCustomId,
type APIButtonComponentWithSKUId,
type APIButtonComponentWithURL,
type APIMessageComponentEmoji,
type ButtonStyle,
} from 'discord-api-types/v10';
import {
@ -94,7 +95,7 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
* @param emoji - The emoji to use
*/
public setEmoji(emoji: APIMessageComponentEmoji) {
this.data.emoji = emojiValidator.parse(emoji);
(this.data as Exclude<APIButtonComponent, APIButtonComponentWithSKUId>).emoji = emojiValidator.parse(emoji);
return this;
}
@ -114,7 +115,7 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
* @param label - The label to use
*/
public setLabel(label: string) {
this.data.label = buttonLabelValidator.parse(label);
(this.data as Exclude<APIButtonComponent, APIButtonComponentWithSKUId>).label = buttonLabelValidator.parse(label);
return this;
}
@ -124,8 +125,8 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
public toJSON(): APIButtonComponent {
validateRequiredButtonParameters(
this.data.style,
this.data.label,
this.data.emoji,
(this.data as Exclude<APIButtonComponent, APIButtonComponentWithSKUId>).label,
(this.data as Exclude<APIButtonComponent, APIButtonComponentWithSKUId>).emoji,
(this.data as APIButtonComponentWithCustomId).custom_id,
(this.data as APIButtonComponentWithURL).url,
);

View file

@ -1,5 +1,5 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandType } from 'discord-api-types/v10';
import { ApplicationCommandType, ApplicationIntegrationType, InteractionContextType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder.js';
@ -49,3 +49,11 @@ const memberPermissionPredicate = s.union(
export function validateDefaultMemberPermissions(permissions: unknown) {
return memberPermissionPredicate.parse(permissions);
}
export const contextsPredicate = s.array(
s.nativeEnum(InteractionContextType).setValidationEnabled(isValidationEnabled),
);
export const integrationTypesPredicate = s.array(
s.nativeEnum(ApplicationIntegrationType).setValidationEnabled(isValidationEnabled),
);

View file

@ -1,10 +1,14 @@
import type {
ApplicationCommandType,
ApplicationIntegrationType,
InteractionContextType,
LocaleString,
LocalizationMap,
Permissions,
RESTPostAPIContextMenuApplicationCommandsJSONBody,
} from 'discord-api-types/v10';
import type { RestOrArray } from '../../util/normalizeArray.js';
import { normalizeArray } from '../../util/normalizeArray.js';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions.js';
import {
validateRequiredParameters,
@ -13,6 +17,8 @@ import {
validateDefaultPermission,
validateDefaultMemberPermissions,
validateDMPermission,
contextsPredicate,
integrationTypesPredicate,
} from './Assertions.js';
/**
@ -39,6 +45,11 @@ export class ContextMenuCommandBuilder {
*/
public readonly type: ContextMenuCommandType = undefined!;
/**
* The contexts for this command.
*/
public readonly contexts?: InteractionContextType[];
/**
* Whether this command is enabled by default when the application is added to a guild.
*
@ -59,6 +70,33 @@ export class ContextMenuCommandBuilder {
*/
public readonly dm_permission: boolean | undefined = undefined;
/**
* The integration types for this command.
*/
public readonly integration_types?: ApplicationIntegrationType[];
/**
* Sets the contexts of this command.
*
* @param contexts - The contexts
*/
public setContexts(...contexts: RestOrArray<InteractionContextType>) {
Reflect.set(this, 'contexts', contextsPredicate.parse(normalizeArray(contexts)));
return this;
}
/**
* Sets integration types of this command.
*
* @param integrationTypes - The integration types
*/
public setIntegrationTypes(...integrationTypes: RestOrArray<ApplicationIntegrationType>) {
Reflect.set(this, 'integration_types', integrationTypesPredicate.parse(normalizeArray(integrationTypes)));
return this;
}
/**
* Sets the name of this command.
*

View file

@ -1,5 +1,11 @@
import { s } from '@sapphire/shapeshift';
import { Locale, type APIApplicationCommandOptionChoice, type LocalizationMap } from 'discord-api-types/v10';
import {
ApplicationIntegrationType,
InteractionContextType,
Locale,
type APIApplicationCommandOptionChoice,
type LocalizationMap,
} from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder.js';
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands.js';
@ -98,3 +104,11 @@ export function validateDefaultMemberPermissions(permissions: unknown) {
export function validateNSFW(value: unknown): asserts value is boolean {
booleanPredicate.parse(value);
}
export const contextsPredicate = s.array(
s.nativeEnum(InteractionContextType).setValidationEnabled(isValidationEnabled),
);
export const integrationTypesPredicate = s.array(
s.nativeEnum(ApplicationIntegrationType).setValidationEnabled(isValidationEnabled),
);

View file

@ -1,4 +1,10 @@
import type { APIApplicationCommandOption, LocalizationMap, Permissions } from 'discord-api-types/v10';
import type {
APIApplicationCommandOption,
ApplicationIntegrationType,
InteractionContextType,
LocalizationMap,
Permissions,
} from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { SharedNameAndDescription } from './mixins/NameAndDescription.js';
import { SharedSlashCommand } from './mixins/SharedSlashCommand.js';
@ -35,6 +41,11 @@ export class SlashCommandBuilder {
*/
public readonly options: ToAPIApplicationCommandOptions[] = [];
/**
* The contexts for this command.
*/
public readonly contexts?: InteractionContextType[];
/**
* Whether this command is enabled by default when the application is added to a guild.
*
@ -55,6 +66,11 @@ export class SlashCommandBuilder {
*/
public readonly dm_permission: boolean | undefined = undefined;
/**
* The integration types for this command.
*/
public readonly integration_types?: ApplicationIntegrationType[];
/**
* Whether this command is NSFW.
*/

View file

@ -1,9 +1,15 @@
import type {
ApplicationIntegrationType,
InteractionContextType,
LocalizationMap,
Permissions,
RESTPostAPIChatInputApplicationCommandsJSONBody,
} from 'discord-api-types/v10';
import type { RestOrArray } from '../../../util/normalizeArray.js';
import { normalizeArray } from '../../../util/normalizeArray.js';
import {
contextsPredicate,
integrationTypesPredicate,
validateDMPermission,
validateDefaultMemberPermissions,
validateDefaultPermission,
@ -27,6 +33,8 @@ export class SharedSlashCommand {
public readonly options: ToAPIApplicationCommandOptions[] = [];
public readonly contexts?: InteractionContextType[];
/**
* @deprecated Use {@link SharedSlashCommand.setDefaultMemberPermissions} or {@link SharedSlashCommand.setDMPermission} instead.
*/
@ -36,8 +44,32 @@ export class SharedSlashCommand {
public readonly dm_permission: boolean | undefined = undefined;
public readonly integration_types?: ApplicationIntegrationType[];
public readonly nsfw: boolean | undefined = undefined;
/**
* Sets the contexts of this command.
*
* @param contexts - The contexts
*/
public setContexts(...contexts: RestOrArray<InteractionContextType>) {
Reflect.set(this, 'contexts', contextsPredicate.parse(normalizeArray(contexts)));
return this;
}
/**
* Sets the integration types of this command.
*
* @param integrationTypes - The integration types
*/
public setIntegrationTypes(...integrationTypes: RestOrArray<ApplicationIntegrationType>) {
Reflect.set(this, 'integration_types', integrationTypesPredicate.parse(normalizeArray(integrationTypes)));
return this;
}
/**
* Sets whether the command is enabled by default when the application is added to a guild.
*

View file

@ -63,19 +63,19 @@
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.18.8",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -70,24 +70,24 @@
"@discordjs/ws": "workspace:^",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "0.37.83"
"discord-api-types": "0.37.90"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.18.8",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -17,7 +17,7 @@ export class StageInstancesAPI {
/**
* Creates a new stage instance
*
* @see {@link https://discord.com/developers/docs/resources/stage-instance#get-stage-instance}
* @see {@link https://discord.com/developers/docs/resources/stage-instance#create-stage-instance}
* @param body - The data for creating the new stage instance
* @param options - The options for creating the new stage instance
*/

View file

@ -49,28 +49,28 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"commander": "^12.0.0",
"commander": "^12.1.0",
"fast-glob": "^3.3.2",
"picocolors": "^1.0.0",
"picocolors": "^1.0.1",
"prompts": "^2.4.2",
"validate-npm-package-name": "^5.0.0"
"validate-npm-package-name": "^5.0.1"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.18.8",
"@types/prompts": "^2.4.9",
"@types/validate-npm-package-name": "^4.0.2",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"terser": "^5.29.1",
"tsup": "^8.0.2",
"prettier": "^3.3.0",
"terser": "^5.31.0",
"tsup": "^8.1.0",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -72,11 +72,11 @@
"@discordjs/util": "workspace:^",
"@discordjs/ws": "workspace:^",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "0.37.83",
"discord-api-types": "0.37.90",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"tslib": "2.6.2",
"undici": "6.13.0"
"undici": "6.18.2"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
@ -84,17 +84,17 @@
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "3.0.2",
"@types/node": "16.18.60",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.11.0",
"cross-env": "^7.0.3",
"dtslint": "4.2.1",
"eslint": "8.57.0",
"eslint-formatter-pretty": "5.0.0",
"jest": "29.7.0",
"prettier": "3.2.5",
"tsd": "0.30.7",
"tsd": "0.31.0",
"tslint": "6.1.3",
"turbo": "^1.13.2",
"turbo": "^1.13.3",
"typescript": "5.4.5"
},
"engines": {

View file

@ -117,14 +117,14 @@ class WebSocketManager extends EventEmitter {
/**
* Emits a debug message.
* @param {string} message The debug message
* @param {string[]} messages The debug message
* @param {?number} [shardId] The id of the shard that emitted this message, if any
* @private
*/
debug(message, shardId) {
debug(messages, shardId) {
this.client.emit(
Events.Debug,
`[WS => ${typeof shardId === 'number' ? `Shard ${shardId}` : 'Manager'}] ${message}`,
`[WS => ${typeof shardId === 'number' ? `Shard ${shardId}` : 'Manager'}] ${messages.join('\n\t')}`,
);
}
@ -170,15 +170,8 @@ class WebSocketManager extends EventEmitter {
});
const { total, remaining } = sessionStartLimit;
this.debug(`Fetched Gateway Information
URL: ${gatewayURL}
Recommended Shards: ${recommendedShards}`);
this.debug(`Session Limit Information
Total: ${total}
Remaining: ${remaining}`);
this.debug(['Fetched Gateway Information', `URL: ${gatewayURL}`, `Recommended Shards: ${recommendedShards}`]);
this.debug(['Session Limit Information', `Total: ${total}`, `Remaining: ${remaining}`]);
this.gateway = `${gatewayURL}/`;
this.client.options.shardCount = await this._ws.getShardCount();
@ -231,7 +224,7 @@ class WebSocketManager extends EventEmitter {
* @private
*/
attachEvents() {
this._ws.on(WSWebSocketShardEvents.Debug, ({ message, shardId }) => this.debug(message, shardId));
this._ws.on(WSWebSocketShardEvents.Debug, ({ message, shardId }) => this.debug([message], shardId));
this._ws.on(WSWebSocketShardEvents.Dispatch, ({ data, shardId }) => {
this.client.emit(Events.Raw, data, shardId);
this.emit(data.t, data.d, shardId);
@ -258,7 +251,7 @@ class WebSocketManager extends EventEmitter {
* @param {number} id The shard id that disconnected
*/
this.client.emit(Events.ShardDisconnect, { code, reason: reasonIsDeprecated, wasClean: true }, shardId);
this.debug(`Shard not resumable: ${code} (${GatewayCloseCodes[code] ?? CloseCodes[code]})`, shardId);
this.debug([`Shard not resumable: ${code} (${GatewayCloseCodes[code] ?? CloseCodes[code]})`], shardId);
return;
}
@ -291,7 +284,7 @@ class WebSocketManager extends EventEmitter {
});
this._ws.on(WSWebSocketShardEvents.HeartbeatComplete, ({ heartbeatAt, latency, shardId }) => {
this.debug(`Heartbeat acknowledged, latency of ${latency}ms.`, shardId);
this.debug([`Heartbeat acknowledged, latency of ${latency}ms.`], shardId);
const shard = this.shards.get(shardId);
shard.lastPingTimestamp = heartbeatAt;
shard.ping = latency;
@ -324,7 +317,7 @@ class WebSocketManager extends EventEmitter {
async destroy() {
if (this.destroyed) return;
// TODO: Make a util for getting a stack
this.debug(Object.assign(new Error(), { name: 'Manager was destroyed:' }).stack);
this.debug([Object.assign(new Error(), { name: 'Manager was destroyed:' }).stack]);
this.destroyed = true;
await this._ws?.destroy({ code: CloseCodes.Normal, reason: 'Manager was destroyed' });
}

View file

@ -85,11 +85,11 @@ class WebSocketShard extends EventEmitter {
/**
* Emits a debug event.
* @param {string} message The debug message
* @param {string[]} messages The debug message
* @private
*/
debug(message) {
this.manager.debug(message, this.id);
debug(messages) {
this.manager.debug(messages, this.id);
}
/**
@ -110,10 +110,13 @@ class WebSocketShard extends EventEmitter {
wasClean: false,
},
) {
this.debug(`[CLOSE]
Event Code: ${event.code}
Clean : ${event.wasClean}
Reason : ${event.reason ?? 'No reason received'}`);
this.debug([
'[CLOSE]',
`Event Code: ${event.code}`,
`Clean : ${event.wasClean}`,
`Reason : ${event.reason ?? 'No reason received'}`,
]);
/**
* Emitted when a shard's WebSocket closes.
* @private
@ -130,7 +133,7 @@ class WebSocketShard extends EventEmitter {
*/
onReadyPacket(packet) {
if (!packet) {
this.debug(`Received broken packet: '${packet}'.`);
this.debug([`Received broken packet: '${packet}'.`]);
return;
}
@ -167,7 +170,7 @@ class WebSocketShard extends EventEmitter {
}
// Step 1. If we don't have any other guilds pending, we are ready
if (!this.expectedGuilds.size) {
this.debug('Shard received all its guilds. Marking as fully ready.');
this.debug(['Shard received all its guilds. Marking as fully ready.']);
this.status = Status.Ready;
/**
@ -191,12 +194,12 @@ class WebSocketShard extends EventEmitter {
this.readyTimeout = setTimeout(
() => {
this.debug(
`Shard ${hasGuildsIntent ? 'did' : 'will'} not receive any more guild packets` +
`${hasGuildsIntent ? ` in ${waitGuildTimeout} ms` : ''}.\nUnavailable guild count: ${
this.expectedGuilds.size
}`,
);
this.debug([
hasGuildsIntent
? `Shard did not receive any guild packets in ${waitGuildTimeout} ms.`
: 'Shard will not receive anymore guild packets.',
`Unavailable guild count: ${this.expectedGuilds.size}`,
]);
this.readyTimeout = null;
this.status = Status.Ready;

View file

@ -57,7 +57,9 @@ class AutoModerationRuleManager extends CachedManager {
* @property {AutoModerationRuleKeywordPresetType[]} [presets]
* The internally pre-defined wordsets which will be searched for in the content
* @property {string[]} [allowList] The substrings that will be exempt from triggering
* {@link AutoModerationRuleTriggerType.Keyword} and {@link AutoModerationRuleTriggerType.KeywordPreset}
* {@link AutoModerationRuleTriggerType.Keyword},
* {@link AutoModerationRuleTriggerType.KeywordPreset},
* and {@link AutoModerationRuleTriggerType.MemberProfile}
* @property {?number} [mentionTotalLimit] The total number of role & user mentions allowed per message
* @property {boolean} [mentionRaidProtectionEnabled] Whether to automatically detect mention raids
*/
@ -87,8 +89,10 @@ class AutoModerationRuleManager extends CachedManager {
* @property {AutoModerationRuleTriggerType} triggerType The trigger type of the auto moderation rule
* @property {AutoModerationTriggerMetadataOptions} [triggerMetadata] The trigger metadata of the auto moderation rule
* <info>This property is required if using a `triggerType` of
* {@link AutoModerationRuleTriggerType.Keyword}, {@link AutoModerationRuleTriggerType.KeywordPreset},
* or {@link AutoModerationRuleTriggerType.MentionSpam}.</info>
* {@link AutoModerationRuleTriggerType.Keyword},
* {@link AutoModerationRuleTriggerType.KeywordPreset},
* {@link AutoModerationRuleTriggerType.MentionSpam},
* or {@link AutoModerationRuleTriggerType.MemberProfile}.</info>
* @property {AutoModerationActionOptions[]} actions
* The actions that will execute when the auto moderation rule is triggered
* @property {boolean} [enabled] Whether the auto moderation rule should be enabled

View file

@ -128,8 +128,9 @@ class GuildMemberManager extends CachedManager {
resolvedOptions.roles = resolvedRoles;
}
const data = await this.client.rest.put(Routes.guildMember(this.guild.id, userId), { body: resolvedOptions });
// Data is an empty Uint8Array if the member is already part of the guild.
return data instanceof Uint8Array
// Data is an empty array buffer if the member is already part of the guild.
return data instanceof ArrayBuffer
? options.fetchWhenExisting === false
? null
: this.fetch(userId)

View file

@ -66,7 +66,9 @@ class AutoModerationRule extends Base {
* @property {AutoModerationRuleKeywordPresetType[]} presets
* The internally pre-defined wordsets which will be searched for in the content
* @property {string[]} allowList The substrings that will be exempt from triggering
* {@link AutoModerationRuleTriggerType.Keyword} and {@link AutoModerationRuleTriggerType.KeywordPreset}
* {@link AutoModerationRuleTriggerType.Keyword},
* {@link AutoModerationRuleTriggerType.KeywordPreset},
* and {@link AutoModerationRuleTriggerType.MemberProfile}
* @property {?number} mentionTotalLimit The total number of role & user mentions allowed per message
* @property {boolean} mentionRaidProtectionEnabled Whether mention raid protection is enabled
*/
@ -209,7 +211,9 @@ class AutoModerationRule extends Base {
/**
* Sets the allow list for this auto moderation rule.
* @param {string[]} allowList The substrings that will be exempt from triggering
* {@link AutoModerationRuleTriggerType.Keyword} and {@link AutoModerationRuleTriggerType.KeywordPreset}
* {@link AutoModerationRuleTriggerType.Keyword},
* {@link AutoModerationRuleTriggerType.KeywordPreset},
* and {@link AutoModerationRuleTriggerType.MemberProfile}
* @param {string} [reason] The reason for changing the allow list of this auto moderation rule
* @returns {Promise<AutoModerationRule>}
*/

View file

@ -417,6 +417,30 @@ class Message extends Base {
} else {
this.poll ??= null;
}
/**
* A call associated with a message
* @typedef {Object} MessageCall
* @property {Readonly<?Date>} endedAt The time the call ended
* @property {?number} endedTimestamp The timestamp the call ended
* @property {Snowflake[]} participants The ids of the users that participated in the call
*/
if (data.call) {
/**
* The call associated with the message
* @type {?MessageCall}
*/
this.call = {
endedTimestamp: data.call.ended_timestamp ? Date.parse(data.call.ended_timestamp) : null,
participants: data.call.participants,
get endedAt() {
return this.endedTimestamp && new Date(this.endedTimestamp);
},
};
} else {
this.call ??= null;
}
}
/**

View file

@ -2027,6 +2027,12 @@ export class LimitedCollection<Key, Value> extends Collection<Key, Value> {
public keepOverLimit: ((value: Value, key: Key, collection: this) => boolean) | null;
}
export interface MessageCall {
get endedAt(): Date | null;
endedTimestamp: number | null;
participants: readonly Snowflake[];
}
export type MessageComponentType = Exclude<ComponentType, ComponentType.TextInput | ComponentType.ActionRow>;
export interface MessageCollectorOptionsParams<
@ -2118,6 +2124,7 @@ export class Message<InGuild extends boolean = boolean> extends Base {
public get thread(): AnyThreadChannel | null;
public tts: boolean;
public poll: Poll | null;
public call: MessageCall | null;
public type: MessageType;
public get url(): string;
public webhookId: Snowflake | null;
@ -3659,7 +3666,7 @@ export class WebSocketManager extends EventEmitter {
public on(event: GatewayDispatchEvents, listener: (data: any, shardId: number) => void): this;
public once(event: GatewayDispatchEvents, listener: (data: any, shardId: number) => void): this;
private debug(message: string, shardId?: number): void;
private debug(messages: readonly string[], shardId?: number): void;
private connect(): Promise<void>;
private broadcast(packet: unknown): void;
private destroy(): Promise<void>;
@ -3690,7 +3697,7 @@ export class WebSocketShard extends EventEmitter {
public status: Status;
public ping: number;
private debug(message: string): void;
private debug(messages: readonly string[]): void;
private onReadyPacket(packet: unknown): void;
private gotGuild(guildId: Snowflake): void;
private checkReady(): void;
@ -4075,7 +4082,7 @@ export class ApplicationCommandManager<
id: Snowflake,
options: FetchApplicationCommandOptions & { guildId: Snowflake },
): Promise<ApplicationCommand>;
public fetch(options: FetchApplicationCommandOptions): Promise<Collection<string, ApplicationCommandScope>>;
public fetch(options: FetchApplicationCommandOptions): Promise<Collection<Snowflake, ApplicationCommandScope>>;
public fetch(id: Snowflake, options?: FetchApplicationCommandOptions): Promise<ApplicationCommandScope>;
public fetch(
id?: Snowflake,

View file

@ -60,22 +60,22 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"commander": "^12.0.0",
"commander": "^12.1.0",
"jsdoc-to-markdown": "^8.0.1",
"tslib": "^2.6.2",
"typedoc": "^0.25.12"
"typedoc": "^0.25.13"
},
"devDependencies": {
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/jsdoc-to-markdown": "^7.0.6",
"@types/node": "18.18.8",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5"
},
"engines": {

View file

@ -55,24 +55,24 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"discord-api-types": "0.37.83"
"discord-api-types": "0.37.90"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "16.18.60",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=16.11.0"

View file

@ -72,24 +72,24 @@
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "workspace:^",
"discord-api-types": "0.37.83"
"discord-api-types": "0.37.90"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.18.8",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -55,9 +55,9 @@
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5"
},
"engines": {

View file

@ -68,26 +68,26 @@
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"tslib": "^2.6.2",
"undici": "6.13.0"
"undici": "6.18.2"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.18.8",
"@types/supertest": "^6.0.2",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"supertest": "^6.3.4",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"supertest": "^7.0.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -1,114 +1,119 @@
import { test, expect } from 'vitest';
import { CDN } from '../src/index.js';
const base = 'https://discord.com';
const baseCDN = 'https://cdn-discord.com';
const baseMedia = 'https://media-discord.com';
const id = '123456';
const hash = 'abcdef';
const animatedHash = 'a_bcdef';
const defaultAvatar = 1_234 % 5;
const cdn = new CDN(base);
const cdn = new CDN(baseCDN, baseMedia);
test('appAsset default', () => {
expect(cdn.appAsset(id, hash)).toEqual(`${base}/app-assets/${id}/${hash}.webp`);
expect(cdn.appAsset(id, hash)).toEqual(`${baseCDN}/app-assets/${id}/${hash}.webp`);
});
test('appIcon default', () => {
expect(cdn.appIcon(id, hash)).toEqual(`${base}/app-icons/${id}/${hash}.webp`);
expect(cdn.appIcon(id, hash)).toEqual(`${baseCDN}/app-icons/${id}/${hash}.webp`);
});
test('avatar default', () => {
expect(cdn.avatar(id, hash)).toEqual(`${base}/avatars/${id}/${hash}.webp`);
expect(cdn.avatar(id, hash)).toEqual(`${baseCDN}/avatars/${id}/${hash}.webp`);
});
test('avatar dynamic-animated', () => {
expect(cdn.avatar(id, animatedHash)).toEqual(`${base}/avatars/${id}/${animatedHash}.gif`);
expect(cdn.avatar(id, animatedHash)).toEqual(`${baseCDN}/avatars/${id}/${animatedHash}.gif`);
});
test('avatar dynamic-not-animated', () => {
expect(cdn.avatar(id, hash)).toEqual(`${base}/avatars/${id}/${hash}.webp`);
expect(cdn.avatar(id, hash)).toEqual(`${baseCDN}/avatars/${id}/${hash}.webp`);
});
test('avatar decoration default', () => {
expect(cdn.avatarDecoration(id, hash)).toEqual(`${base}/avatar-decorations/${id}/${hash}.webp`);
expect(cdn.avatarDecoration(id, hash)).toEqual(`${baseCDN}/avatar-decorations/${id}/${hash}.webp`);
});
test('avatar decoration preset', () => {
expect(cdn.avatarDecoration(hash)).toEqual(`${base}/avatar-decoration-presets/${hash}.png`);
expect(cdn.avatarDecoration(hash)).toEqual(`${baseCDN}/avatar-decoration-presets/${hash}.png`);
});
test('banner default', () => {
expect(cdn.banner(id, hash)).toEqual(`${base}/banners/${id}/${hash}.webp`);
expect(cdn.banner(id, hash)).toEqual(`${baseCDN}/banners/${id}/${hash}.webp`);
});
test('channelIcon default', () => {
expect(cdn.channelIcon(id, hash)).toEqual(`${base}/channel-icons/${id}/${hash}.webp`);
expect(cdn.channelIcon(id, hash)).toEqual(`${baseCDN}/channel-icons/${id}/${hash}.webp`);
});
test('defaultAvatar default', () => {
expect(cdn.defaultAvatar(defaultAvatar)).toEqual(`${base}/embed/avatars/${defaultAvatar}.png`);
expect(cdn.defaultAvatar(defaultAvatar)).toEqual(`${baseCDN}/embed/avatars/${defaultAvatar}.png`);
});
test('discoverySplash default', () => {
expect(cdn.discoverySplash(id, hash)).toEqual(`${base}/discovery-splashes/${id}/${hash}.webp`);
expect(cdn.discoverySplash(id, hash)).toEqual(`${baseCDN}/discovery-splashes/${id}/${hash}.webp`);
});
test('emoji default', () => {
expect(cdn.emoji(id)).toEqual(`${base}/emojis/${id}.webp`);
expect(cdn.emoji(id)).toEqual(`${baseCDN}/emojis/${id}.webp`);
});
test('emoji gif', () => {
expect(cdn.emoji(id, 'gif')).toEqual(`${base}/emojis/${id}.gif`);
expect(cdn.emoji(id, 'gif')).toEqual(`${baseCDN}/emojis/${id}.gif`);
});
test('guildMemberAvatar default', () => {
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${base}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${baseCDN}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
});
test('guildMemberAvatar dynamic-animated', () => {
expect(cdn.guildMemberAvatar(id, id, animatedHash)).toEqual(
`${base}/guilds/${id}/users/${id}/avatars/${animatedHash}.gif`,
`${baseCDN}/guilds/${id}/users/${id}/avatars/${animatedHash}.gif`,
);
});
test('guildMemberAvatar dynamic-not-animated', () => {
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${base}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${baseCDN}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
});
test('guildScheduledEventCover default', () => {
expect(cdn.guildScheduledEventCover(id, hash)).toEqual(`${base}/guild-events/${id}/${hash}.webp`);
expect(cdn.guildScheduledEventCover(id, hash)).toEqual(`${baseCDN}/guild-events/${id}/${hash}.webp`);
});
test('icon default', () => {
expect(cdn.icon(id, hash)).toEqual(`${base}/icons/${id}/${hash}.webp`);
expect(cdn.icon(id, hash)).toEqual(`${baseCDN}/icons/${id}/${hash}.webp`);
});
test('icon dynamic-animated', () => {
expect(cdn.icon(id, animatedHash)).toEqual(`${base}/icons/${id}/${animatedHash}.gif`);
expect(cdn.icon(id, animatedHash)).toEqual(`${baseCDN}/icons/${id}/${animatedHash}.gif`);
});
test('icon dynamic-not-animated', () => {
expect(cdn.icon(id, hash)).toEqual(`${base}/icons/${id}/${hash}.webp`);
expect(cdn.icon(id, hash)).toEqual(`${baseCDN}/icons/${id}/${hash}.webp`);
});
test('role icon default', () => {
expect(cdn.roleIcon(id, hash)).toEqual(`${base}/role-icons/${id}/${hash}.webp`);
expect(cdn.roleIcon(id, hash)).toEqual(`${baseCDN}/role-icons/${id}/${hash}.webp`);
});
test('splash default', () => {
expect(cdn.splash(id, hash)).toEqual(`${base}/splashes/${id}/${hash}.webp`);
expect(cdn.splash(id, hash)).toEqual(`${baseCDN}/splashes/${id}/${hash}.webp`);
});
test('sticker default', () => {
expect(cdn.sticker(id)).toEqual(`${base}/stickers/${id}.png`);
expect(cdn.sticker(id)).toEqual(`${baseCDN}/stickers/${id}.png`);
});
test('sticker GIF', () => {
expect(cdn.sticker(id, 'gif')).toEqual(`${baseMedia}/stickers/${id}.gif`);
});
test('stickerPackBanner default', () => {
expect(cdn.stickerPackBanner(id)).toEqual(`${base}/app-assets/710982414301790216/store/${id}.webp`);
expect(cdn.stickerPackBanner(id)).toEqual(`${baseCDN}/app-assets/710982414301790216/store/${id}.webp`);
});
test('teamIcon default', () => {
expect(cdn.teamIcon(id, hash)).toEqual(`${base}/team-icons/${id}/${hash}.webp`);
expect(cdn.teamIcon(id, hash)).toEqual(`${baseCDN}/team-icons/${id}/${hash}.webp`);
});
test('makeURL throws on invalid size', () => {
@ -122,5 +127,5 @@ test('makeURL throws on invalid extension', () => {
});
test('makeURL valid size', () => {
expect(cdn.avatar(id, animatedHash, { size: 512 })).toEqual(`${base}/avatars/${id}/${animatedHash}.gif?size=512`);
expect(cdn.avatar(id, animatedHash, { size: 512 })).toEqual(`${baseCDN}/avatars/${id}/${animatedHash}.gif?size=512`);
});

View file

@ -88,27 +88,27 @@
"@sapphire/async-queue": "^1.5.2",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "0.37.83",
"discord-api-types": "0.37.90",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.2",
"undici": "6.13.0"
"undici": "6.18.2"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.17.9",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=16.11.0"

View file

@ -46,6 +46,12 @@ export interface MakeURLOptions {
* The allowed extensions that can be used
*/
allowedExtensions?: readonly string[];
/**
* The base URL.
*
* @defaultValue `DefaultRestOptions.cdn`
*/
base?: string;
/**
* The extension to use for the image URL
*
@ -62,7 +68,10 @@ export interface MakeURLOptions {
* The CDN link builder
*/
export class CDN {
public constructor(private readonly base: string = DefaultRestOptions.cdn) {}
public constructor(
private readonly cdn: string = DefaultRestOptions.cdn,
private readonly mediaProxy: string = DefaultRestOptions.mediaProxy,
) {}
/**
* Generates an app asset URL for a client's asset.
@ -287,10 +296,15 @@ export class CDN {
* @param stickerId - The sticker id
* @param extension - The extension of the sticker
* @privateRemarks
* Stickers cannot have a `.webp` extension, so we default to a `.png`
* Stickers cannot have a `.webp` extension, so we default to a `.png`.
* Sticker GIFs do not use the CDN base URL.
*/
public sticker(stickerId: string, extension: StickerExtension = 'png'): string {
return this.makeURL(`/stickers/${stickerId}`, { allowedExtensions: ALLOWED_STICKER_EXTENSIONS, extension });
return this.makeURL(`/stickers/${stickerId}`, {
allowedExtensions: ALLOWED_STICKER_EXTENSIONS,
base: extension === 'gif' ? this.mediaProxy : this.cdn,
extension,
});
}
/**
@ -352,7 +366,12 @@ export class CDN {
*/
private makeURL(
route: string,
{ allowedExtensions = ALLOWED_EXTENSIONS, extension = 'webp', size }: Readonly<MakeURLOptions> = {},
{
allowedExtensions = ALLOWED_EXTENSIONS,
base = this.cdn,
extension = 'webp',
size,
}: Readonly<MakeURLOptions> = {},
): string {
// eslint-disable-next-line no-param-reassign
extension = String(extension).toLowerCase();
@ -365,7 +384,7 @@ export class CDN {
throw new RangeError(`Invalid size provided: ${size}\nMust be one of: ${ALLOWED_SIZES.join(', ')}`);
}
const url = new URL(`${this.base}${route}.${extension}`);
const url = new URL(`${base}${route}.${extension}`);
if (size) {
url.searchParams.set('size', String(size));

View file

@ -75,7 +75,7 @@ export class REST extends AsyncEventEmitter<RestEvents> {
public constructor(options: Partial<RESTOptions> = {}) {
super();
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn, options.mediaProxy ?? DefaultRestOptions.mediaProxy);
this.options = { ...DefaultRestOptions, ...options };
this.globalRemaining = Math.max(1, this.options.globalRequestsPerSecond);
this.agent = options.agent ?? null;

View file

@ -31,6 +31,7 @@ export const DefaultRestOptions = {
async makeRequest(...args): Promise<ResponseLike> {
return getDefaultStrategy()(...args);
},
mediaProxy: 'https://media.discordapp.net',
} as const satisfies Required<RESTOptions>;
/**

View file

@ -85,6 +85,12 @@ export interface RESTOptions {
* For example, to use global fetch, simply provide `makeRequest: fetch`
*/
makeRequest(url: string, init: RequestInit): Promise<ResponseLike>;
/**
* The media proxy path
*
* @defaultValue `'https://media.discordapp.net'`
*/
mediaProxy: string;
/**
* The extra offset to add to rate limits in milliseconds
*

View file

@ -66,25 +66,25 @@
"@microsoft/tsdoc-config": "0.16.2",
"@vercel/blob": "^0.22.3",
"@vercel/postgres": "^0.8.0",
"commander": "^12.0.0",
"commander": "^12.1.0",
"tslib": "^2.6.2",
"undici": "6.13.0",
"yaml": "2.4.1"
"undici": "6.18.2",
"yaml": "2.4.3"
},
"devDependencies": {
"@turbo/gen": "^1.12.5",
"@turbo/gen": "^1.13.3",
"@types/node": "18.18.8",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"env-cmd": "^10.1.0",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -240,7 +240,12 @@ function itemExcerptText(excerpt: Excerpt, apiPackage: ApiPackage) {
// dapi-types doesn't have routes for class members
// so we can assume this member is for an enum
if (meaning === 'member' && path && 'parent' in path) {
href += `/enum/${path.parent}#${path.component}`;
// unless it's a variable like FormattingPatterns.Role
if (path.parent.toString() === '__type') {
href += `#${token.text.split('.')[0]}`;
} else {
href += `/enum/${path.parent}#${path.component}`;
}
} else if (meaning === 'type' || meaning === 'var') {
href += `#${token.text}`;
} else {
@ -951,11 +956,16 @@ export async function generateSplitDocumentation({
const members = entry.members
.filter((item) => {
if (item.kind !== 'Function') {
return true;
switch (item.kind) {
case ApiItemKind.Function:
return (item as ApiFunction).overloadIndex === 1;
case ApiItemKind.Interface:
return !entry.members.some(
(innerItem) => innerItem.kind === ApiItemKind.Class && innerItem.displayName === item.displayName,
);
default:
return true;
}
return (item as ApiFunction).overloadIndex === 1;
})
.map((item) => ({
kind: item.kind,

View file

@ -41,14 +41,15 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
{
type: 'addMany',
destination: `${plop.getDestBasePath()}/../{{name}}`,
templateFiles: ['templates/**'],
// plop doesn't like our cliff.toml file since it tries to parse it. we add it manually later
templateFiles: ['templates/**', '!templates/default/cliff.toml'],
globOptions: { dot: true },
base: 'templates/default/',
stripExtensions: ['hbs'],
},
{
type: 'modify',
path: `${plop.getDestBasePath()}/turbo/generators/templates/cliff.toml`,
path: `${plop.getDestBasePath()}/turbo/generators/templates/default/cliff.toml`,
async transform(content, answers) {
const cliffTOML = content.replace('{{name}}', answers.name);
await writeFile(`${plop.getDestBasePath()}/../${answers.name}/cliff.toml`, cliffTOML);
@ -70,10 +71,17 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
type: 'modify',
path: `${plop.getDestBasePath()}/../../.github/labeler.yml`,
transform(content, answers) {
const labelerYAML = parseYAML(content) as Record<string, string[]>;
labelerYAML[`packages:${answers.name}`] = [`packages/${answers.name}/*`, `packages/${answers.name}/**/*`];
const labelerYAML = parseYAML(content) as Record<string, Record<string, Record<string, string[]>[]>[]>;
return stringifyYAML(sortYAMLObject(labelerYAML));
labelerYAML[`packages:${answers.name}`] = [
{
'changed-files': [
{ 'any-glob-to-any-file': [`packages/${answers.name}/*`, `packages/${answers.name}/**/*`] },
],
},
];
return stringifyYAML(labelerYAML, { sortMapEntries: true });
},
},
{

View file

@ -1,2 +1,2 @@
# Packages node_modules # Log files logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Env .env # Dist dist
dist-docs # Docs docs/**/* !docs/README.md # Miscellaneous .turbo .tmp coverage
dist-docs # Docs docs/**/* !docs/README.md # Miscellaneous .turbo .tmp coverage

View file

@ -58,37 +58,37 @@
"react-dom": "^18.3.1"
},
"devDependencies": {
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@react-icons/all-files": "^4.1.0",
"@storybook/addon-essentials": "^8.0.8",
"@storybook/addon-interactions": "^8.0.8",
"@storybook/addon-links": "^8.0.8",
"@storybook/addon-essentials": "^8.1.5",
"@storybook/addon-interactions": "^8.1.5",
"@storybook/addon-links": "^8.1.5",
"@storybook/addon-styling": "^1.3.7",
"@storybook/blocks": "^8.0.8",
"@storybook/react": "^8.0.8",
"@storybook/react-vite": "^8.0.8",
"@storybook/blocks": "^8.1.5",
"@storybook/react": "^8.1.5",
"@storybook/react-vite": "^8.1.5",
"@storybook/testing-library": "^0.2.2",
"@types/node": "18.18.8",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@unocss/eslint-plugin": "^0.60.2",
"@unocss/reset": "^0.60.2",
"@vitejs/plugin-react": "^4.2.1",
"@unocss/eslint-plugin": "^0.60.4",
"@unocss/reset": "^0.60.4",
"@vitejs/plugin-react": "^4.3.0",
"@vitest/coverage-v8": "^1.6.0",
"chromatic": "^11.3.0",
"chromatic": "^11.5.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"prettier": "^3.3.0",
"prop-types": "^15.8.1",
"storybook": "^8.0.8",
"turbo": "^1.13.2",
"storybook": "^8.1.5",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"unocss": "^0.60.2",
"vite": "^5.2.9",
"vite-plugin-dts": "^3.8.3",
"vitest": "^1.5.0"
"unocss": "^0.60.4",
"vite": "^5.2.12",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.6.0"
},
"engines": {
"node": ">=18"

View file

@ -64,20 +64,20 @@
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "16.18.60",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.2.5",
"tsd": "^0.30.7",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsd": "^0.31.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"vitest": "^1.5.0"
"vitest": "^1.6.0"
},
"engines": {
"node": ">=16.11.0"

View file

@ -64,18 +64,18 @@
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@types/ws": "^8.5.10",
"discord-api-types": "0.37.83",
"discord-api-types": "0.37.90",
"prism-media": "^1.3.5",
"tslib": "^2.6.2",
"ws": "^8.16.0"
"ws": "^8.17.0"
},
"devDependencies": {
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-typescript": "^7.24.1",
"@babel/core": "^7.24.6",
"@babel/preset-env": "^7.24.6",
"@babel/preset-typescript": "^7.24.6",
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/jest": "^29.5.12",
"@types/node": "16.18.60",
"cross-env": "^7.0.3",
@ -86,9 +86,9 @@
"jest": "^29.7.0",
"jest-websocket-mock": "^2.5.0",
"mock-socket": "^9.3.1",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"tweetnacl": "^1.0.3",
"typescript": "^5.4.5"
},

View file

@ -79,29 +79,29 @@
"@sapphire/async-queue": "^1.5.2",
"@types/ws": "^8.5.10",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "0.37.83",
"discord-api-types": "0.37.90",
"tslib": "^2.6.2",
"ws": "^8.16.0"
"ws": "^8.17.0"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@favware/cliff-jumper": "^3.0.2",
"@favware/cliff-jumper": "^3.0.3",
"@types/node": "18.17.9",
"@vitest/coverage-v8": "^1.5.0",
"@vitest/coverage-v8": "^1.6.0",
"cross-env": "^7.0.3",
"esbuild-plugin-version-injector": "^1.2.1",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"mock-socket": "^9.3.1",
"prettier": "^3.2.5",
"tsd": "^0.30.7",
"tsup": "^8.0.2",
"turbo": "^1.13.2",
"prettier": "^3.3.0",
"tsd": "^0.31.0",
"tsup": "^8.1.0",
"turbo": "^1.13.3",
"typescript": "^5.4.5",
"undici": "6.13.0",
"vitest": "^1.5.0",
"undici": "6.18.2",
"vitest": "^1.6.0",
"zlib-sync": "^0.1.9"
},
"engines": {

View file

@ -915,15 +915,6 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
}
private debug(messages: [string, ...string[]]) {
const message = `${messages[0]}${
messages.length > 1
? `\n${messages
.slice(1)
.map((message) => ` ${message}`)
.join('\n')}`
: ''
}`;
this.emit(WebSocketShardEvents.Debug, { message });
this.emit(WebSocketShardEvents.Debug, { message: messages.join('\n\t') });
}
}

10489
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff