mirror of
https://github.com/discordjs/discord.js.git
synced 2026-05-23 20:10:08 +00:00
Compare commits
79 Commits
@discordjs
...
@discordjs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25cc7282d0 | ||
|
|
d8c10a7825 | ||
|
|
ea5471aa0a | ||
|
|
7a5e6ffc70 | ||
|
|
e1c4ee90b5 | ||
|
|
06c2b820c6 | ||
|
|
f65ff060ae | ||
|
|
835c4496ab | ||
|
|
a589c6d492 | ||
|
|
fdf0b8455c | ||
|
|
3f9c3dc497 | ||
|
|
437b2d459d | ||
|
|
5b8a08ebb6 | ||
|
|
22e013b3e9 | ||
|
|
e133aa9a1e | ||
|
|
c1b5242d2f | ||
|
|
9779baea84 | ||
|
|
a5437a41f3 | ||
|
|
11dd1c0666 | ||
|
|
95fae30606 | ||
|
|
93eeaeb56b | ||
|
|
886a701251 | ||
|
|
6dc0b7c18a | ||
|
|
c4eba873ea | ||
|
|
5c023bd64b | ||
|
|
e7cc754fd3 | ||
|
|
6e32ee565f | ||
|
|
c6710e56cc | ||
|
|
5761f2cbfd | ||
|
|
dd9ba1ad9d | ||
|
|
0b0abfa283 | ||
|
|
419266d839 | ||
|
|
5e7ba2a016 | ||
|
|
efd6b36227 | ||
|
|
66774c1b53 | ||
|
|
728c2686bd | ||
|
|
1bafba3fff | ||
|
|
a193147f96 | ||
|
|
17d4c78fde | ||
|
|
3b5c600b9e | ||
|
|
311aaf2605 | ||
|
|
4ea73bb64e | ||
|
|
aae2faf9e9 | ||
|
|
9b07036d70 | ||
|
|
c1e6890132 | ||
|
|
38a37b5caf | ||
|
|
29a50bb476 | ||
|
|
d22b55fc82 | ||
|
|
a468ae8bb5 | ||
|
|
638b896efa | ||
|
|
27d0659a45 | ||
|
|
a35d760421 | ||
|
|
7f467ed2d1 | ||
|
|
f5dd6879a2 | ||
|
|
f9ba11eba3 | ||
|
|
b36ec98382 | ||
|
|
bb884fc260 | ||
|
|
555961b3b8 | ||
|
|
92c1a511dc | ||
|
|
35207b0b31 | ||
|
|
29fd89f23c | ||
|
|
c2432d5704 | ||
|
|
616208ba77 | ||
|
|
3640fe7bca | ||
|
|
c78af13c1e | ||
|
|
914cc4ba54 | ||
|
|
393ded4ea1 | ||
|
|
20258f94bf | ||
|
|
7816ec2e6b | ||
|
|
5498e18bf4 | ||
|
|
e673b3c129 | ||
|
|
776880d06b | ||
|
|
c05244af61 | ||
|
|
12deea85e5 | ||
|
|
07c12101e5 | ||
|
|
30d79e85fb | ||
|
|
f2794e1221 | ||
|
|
0474a43751 | ||
|
|
c91d03c535 |
@@ -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]
|
||||
}
|
||||
|
||||
2
.github/COMMIT_CONVENTION.md
vendored
2
.github/COMMIT_CONVENTION.md
vendored
@@ -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
|
||||
|
||||
120
.github/labeler.yml
vendored
120
.github/labeler.yml
vendored
@@ -1,60 +1,100 @@
|
||||
apps:guide:
|
||||
- apps/guide/*
|
||||
- apps/guide/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- apps/guide/*
|
||||
- apps/guide/**/*
|
||||
apps:website:
|
||||
- apps/website/*
|
||||
- apps/website/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- apps/website/*
|
||||
- apps/website/**/*
|
||||
packages:api-extractor:
|
||||
- packages/api-extractor/*
|
||||
- packages/api-extractor/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/api-extractor/*
|
||||
- packages/api-extractor/**/*
|
||||
packages:api-extractor-model:
|
||||
- packages/api-extractor-model/*
|
||||
- packages/api-extractor-model/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/api-extractor-model/*
|
||||
- packages/api-extractor-model/**/*
|
||||
packages:brokers:
|
||||
- packages/brokers/*
|
||||
- packages/brokers/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/brokers/*
|
||||
- packages/brokers/**/*
|
||||
packages:builders:
|
||||
- packages/builders/*
|
||||
- packages/builders/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/builders/*
|
||||
- packages/builders/**/*
|
||||
packages:collection:
|
||||
- packages/collection/*
|
||||
- packages/collection/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/collection/*
|
||||
- packages/collection/**/*
|
||||
packages:core:
|
||||
- packages/core/*
|
||||
- packages/core/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/core/*
|
||||
- packages/core/**/*
|
||||
packages:create-discord-bot:
|
||||
- packages/create-discord-bot/*
|
||||
- packages/create-discord-bot/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/create-discord-bot/*
|
||||
- packages/create-discord-bot/**/*
|
||||
packages:discord.js:
|
||||
- packages/discord.js/*
|
||||
- packages/discord.js/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/discord.js/*
|
||||
- packages/discord.js/**/*
|
||||
packages:docgen:
|
||||
- packages/docgen/*
|
||||
- packages/docgen/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/docgen/*
|
||||
- packages/docgen/**/*
|
||||
packages:formatters:
|
||||
- packages/formatters/*
|
||||
- packages/formatters/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/formatters/*
|
||||
- packages/formatters/**/*
|
||||
packages:next:
|
||||
- packages/next/*
|
||||
- packages/next/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/next/*
|
||||
- packages/next/**/*
|
||||
packages:proxy:
|
||||
- packages/proxy/*
|
||||
- packages/proxy/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/proxy/*
|
||||
- packages/proxy/**/*
|
||||
packages:proxy-container:
|
||||
- packages/proxy-container/*
|
||||
- packages/proxy-container/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/proxy-container/*
|
||||
- packages/proxy-container/**/*
|
||||
packages:rest:
|
||||
- packages/rest/*
|
||||
- packages/rest/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/rest/*
|
||||
- packages/rest/**/*
|
||||
packages:ui:
|
||||
- packages/ui/*
|
||||
- packages/ui/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/ui/*
|
||||
- packages/ui/**/*
|
||||
packages:util:
|
||||
- packages/util/*
|
||||
- packages/util/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/util/*
|
||||
- packages/util/**/*
|
||||
packages:voice:
|
||||
- packages/voice/*
|
||||
- packages/voice/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/voice/*
|
||||
- packages/voice/**/*
|
||||
packages:ws:
|
||||
- packages/ws/*
|
||||
- packages/ws/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- packages/ws/*
|
||||
- packages/ws/**/*
|
||||
|
||||
2
.github/workflows/cleanup-cache.yml
vendored
2
.github/workflows/cleanup-cache.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cleanup caches
|
||||
run: |
|
||||
|
||||
8
.github/workflows/deploy-website.yml
vendored
8
.github/workflows/deploy-website.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
8
.github/workflows/deprecate-version.yml
vendored
8
.github/workflows/deprecate-version.yml
vendored
@@ -34,12 +34,12 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
22
.github/workflows/documentation.yml
vendored
22
.github/workflows/documentation.yml
vendored
@@ -36,14 +36,14 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || '' }}
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- name: Checkout main repository
|
||||
if: ${{ inputs.ref && inputs.ref != 'main' }}
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'main'
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
- 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/${{ steps.extract-tag.outputs.semver }}!' "packages/${{ steps.extract-tag.outputs.package}}/"
|
||||
run: sed -i 's!https://github.com/discordjs/discord.js/tree/main!https://github.com/discordjs/discord.js/tree/${{ steps.extract-tag.outputs.semver }}!' "packages/${{ steps.extract-tag.outputs.package}}/api-extractor.json"
|
||||
|
||||
- name: Build docs
|
||||
run: pnpm run docs
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Checkout docs repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'discordjs/docs'
|
||||
token: ${{ secrets.DJS_DOCS }}
|
||||
@@ -211,12 +211,12 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
2
.github/workflows/issue-triage.yml
vendored
2
.github/workflows/issue-triage.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
issue-triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/issue-labeler@v3.2
|
||||
- uses: github/issue-labeler@v3.4
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
configuration-path: .github/issue-labeler.yml
|
||||
|
||||
4
.github/workflows/label-sync.yml
vendored
4
.github/workflows/label-sync.yml
vendored
@@ -15,9 +15,9 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Label sync
|
||||
uses: crazy-max/ghaction-github-labeler@v4
|
||||
uses: crazy-max/ghaction-github-labeler@v5
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-inactive-days: 365
|
||||
|
||||
30
.github/workflows/pr-triage.yml
vendored
30
.github/workflows/pr-triage.yml
vendored
@@ -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
|
||||
uses: actions/labeler@v4
|
||||
- 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
|
||||
|
||||
10
.github/workflows/publish-dev-docker.yml
vendored
10
.github/workflows/publish-dev-docker.yml
vendored
@@ -10,18 +10,18 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
run: echo ${{ secrets.DOCKER_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
8
.github/workflows/publish-dev.yml
vendored
8
.github/workflows/publish-dev.yml
vendored
@@ -43,14 +43,14 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Check the current development version
|
||||
|
||||
10
.github/workflows/publish-docker.yml
vendored
10
.github/workflows/publish-docker.yml
vendored
@@ -7,18 +7,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
run: echo ${{ secrets.DOCKER_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
8
.github/workflows/publish-release.yml
vendored
8
.github/workflows/publish-release.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
10
.github/workflows/tests.yml
vendored
10
.github/workflows/tests.yml
vendored
@@ -15,14 +15,14 @@ jobs:
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install Node.js v20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
@@ -62,3 +62,5 @@ jobs:
|
||||
- name: Upload Coverage
|
||||
if: github.repository_owner == 'discordjs'
|
||||
uses: ./packages/actions/src/uploadCoverage
|
||||
with:
|
||||
codecov_token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
// import bundleAnalyzer from '@next/bundle-analyzer';
|
||||
// import { withContentlayer } from 'next-contentlayer';
|
||||
const bundleAnalyzer = require('@next/bundle-analyzer');
|
||||
const { withContentlayer } = require('next-contentlayer');
|
||||
|
||||
const withBundleAnalyzer = bundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
module.exports = withContentlayer({
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
typedRoutes: true,
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentDispositionType: 'attachment',
|
||||
contentSecurityPolicy: "default-src 'self'; frame-src 'none'; sandbox;",
|
||||
},
|
||||
poweredByHeader: false,
|
||||
});
|
||||
|
||||
module.exports = withBundleAnalyzer(
|
||||
withContentlayer({
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
typedRoutes: true,
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentDispositionType: 'attachment',
|
||||
contentSecurityPolicy: "default-src 'self'; frame-src 'none'; sandbox;",
|
||||
},
|
||||
poweredByHeader: false,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -48,51 +48,50 @@
|
||||
"@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",
|
||||
"contentlayer": "^0.3.4",
|
||||
"next": "14.2.1",
|
||||
"next": "^14.2.3",
|
||||
"next-contentlayer": "^0.3.4",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-custom-scrollbars-2": "^4.5.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.33.3"
|
||||
"sharp": "^0.33.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "14.2.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/html-escaper": "^3.0.2",
|
||||
"@types/node": "18.18.8",
|
||||
"@types/react": "^18.2.79",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@unocss/eslint-plugin": "^0.59.3",
|
||||
"@unocss/postcss": "^0.58.5",
|
||||
"@unocss/reset": "^0.59.3",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@vitest/coverage-v8": "^1.5.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@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.7.1",
|
||||
"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.59.3",
|
||||
"vercel": "^34.0.0",
|
||||
"vitest": "^1.5.0"
|
||||
"unocss": "^0.60.4",
|
||||
"vercel": "^34.2.4",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import bundleAnalyzer from '@next/bundle-analyzer';
|
||||
import localesPlugin from '@react-aria/optimize-locales-plugin';
|
||||
|
||||
const withBundleAnalyzer = bundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
export default withBundleAnalyzer({
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
export default {
|
||||
reactStrictMode: true,
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
@@ -18,14 +14,8 @@ export default withBundleAnalyzer({
|
||||
},
|
||||
},
|
||||
experimental: {
|
||||
ppr: false,
|
||||
},
|
||||
webpack(config, { isServer }) {
|
||||
if (!isServer) {
|
||||
config.plugins.push(localesPlugin.webpack({ locales: ['en-US'] }));
|
||||
}
|
||||
|
||||
return config;
|
||||
ppr: true,
|
||||
reactCompiler: true,
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
@@ -41,4 +31,4 @@ export default withBundleAnalyzer({
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -49,58 +49,57 @@
|
||||
"dependencies": {
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@vercel/analytics": "^1.2.2",
|
||||
"@vercel/blob": "^0.22.3",
|
||||
"@vercel/edge-config": "^1.1.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.0",
|
||||
"lucide-react": "^0.368.0",
|
||||
"meilisearch": "^0.38.0",
|
||||
"next": "14.2.1",
|
||||
"next-mdx-remote": "^4.4.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.6.0",
|
||||
"overlayscrollbars": "^2.8.3",
|
||||
"overlayscrollbars-react": "^0.5.6",
|
||||
"react": "^18.2.0",
|
||||
"react-aria-components": "^1.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"sharp": "^0.33.3",
|
||||
"react": "19.0.0-rc-f994737d14-20240522",
|
||||
"react-aria-components": "^1.2.1",
|
||||
"react-dom": "19.0.0-rc-f994737d14-20240522",
|
||||
"sharp": "^0.33.4",
|
||||
"usehooks-ts": "^3.1.0",
|
||||
"vaul": "^0.9.0"
|
||||
"vaul": "^0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "14.2.1",
|
||||
"@react-aria/optimize-locales-plugin": "^1.0.2",
|
||||
"@shikijs/rehype": "1.1.7",
|
||||
"@tailwindcss/typography": "^0.5.12",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@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.2.79",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@vitest/coverage-v8": "^1.5.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@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",
|
||||
"cpy-cli": "^5.0.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.7.1",
|
||||
"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": "^3.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-rehype": "^11.1.0",
|
||||
"shiki": "1.3.0",
|
||||
"shiki": "^1.6.2",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"turbo": "^1.13.2",
|
||||
"turbo": "^1.13.3",
|
||||
"typescript": "^5.4.5",
|
||||
"vercel": "^34.0.0",
|
||||
"vitest": "^1.5.0"
|
||||
"vercel": "^34.2.4",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable react/no-unknown-property */
|
||||
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { resolveKind } from '~/util/resolveNodeKind';
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { DocItem } from '~/components/DocItem';
|
||||
import { fetchNode } from '~/util/fetchNode';
|
||||
|
||||
@@ -25,6 +26,10 @@ export default async function Page({
|
||||
}) {
|
||||
const node = await fetchNode({ item: params.item, packageName: params.packageName, version: params.version });
|
||||
|
||||
if (!node) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="flex w-full flex-col gap-8 pb-12 md:pb-0">
|
||||
<DocItem node={node} packageName={params.packageName} version={params.version} />
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Navigation } from '~/components/Navigation';
|
||||
import { OverlayScrollbarsComponent } from '~/components/OverlayScrollbars';
|
||||
import { Drawer } from '~/components/ui/Drawer';
|
||||
import { Footer } from '~/components/ui/Footer';
|
||||
import { ENV } from '~/util/env';
|
||||
import { fetchDependencies } from '~/util/fetchDependencies';
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
@@ -33,11 +32,9 @@ export default async function Layout({
|
||||
return (
|
||||
// eslint-disable-next-line react/no-unknown-property
|
||||
<div vaul-drawer-wrapper="" className="mx-auto flex max-w-screen-2xl flex-col gap-12 p-6 md:flex-row">
|
||||
<div
|
||||
className={`sticky hidden flex-shrink-0 self-start md:block ${ENV.IS_LOCAL_DEV || ENV.IS_PREVIEW ? 'top-[64px]' : 'top-6'}`}
|
||||
>
|
||||
<div className="sticky top-6 hidden flex-shrink-0 self-start md:block">
|
||||
<OverlayScrollbarsComponent
|
||||
className={`${ENV.IS_LOCAL_DEV || ENV.IS_PREVIEW ? 'max-h-[calc(100dvh-48px-40px)]' : 'max-h-[calc(100dvh-48px)]'}`}
|
||||
className="max-h-[calc(100dvh-48px)]"
|
||||
defer
|
||||
options={{
|
||||
overflow: { x: 'hidden' },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import rehypeShikiFromHighlighter from '@shikijs/rehype/core';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { MDXRemote } from 'next-mdx-remote-client/rsc';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { getHighlighterCore } from 'shiki/core';
|
||||
import getWasm from 'shiki/wasm';
|
||||
@@ -30,7 +30,7 @@ export default async function Page({ params }: { readonly params: { readonly pac
|
||||
remarkPlugins: [remarkGfm],
|
||||
rehypePlugins: [
|
||||
[
|
||||
rehypeShikiFromHighlighter as any,
|
||||
rehypeShikiFromHighlighter,
|
||||
highlighter,
|
||||
{
|
||||
themes: {
|
||||
|
||||
@@ -20,11 +20,7 @@ export const viewport: Viewport = {
|
||||
};
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(
|
||||
process.env.NEXT_PUBLIC_LOCAL_DEV === 'true'
|
||||
? `http://localhost:${process.env.PORT ?? 3_000}`
|
||||
: 'https://discord.js.org',
|
||||
),
|
||||
metadataBase: new URL(ENV.IS_LOCAL_DEV ? `http://localhost:${ENV.PORT}` : 'https://discord.js.org'),
|
||||
title: {
|
||||
template: '%s | discord.js',
|
||||
default: 'discord.js',
|
||||
|
||||
@@ -18,8 +18,9 @@ export async function Badges({ node }: { readonly node: any }) {
|
||||
const isAbstract = node.isAbstract;
|
||||
const isReadonly = node.isReadonly;
|
||||
const isOptional = node.isOptional;
|
||||
const isExternal = node.isExternal;
|
||||
|
||||
const isAny = isDeprecated || isProtected || isStatic || isAbstract || isReadonly || isOptional;
|
||||
const isAny = isDeprecated || isProtected || isStatic || isAbstract || isReadonly || isOptional || isExternal;
|
||||
|
||||
return isAny ? (
|
||||
<div className="mb-1 flex gap-3">
|
||||
@@ -33,6 +34,7 @@ export async function Badges({ node }: { readonly node: any }) {
|
||||
{isAbstract ? <Badge className="bg-cyan-500/20 text-cyan-500">abstract</Badge> : null}
|
||||
{isReadonly ? <Badge className="bg-purple-500/20 text-purple-500">readonly</Badge> : null}
|
||||
{isOptional ? <Badge className="bg-cyan-500/20 text-cyan-500">optional</Badge> : null}
|
||||
{isExternal ? <Badge className="bg-purple-500/20 text-purple-500">external</Badge> : null}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { BuiltinDocumentationLinks } from '~/util/builtinDocumentationLinks';
|
||||
import { OverlayScrollbarsComponent } from './OverlayScrollbars';
|
||||
import { SyntaxHighlighter } from './SyntaxHighlighter';
|
||||
|
||||
@@ -28,6 +29,21 @@ export async function DocNode({ node, version }: { readonly node?: any; readonly
|
||||
href={node.uri}
|
||||
rel="external noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
{`${node.text}${node.members ?? ''}`}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (node.text in BuiltinDocumentationLinks) {
|
||||
const href = BuiltinDocumentationLinks[node.text as keyof typeof BuiltinDocumentationLinks];
|
||||
return (
|
||||
<a
|
||||
key={`${node.text}-${idx}`}
|
||||
className="text-blurple hover:text-blurple-500 dark:hover:text-blurple-300"
|
||||
href={href}
|
||||
rel="external noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
{node.text}
|
||||
</a>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted'
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { fetchSitemap } from '~/util/fetchSitemap';
|
||||
import { fetchVersions } from '~/util/fetchVersions';
|
||||
import { resolveNodeKind } from './DocKind';
|
||||
@@ -28,6 +29,11 @@ export async function Navigation({
|
||||
readonly version: string;
|
||||
}) {
|
||||
const node = await fetchSitemap({ packageName, version });
|
||||
|
||||
if (!node) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const versions = await fetchVersions(packageName);
|
||||
|
||||
const groupedNodes = node.reduce((acc: any, node: any) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useAtom, useSetAtom } from 'jotai';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import MeiliSearch from 'meilisearch';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useDebounceValue } from 'usehooks-ts';
|
||||
import { isCmdKOpenAtom } from '~/stores/cmdk';
|
||||
import { isDrawerOpenAtom } from '~/stores/drawer';
|
||||
@@ -25,32 +25,29 @@ export function CmdK({ dependencies }: { readonly dependencies: string[] }) {
|
||||
const [search, setSearch] = useDebounceValue('', 250);
|
||||
const [searchResults, setSearchResults] = useState<any[]>([]);
|
||||
|
||||
const packageName = useMemo(() => pathname?.split('/').slice(3, 4)[0], [pathname]);
|
||||
const branchName = useMemo(() => pathname?.split('/').slice(4, 5)[0], [pathname]);
|
||||
const packageName = pathname?.split('/').slice(3, 4)[0];
|
||||
const branchName = pathname?.split('/').slice(4, 5)[0];
|
||||
|
||||
const searchResultItems = useMemo(
|
||||
() =>
|
||||
searchResults?.map((item, idx) => (
|
||||
<Command.Item
|
||||
key={`${item.id}-${idx}`}
|
||||
className="flex cursor-pointer place-items-center gap-2 rounded-md p-2 data-[selected]:bg-neutral-200 dark:data-[selected]:bg-neutral-800"
|
||||
onSelect={() => {
|
||||
router.push(item.path);
|
||||
setOpen(false);
|
||||
}}
|
||||
value={item.id}
|
||||
>
|
||||
{resolveKind(item.kind)}
|
||||
<div className="flex flex-grow flex-col">
|
||||
<span className="font-semibold">{item.name}</span>
|
||||
<span className="line-clamp-1 text-sm">{item.summary}</span>
|
||||
<span className="truncate text-xs">{item.path}</span>
|
||||
</div>
|
||||
<ArrowRight aria-hidden className="flex-shrink-0" />
|
||||
</Command.Item>
|
||||
)) ?? [],
|
||||
[router, searchResults, setOpen],
|
||||
);
|
||||
const searchResultItems =
|
||||
searchResults?.map((item, idx) => (
|
||||
<Command.Item
|
||||
key={`${item.id}-${idx}`}
|
||||
className="flex cursor-pointer place-items-center gap-2 rounded-md p-2 data-[selected='true']:bg-neutral-200 dark:data-[selected='true']:bg-neutral-800"
|
||||
onSelect={() => {
|
||||
router.push(item.path);
|
||||
setOpen(false);
|
||||
}}
|
||||
value={item.id}
|
||||
>
|
||||
{resolveKind(item.kind)}
|
||||
<div className="flex flex-grow flex-col">
|
||||
<span className="font-semibold">{item.name}</span>
|
||||
<span className="line-clamp-1 text-sm">{item.summary}</span>
|
||||
<span className="truncate text-xs">{item.path}</span>
|
||||
</div>
|
||||
<ArrowRight aria-hidden className="flex-shrink-0" />
|
||||
</Command.Item>
|
||||
)) ?? [];
|
||||
|
||||
// Toggle the menu when ⌘K is pressed
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const ENV = {
|
||||
IS_LOCAL_DEV: process.env.VERCEL_ENV === 'development' || process.env.NEXT_PUBLIC_LOCAL_DEV === 'true',
|
||||
IS_PREVIEW: process.env.VERCEL_ENV === 'preview',
|
||||
PORT: process.env.PORT ?? 3_000,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ENV } from './env';
|
||||
|
||||
export async function fetchNode({
|
||||
@@ -15,30 +14,26 @@ export async function fetchNode({
|
||||
const normalizeItem = item.split(encodeURIComponent(':')).join('.').toLowerCase();
|
||||
|
||||
if (ENV.IS_LOCAL_DEV) {
|
||||
try {
|
||||
const fileContent = await readFile(
|
||||
join(
|
||||
process.cwd(),
|
||||
`../../packages/${packageName}/docs/${packageName}/split/${version}.${normalizeItem}.api.json`,
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
return JSON.parse(fileContent);
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const isMainVersion = version === 'main';
|
||||
const fileContent = await fetch(
|
||||
`${process.env.BLOB_STORAGE_URL}/rewrite/${packageName}/${version}.${normalizeItem}.api.json`,
|
||||
{ next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
|
||||
const fileContent = await readFile(
|
||||
join(
|
||||
process.cwd(),
|
||||
`../../packages/${packageName}/docs/${packageName}/split/${version}.${normalizeItem}.api.json`,
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
return await fileContent.json();
|
||||
} catch {
|
||||
notFound();
|
||||
return JSON.parse(fileContent);
|
||||
}
|
||||
|
||||
const isMainVersion = version === 'main';
|
||||
const fileContent = await fetch(
|
||||
`${process.env.BLOB_STORAGE_URL}/rewrite/${packageName}/${version}.${normalizeItem}.api.json`,
|
||||
{ next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
|
||||
);
|
||||
|
||||
if (!fileContent.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fileContent.json();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ENV } from './env';
|
||||
|
||||
export async function fetchSitemap({
|
||||
@@ -11,27 +10,19 @@ export async function fetchSitemap({
|
||||
readonly version: string;
|
||||
}) {
|
||||
if (ENV.IS_LOCAL_DEV) {
|
||||
try {
|
||||
const fileContent = await readFile(
|
||||
join(process.cwd(), `../../packages/${packageName}/docs/${packageName}/split/${version}.sitemap.api.json`),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
return JSON.parse(fileContent);
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const isMainVersion = version === 'main';
|
||||
const fileContent = await fetch(
|
||||
`${process.env.BLOB_STORAGE_URL}/rewrite/${packageName}/${version}.sitemap.api.json`,
|
||||
{ next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
|
||||
const fileContent = await readFile(
|
||||
join(process.cwd(), `../../packages/${packageName}/docs/${packageName}/split/${version}.sitemap.api.json`),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
return await fileContent.json();
|
||||
} catch {
|
||||
notFound();
|
||||
return JSON.parse(fileContent);
|
||||
}
|
||||
|
||||
const isMainVersion = version === 'main';
|
||||
const fileContent = await fetch(
|
||||
`${process.env.BLOB_STORAGE_URL}/rewrite/${packageName}/${version}.sitemap.api.json`,
|
||||
{ next: isMainVersion ? { revalidate: 0 } : { revalidate: 604_800 } },
|
||||
);
|
||||
|
||||
return fileContent.json();
|
||||
}
|
||||
|
||||
44
package.json
44
package.json
@@ -6,19 +6,19 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "turbo run build --concurrency=4",
|
||||
"build:affected": "turbo run build --filter='...[origin/main]' --concurrency=4",
|
||||
"build:apps": "turbo run build:local --filter='...{apps/*}' --concurrency=4",
|
||||
"build:apps:affected": "turbo run build:local --filter='...{apps/*}[origin/main]' --concurrency=4",
|
||||
"build:affected": "turbo run build --filter=...[origin/main] --concurrency=4",
|
||||
"build:apps": "turbo run build:local --filter=...{apps/*} --concurrency=4",
|
||||
"build:apps:affected": "turbo run build:local --filter=...{apps/*}[origin/main] --concurrency=4",
|
||||
"test": "turbo run test --concurrency=4",
|
||||
"test:affected": "turbo run test --filter='...[origin/main]' --concurrency=4",
|
||||
"test:affected": "turbo run test --filter=...[origin/main] --concurrency=4",
|
||||
"lint": "turbo run lint --concurrency=4",
|
||||
"lint:affected": "turbo run lint --filter='...[origin/main]' --concurrency=4",
|
||||
"lint:affected": "turbo run lint --filter=...[origin/main] --concurrency=4",
|
||||
"format": "turbo run format --concurrency=4",
|
||||
"format:affected": "turbo run format --filter='...[origin/main]' --concurrency=4",
|
||||
"format:affected": "turbo run format --filter=...[origin/main] --concurrency=4",
|
||||
"fmt": "turbo run format --concurrency=4",
|
||||
"fmt:affected": "turbo run format --filter='...[origin/main]' --concurrency=4",
|
||||
"fmt:affected": "turbo run format --filter=...[origin/main] --concurrency=4",
|
||||
"docs": "turbo run docs --concurrency=4",
|
||||
"docs:affected": "turbo run docs --filter='...[origin/main]' --concurrency=4",
|
||||
"docs:affected": "turbo run docs --filter=...[origin/main] --concurrency=4",
|
||||
"prepare": "is-ci || husky",
|
||||
"update": "pnpm --recursive update --interactive",
|
||||
"update:latest": "pnpm --recursive update --interactive --latest",
|
||||
@@ -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.59.3",
|
||||
"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@8.15.7+sha256.50783dd0fa303852de2dd1557cd4b9f07cb5b018154a6e76d0f40635d6cee019"
|
||||
"packageManager": "pnpm@9.1.4"
|
||||
}
|
||||
|
||||
@@ -49,20 +49,20 @@
|
||||
"meilisearch": "^0.38.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"tslib": "^2.6.2",
|
||||
"undici": "6.13.0"
|
||||
"undici": "6.21.1"
|
||||
},
|
||||
"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"
|
||||
|
||||
@@ -9,7 +9,7 @@ runs:
|
||||
with:
|
||||
swap-size-gb: 10
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
- uses: pnpm/action-setup@v4.0.0
|
||||
name: Install pnpm
|
||||
with:
|
||||
run_install: false
|
||||
@@ -26,7 +26,7 @@ runs:
|
||||
run: |
|
||||
echo "YEAR_MONTH=$(/bin/date -u "+%Y%m")" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-config.outputs.STORE_PATH }}
|
||||
|
||||
@@ -1,88 +1,120 @@
|
||||
name: 'Upload Coverage'
|
||||
description: 'Uploads code coverage reports to codecov with separate flags for separate packages'
|
||||
inputs:
|
||||
codecov_token:
|
||||
description: 'Codecov token.'
|
||||
required: true
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Upload Guide Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./apps/guide/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: guide
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Website Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./apps/website/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: website
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Brokers Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/brokers/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: brokers
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Builders Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/builders/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: builders
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Collection Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/collection/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: collection
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Discord.js Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/discord.js/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: discord.js
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Formatters Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/formatters/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: formatters
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Next Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/next/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: next
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Proxy Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/proxy/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: proxy
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Rest Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/rest/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: rest
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Voice Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/voice/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: voice
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload WS Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/ws/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: ws
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Util Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./packages/util/coverage/cobertura-coverage.xml
|
||||
disable_search: true
|
||||
flags: util
|
||||
token: ${{ inputs.CODECOV_TOKEN }}
|
||||
|
||||
- name: Upload Utilities Coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
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 }}
|
||||
|
||||
@@ -21,7 +21,7 @@ runs:
|
||||
echo "NPM_GLOBAL_CACHE_FOLDER=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: yarn-download-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-config.outputs.CACHE_FOLDER }}
|
||||
@@ -31,7 +31,7 @@ runs:
|
||||
|
||||
- name: Restore global npm cache folder
|
||||
id: npm-global-cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.yarn-config.outputs.NPM_GLOBAL_CACHE_FOLDER }}
|
||||
key: npm-global-cache-default-${{ runner.os }}-${{ steps.yarn-config.outputs.CURRENT_NODE_VERSION }}-${{ hashFiles(yarn.lock, .yarnrc.yml) }}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { type ApiItem, ApiItemKind } from '../items/ApiItem.js';
|
||||
import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import type { ApiEntryPoint } from './ApiEntryPoint.js';
|
||||
import type { ApiMethod } from './ApiMethod.js';
|
||||
import type { ApiModel } from './ApiModel.js';
|
||||
import type { ApiPackage } from './ApiPackage.js';
|
||||
|
||||
@@ -114,11 +115,21 @@ 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
|
||||
) {
|
||||
currentItem = foundClass;
|
||||
} else if (
|
||||
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;
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pnpm add @discordjs/brokers
|
||||
import { PubSubRedisBroker } from '@discordjs/brokers';
|
||||
import Redis from 'ioredis';
|
||||
|
||||
const broker = new PubSubRedisBroker({ redisClient: new Redis() });
|
||||
const broker = new PubSubRedisBroker(new Redis());
|
||||
|
||||
await broker.publish('test', 'Hello World!');
|
||||
await broker.destroy();
|
||||
@@ -49,7 +49,7 @@ await broker.destroy();
|
||||
import { PubSubRedisBroker } from '@discordjs/brokers';
|
||||
import Redis from 'ioredis';
|
||||
|
||||
const broker = new PubSubRedisBroker({ redisClient: new Redis() });
|
||||
const broker = new PubSubRedisBroker(new Redis());
|
||||
broker.on('test', ({ data, ack }) => {
|
||||
console.log(data);
|
||||
void ack();
|
||||
@@ -65,7 +65,7 @@ await broker.subscribe('subscribers', ['test']);
|
||||
import { RPCRedisBroker } from '@discordjs/brokers';
|
||||
import Redis from 'ioredis';
|
||||
|
||||
const broker = new RPCRedisBroker({ redisClient: new Redis() });
|
||||
const broker = new RPCRedisBroker(new Redis());
|
||||
|
||||
console.log(await broker.call('testcall', 'Hello World!'));
|
||||
await broker.destroy();
|
||||
@@ -74,7 +74,7 @@ await broker.destroy();
|
||||
import { RPCRedisBroker } from '@discordjs/brokers';
|
||||
import Redis from 'ioredis';
|
||||
|
||||
const broker = new RPCRedisBroker({ redisClient: new Redis() });
|
||||
const broker = new RPCRedisBroker(new Redis());
|
||||
broker.on('testcall', ({ data, ack, reply }) => {
|
||||
console.log('responder', data);
|
||||
void ack();
|
||||
|
||||
@@ -17,7 +17,7 @@ const mockRedisClient = {
|
||||
test('pubsub with custom encoding', async () => {
|
||||
const encode = vi.fn((data) => data);
|
||||
|
||||
const broker = new PubSubRedisBroker({ redisClient: mockRedisClient, encode });
|
||||
const broker = new PubSubRedisBroker(mockRedisClient, { encode });
|
||||
await broker.publish('test', 'test');
|
||||
expect(encode).toHaveBeenCalledWith('test');
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { encode, decode } from '@msgpack/msgpack';
|
||||
import type { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
||||
|
||||
@@ -7,10 +6,6 @@ import type { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
||||
* Base options for a broker implementation
|
||||
*/
|
||||
export interface BaseBrokerOptions {
|
||||
/**
|
||||
* How long to block for messages when polling
|
||||
*/
|
||||
blockTimeout?: number;
|
||||
/**
|
||||
* Function to use for decoding messages
|
||||
*/
|
||||
@@ -21,25 +16,12 @@ export interface BaseBrokerOptions {
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
||||
encode?: (data: unknown) => Buffer;
|
||||
/**
|
||||
* Max number of messages to poll at once
|
||||
*/
|
||||
maxChunk?: number;
|
||||
/**
|
||||
* Unique consumer name.
|
||||
*
|
||||
* @see {@link https://redis.io/commands/xreadgroup/}
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default broker options
|
||||
*/
|
||||
export const DefaultBrokerOptions = {
|
||||
name: randomBytes(20).toString('hex'),
|
||||
maxChunk: 10,
|
||||
blockTimeout: 5_000,
|
||||
encode: (data): Buffer => {
|
||||
const encoded = encode(data);
|
||||
return Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);
|
||||
@@ -60,13 +42,13 @@ export type ToEventMap<
|
||||
|
||||
export interface IBaseBroker<TEvents extends Record<string, any>> {
|
||||
/**
|
||||
* Subscribes to the given events, grouping them by the given group name
|
||||
* Subscribes to the given events
|
||||
*/
|
||||
subscribe(group: string, events: (keyof TEvents)[]): Promise<void>;
|
||||
subscribe(events: (keyof TEvents)[]): Promise<void>;
|
||||
/**
|
||||
* Unsubscribes from the given events - it's required to pass the same group name as when subscribing for proper cleanup
|
||||
* Unsubscribes from the given events
|
||||
*/
|
||||
unsubscribe(group: string, events: (keyof TEvents)[]): Promise<void>;
|
||||
unsubscribe(events: (keyof TEvents)[]): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IPubSubBroker<TEvents extends Record<string, any>>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Buffer } from 'node:buffer';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
||||
@@ -19,11 +20,40 @@ declare module 'ioredis' {
|
||||
*/
|
||||
export interface RedisBrokerOptions extends BaseBrokerOptions {
|
||||
/**
|
||||
* The Redis client to use
|
||||
* How long to block for messages when polling
|
||||
*/
|
||||
redisClient: Redis;
|
||||
blockTimeout?: number;
|
||||
|
||||
/**
|
||||
* Consumer group name to use for this broker
|
||||
*
|
||||
* @see {@link https://redis.io/commands/xreadgroup/}
|
||||
*/
|
||||
group: string;
|
||||
|
||||
/**
|
||||
* Max number of messages to poll at once
|
||||
*/
|
||||
maxChunk?: number;
|
||||
|
||||
/**
|
||||
* Unique consumer name.
|
||||
*
|
||||
* @see {@link https://redis.io/commands/xreadgroup/}
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default broker options for redis
|
||||
*/
|
||||
export const DefaultRedisBrokerOptions = {
|
||||
...DefaultBrokerOptions,
|
||||
name: randomBytes(20).toString('hex'),
|
||||
maxChunk: 10,
|
||||
blockTimeout: 5_000,
|
||||
} as const satisfies Required<Omit<RedisBrokerOptions, 'group'>>;
|
||||
|
||||
/**
|
||||
* Helper class with shared Redis logic
|
||||
*/
|
||||
@@ -56,26 +86,29 @@ export abstract class BaseRedisBroker<TEvents extends Record<string, any>>
|
||||
*/
|
||||
protected listening = false;
|
||||
|
||||
public constructor(options: RedisBrokerOptions) {
|
||||
public constructor(
|
||||
protected readonly redisClient: Redis,
|
||||
options: RedisBrokerOptions,
|
||||
) {
|
||||
super();
|
||||
this.options = { ...DefaultBrokerOptions, ...options };
|
||||
options.redisClient.defineCommand('xcleangroup', {
|
||||
this.options = { ...DefaultRedisBrokerOptions, ...options };
|
||||
redisClient.defineCommand('xcleangroup', {
|
||||
numberOfKeys: 1,
|
||||
lua: readFileSync(resolve(__dirname, '..', 'scripts', 'xcleangroup.lua'), 'utf8'),
|
||||
});
|
||||
this.streamReadClient = options.redisClient.duplicate();
|
||||
this.streamReadClient = redisClient.duplicate();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc IBaseBroker.subscribe}
|
||||
*/
|
||||
public async subscribe(group: string, events: (keyof TEvents)[]): Promise<void> {
|
||||
public async subscribe(events: (keyof TEvents)[]): Promise<void> {
|
||||
await Promise.all(
|
||||
// @ts-expect-error: Intended
|
||||
events.map(async (event) => {
|
||||
this.subscribedEvents.add(event as string);
|
||||
try {
|
||||
return await this.options.redisClient.xgroup('CREATE', event as string, group, 0, 'MKSTREAM');
|
||||
return await this.redisClient.xgroup('CREATE', event as string, this.options.group, 0, 'MKSTREAM');
|
||||
} catch (error) {
|
||||
if (!(error instanceof ReplyError)) {
|
||||
throw error;
|
||||
@@ -83,21 +116,21 @@ export abstract class BaseRedisBroker<TEvents extends Record<string, any>>
|
||||
}
|
||||
}),
|
||||
);
|
||||
void this.listen(group);
|
||||
void this.listen();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc IBaseBroker.unsubscribe}
|
||||
*/
|
||||
public async unsubscribe(group: string, events: (keyof TEvents)[]): Promise<void> {
|
||||
public async unsubscribe(events: (keyof TEvents)[]): Promise<void> {
|
||||
const commands: unknown[][] = Array.from({ length: events.length * 2 });
|
||||
for (let idx = 0; idx < commands.length; idx += 2) {
|
||||
const event = events[idx / 2];
|
||||
commands[idx] = ['xgroup', 'delconsumer', event as string, group, this.options.name];
|
||||
commands[idx + 1] = ['xcleangroup', event as string, group];
|
||||
commands[idx] = ['xgroup', 'delconsumer', event as string, this.options.group, this.options.name];
|
||||
commands[idx + 1] = ['xcleangroup', event as string, this.options.group];
|
||||
}
|
||||
|
||||
await this.options.redisClient.pipeline(commands).exec();
|
||||
await this.redisClient.pipeline(commands).exec();
|
||||
|
||||
for (const event of events) {
|
||||
this.subscribedEvents.delete(event as string);
|
||||
@@ -107,18 +140,18 @@ export abstract class BaseRedisBroker<TEvents extends Record<string, any>>
|
||||
/**
|
||||
* Begins polling for events, firing them to {@link BaseRedisBroker.listen}
|
||||
*/
|
||||
protected async listen(group: string): Promise<void> {
|
||||
protected async listen(): Promise<void> {
|
||||
if (this.listening) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.listening = true;
|
||||
|
||||
while (true) {
|
||||
while (this.subscribedEvents.size > 0) {
|
||||
try {
|
||||
const data = await this.streamReadClient.xreadgroupBuffer(
|
||||
'GROUP',
|
||||
group,
|
||||
this.options.group,
|
||||
this.options.name,
|
||||
'COUNT',
|
||||
String(this.options.maxChunk),
|
||||
@@ -145,7 +178,7 @@ export abstract class BaseRedisBroker<TEvents extends Record<string, any>>
|
||||
continue;
|
||||
}
|
||||
|
||||
this.emitEvent(id, group, event.toString('utf8'), this.options.decode(data));
|
||||
this.emitEvent(id, this.options.group, event.toString('utf8'), this.options.decode(data));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -161,8 +194,9 @@ export abstract class BaseRedisBroker<TEvents extends Record<string, any>>
|
||||
* Destroys the broker, closing all connections
|
||||
*/
|
||||
public async destroy() {
|
||||
await this.unsubscribe([...this.subscribedEvents]);
|
||||
this.streamReadClient.disconnect();
|
||||
this.options.redisClient.disconnect();
|
||||
this.redisClient.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import { BaseRedisBroker } from './BaseRedis.js';
|
||||
* import { PubSubRedisBroker } from '@discordjs/brokers';
|
||||
* import Redis from 'ioredis';
|
||||
*
|
||||
* const broker = new PubSubRedisBroker({ redisClient: new Redis() });
|
||||
* const broker = new PubSubRedisBroker(new Redis());
|
||||
*
|
||||
* await broker.publish('test', 'Hello World!');
|
||||
* await broker.destroy();
|
||||
@@ -20,7 +20,7 @@ import { BaseRedisBroker } from './BaseRedis.js';
|
||||
* import { PubSubRedisBroker } from '@discordjs/brokers';
|
||||
* import Redis from 'ioredis';
|
||||
*
|
||||
* const broker = new PubSubRedisBroker({ redisClient: new Redis() });
|
||||
* const broker = new PubSubRedisBroker(new Redis());
|
||||
* broker.on('test', ({ data, ack }) => {
|
||||
* console.log(data);
|
||||
* void ack();
|
||||
@@ -37,19 +37,14 @@ export class PubSubRedisBroker<TEvents extends Record<string, any>>
|
||||
* {@inheritDoc IPubSubBroker.publish}
|
||||
*/
|
||||
public async publish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void> {
|
||||
await this.options.redisClient.xadd(
|
||||
event as string,
|
||||
'*',
|
||||
BaseRedisBroker.STREAM_DATA_KEY,
|
||||
this.options.encode(data),
|
||||
);
|
||||
await this.redisClient.xadd(event as string, '*', BaseRedisBroker.STREAM_DATA_KEY, this.options.encode(data));
|
||||
}
|
||||
|
||||
protected emitEvent(id: Buffer, group: string, event: string, data: unknown) {
|
||||
const payload: { ack(): Promise<void>; data: unknown } = {
|
||||
data,
|
||||
ack: async () => {
|
||||
await this.options.redisClient.xack(event, group, id);
|
||||
await this.redisClient.xack(event, group, id);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Buffer } from 'node:buffer';
|
||||
import { clearTimeout, setTimeout } from 'node:timers';
|
||||
import type Redis from 'ioredis/built/Redis.js';
|
||||
import type { IRPCBroker } from '../Broker.js';
|
||||
import { DefaultBrokerOptions } from '../Broker.js';
|
||||
import type { RedisBrokerOptions } from './BaseRedis.js';
|
||||
import { BaseRedisBroker } from './BaseRedis.js';
|
||||
import { BaseRedisBroker, DefaultRedisBrokerOptions } from './BaseRedis.js';
|
||||
|
||||
interface InternalPromise {
|
||||
reject(error: any): void;
|
||||
@@ -22,9 +22,9 @@ export interface RPCRedisBrokerOptions extends RedisBrokerOptions {
|
||||
* Default values used for the {@link RPCRedisBrokerOptions}
|
||||
*/
|
||||
export const DefaultRPCRedisBrokerOptions = {
|
||||
...DefaultBrokerOptions,
|
||||
...DefaultRedisBrokerOptions,
|
||||
timeout: 5_000,
|
||||
} as const satisfies Required<Omit<RPCRedisBrokerOptions, 'redisClient'>>;
|
||||
} as const satisfies Required<Omit<RPCRedisBrokerOptions, 'group'>>;
|
||||
|
||||
/**
|
||||
* RPC broker powered by Redis
|
||||
@@ -35,7 +35,7 @@ export const DefaultRPCRedisBrokerOptions = {
|
||||
* import { RPCRedisBroker } from '@discordjs/brokers';
|
||||
* import Redis from 'ioredis';
|
||||
*
|
||||
* const broker = new RPCRedisBroker({ redisClient: new Redis() });
|
||||
* const broker = new RPCRedisBroker(new Redis());
|
||||
*
|
||||
* console.log(await broker.call('testcall', 'Hello World!'));
|
||||
* await broker.destroy();
|
||||
@@ -44,7 +44,7 @@ export const DefaultRPCRedisBrokerOptions = {
|
||||
* import { RPCRedisBroker } from '@discordjs/brokers';
|
||||
* import Redis from 'ioredis';
|
||||
*
|
||||
* const broker = new RPCRedisBroker({ redisClient: new Redis() });
|
||||
* const broker = new RPCRedisBroker(new Redis());
|
||||
* broker.on('testcall', ({ data, ack, reply }) => {
|
||||
* console.log('responder', data);
|
||||
* void ack();
|
||||
@@ -65,8 +65,8 @@ export class RPCRedisBroker<TEvents extends Record<string, any>, TResponses exte
|
||||
|
||||
protected readonly promises = new Map<string, InternalPromise>();
|
||||
|
||||
public constructor(options: RPCRedisBrokerOptions) {
|
||||
super(options);
|
||||
public constructor(redisClient: Redis, options: RPCRedisBrokerOptions) {
|
||||
super(redisClient, options);
|
||||
this.options = { ...DefaultRPCRedisBrokerOptions, ...options };
|
||||
|
||||
this.streamReadClient.on('messageBuffer', (channel: Buffer, message: Buffer) => {
|
||||
@@ -88,7 +88,7 @@ export class RPCRedisBroker<TEvents extends Record<string, any>, TResponses exte
|
||||
data: TEvents[Event],
|
||||
timeoutDuration: number = this.options.timeout,
|
||||
): Promise<TResponses[Event]> {
|
||||
const id = await this.options.redisClient.xadd(
|
||||
const id = await this.redisClient.xadd(
|
||||
event as string,
|
||||
'*',
|
||||
BaseRedisBroker.STREAM_DATA_KEY,
|
||||
@@ -114,14 +114,14 @@ export class RPCRedisBroker<TEvents extends Record<string, any>, TResponses exte
|
||||
});
|
||||
}
|
||||
|
||||
protected emitEvent(id: Buffer, group: string, event: string, data: unknown) {
|
||||
protected emitEvent(id: Buffer, event: string, data: unknown) {
|
||||
const payload: { ack(): Promise<void>; data: unknown; reply(data: unknown): Promise<void> } = {
|
||||
data,
|
||||
ack: async () => {
|
||||
await this.options.redisClient.xack(event, group, id);
|
||||
await this.redisClient.xack(event, this.options.group, id);
|
||||
},
|
||||
reply: async (data) => {
|
||||
await this.options.redisClient.publish(`${event}:${id.toString()}`, this.options.encode(data));
|
||||
await this.redisClient.publish(`${event}:${id.toString()}`, this.options.encode(data));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,47 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
# [@discordjs/builders@1.8.2](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.8.1...@discordjs/builders@1.8.2) - (2024-06-02)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **SlashCommandBuilder:** Add missing shared properties (#10255) ([29fd89f](https://github.com/discordjs/discord.js/commit/29fd89f23c22ac5b4ce0a3ed34f5d27e28b1a0b8))
|
||||
|
||||
## Documentation
|
||||
|
||||
- **SelectMenuBuilder:** Correct grammatical errors (#10309) ([aae2faf](https://github.com/discordjs/discord.js/commit/aae2faf9e923a268f84c8b7fb3283aea09dca586))
|
||||
- **TextInputBuilder:** Correct constructor documentation (#10308) ([c1e6890](https://github.com/discordjs/discord.js/commit/c1e6890132d5597a6ebd9d79383ec572582c0601))
|
||||
- **MappedComponentTypes:** Fix "inpiut" typo (#10306) ([29a50bb](https://github.com/discordjs/discord.js/commit/29a50bb476e8e84896dbaec96c6009589afaafbf))
|
||||
|
||||
# [@discordjs/builders@1.8.1](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.8.0...@discordjs/builders@1.8.1) - (2024-05-05)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Slashcommand builder type split (#10253) ([07c1210](https://github.com/discordjs/discord.js/commit/07c12101e534fdce836a94bc571b53f75979ea86))
|
||||
- Don't mutate user provided array (#10014) ([7ea3638](https://github.com/discordjs/discord.js/commit/7ea3638dbcf38926596fb5da8b85040e70f1b98b))
|
||||
- Minify mainlib docs json (#9963) ([4b88306](https://github.com/discordjs/discord.js/commit/4b88306dcb2b16b840ec61e9e33047af3a31c45d))
|
||||
|
||||
## Documentation
|
||||
|
||||
- Split docs.api.json into multiple json files ([597340f](https://github.com/discordjs/discord.js/commit/597340f288437c35da8c703d9b621274de60d880))
|
||||
|
||||
## Features
|
||||
|
||||
- **api-extractor:** Support `export * as ___` syntax (#10173) ([1c5de21](https://github.com/discordjs/discord.js/commit/1c5de21a2905fe21b54dea805013f089ed9000d0))
|
||||
- Allow RestOrArray for command option builders (#10175) ([a1a3a95](https://github.com/discordjs/discord.js/commit/a1a3a95c94194a8ab789d567a778b376e13ea973))
|
||||
- Local and preview detection ([79fbda3](https://github.com/discordjs/discord.js/commit/79fbda3aac6d4f0f8bfb193e797d09cbe331d315))
|
||||
|
||||
## Refactor
|
||||
|
||||
- Docs (#10126) ([18cce83](https://github.com/discordjs/discord.js/commit/18cce83d80598c430218775c53441b6b2ecdc776))
|
||||
- Make builders types great again (#10026) ([a0c83a2](https://github.com/discordjs/discord.js/commit/a0c83a254c21dad5ac14b649a95ded57d6678d95))
|
||||
|
||||
# [@discordjs/builders@1.8.1](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.8.0...@discordjs/builders@1.8.1) - (2024-05-05)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Slashcommand builder type split (#10253) ([07c1210](https://github.com/discordjs/discord.js/commit/07c12101e534fdce836a94bc571b53f75979ea86))
|
||||
|
||||
# [@discordjs/builders@1.8.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.7.0...@discordjs/builders@1.8.0) - (2024-05-04)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
@@ -50,6 +50,11 @@ describe('Button Components', () => {
|
||||
button.toJSON();
|
||||
}).not.toThrowError();
|
||||
|
||||
expect(() => {
|
||||
const button = buttonComponent().setSKUId('123456789012345678').setStyle(ButtonStyle.Premium);
|
||||
button.toJSON();
|
||||
}).not.toThrowError();
|
||||
|
||||
expect(() => buttonComponent().setURL('https://google.com')).not.toThrowError();
|
||||
});
|
||||
|
||||
@@ -101,6 +106,47 @@ describe('Button Components', () => {
|
||||
button.toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
const button = buttonComponent().setStyle(ButtonStyle.Primary).setSKUId('123456789012345678');
|
||||
button.toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
const button = buttonComponent()
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setLabel('button')
|
||||
.setSKUId('123456789012345678');
|
||||
|
||||
button.toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
const button = buttonComponent()
|
||||
.setStyle(ButtonStyle.Success)
|
||||
.setEmoji({ name: '😇' })
|
||||
.setSKUId('123456789012345678');
|
||||
|
||||
button.toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
const button = buttonComponent()
|
||||
.setStyle(ButtonStyle.Danger)
|
||||
.setCustomId('test')
|
||||
.setSKUId('123456789012345678');
|
||||
|
||||
button.toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
expect(() => {
|
||||
const button = buttonComponent()
|
||||
.setStyle(ButtonStyle.Link)
|
||||
.setURL('https://google.com')
|
||||
.setSKUId('123456789012345678');
|
||||
|
||||
button.toJSON();
|
||||
}).toThrowError();
|
||||
|
||||
// @ts-expect-error: Invalid style
|
||||
expect(() => buttonComponent().setStyle(24)).toThrowError();
|
||||
expect(() => buttonComponent().setLabel(longStr)).toThrowError();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
@@ -357,6 +363,10 @@ describe('Slash Commands', () => {
|
||||
getBuilder().addStringOption(getStringOption().setChoices({ name: 'owo', value: 'uwu' })),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder with NSFW, THEN does not throw error', () => {
|
||||
expect(() => getBuilder().setName('foo').setDescription('foo').setNSFW(true)).not.toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Builder with subcommand (group) options', () => {
|
||||
@@ -519,6 +529,60 @@ describe('Slash Commands', () => {
|
||||
|
||||
expect(() => getBuilder().setDefaultMemberPermissions(1.1)).toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid permission with options THEN does not throw error', () => {
|
||||
expect(() =>
|
||||
getBuilder().addBooleanOption(getBooleanOption()).setDefaultMemberPermissions('1'),
|
||||
).not.toThrowError();
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
17
packages/builders/__tests__/types.test.ts
Normal file
17
packages/builders/__tests__/types.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { expectTypeOf } from 'vitest';
|
||||
import { SlashCommandBuilder, SlashCommandStringOption, SlashCommandSubcommandBuilder } from '../src/index.js';
|
||||
|
||||
const getBuilder = () => new SlashCommandBuilder();
|
||||
const getStringOption = () => new SlashCommandStringOption().setName('owo').setDescription('Testing 123');
|
||||
const getSubcommand = () => new SlashCommandSubcommandBuilder().setName('owo').setDescription('Testing 123');
|
||||
|
||||
type BuilderPropsOnly<Type = SlashCommandBuilder> = Pick<
|
||||
Type,
|
||||
keyof {
|
||||
[Key in keyof Type as Type[Key] extends (...args: any) => any ? never : Key]: any;
|
||||
}
|
||||
>;
|
||||
|
||||
expectTypeOf(getBuilder().addStringOption(getStringOption())).toMatchTypeOf<BuilderPropsOnly>();
|
||||
|
||||
expectTypeOf(getBuilder().addSubcommand(getSubcommand())).toMatchTypeOf<BuilderPropsOnly>();
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@discordjs/builders",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.2",
|
||||
"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": "^3.9.7",
|
||||
"discord-api-types": "0.37.83",
|
||||
"discord-api-types": "^0.37.119",
|
||||
"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"
|
||||
|
||||
@@ -81,21 +81,36 @@ export function validateRequiredButtonParameters(
|
||||
label?: string,
|
||||
emoji?: APIMessageComponentEmoji,
|
||||
customId?: string,
|
||||
skuId?: string,
|
||||
url?: string,
|
||||
) {
|
||||
if (url && customId) {
|
||||
throw new RangeError('URL and custom id are mutually exclusive');
|
||||
}
|
||||
|
||||
if (!label && !emoji) {
|
||||
throw new RangeError('Buttons must have a label and/or an emoji');
|
||||
}
|
||||
|
||||
if (style === ButtonStyle.Link) {
|
||||
if (!url) {
|
||||
throw new RangeError('Link buttons must have a url');
|
||||
if (style === ButtonStyle.Premium) {
|
||||
if (!skuId) {
|
||||
throw new RangeError('Premium buttons must have an SKU id.');
|
||||
}
|
||||
|
||||
if (customId || label || url || emoji) {
|
||||
throw new RangeError('Premium buttons cannot have a custom id, label, URL, or emoji.');
|
||||
}
|
||||
} else {
|
||||
if (skuId) {
|
||||
throw new RangeError('Non-premium buttons must not have an SKU id.');
|
||||
}
|
||||
|
||||
if (url && customId) {
|
||||
throw new RangeError('URL and custom id are mutually exclusive.');
|
||||
}
|
||||
|
||||
if (!label && !emoji) {
|
||||
throw new RangeError('Non-premium buttons must have a label and/or an emoji.');
|
||||
}
|
||||
|
||||
if (style === ButtonStyle.Link) {
|
||||
if (!url) {
|
||||
throw new RangeError('Link buttons must have a URL.');
|
||||
}
|
||||
} else if (url) {
|
||||
throw new RangeError('Non-premium and non-link buttons cannot have a URL.');
|
||||
}
|
||||
} else if (url) {
|
||||
throw new RangeError('Non-link buttons cannot have a url');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,31 +23,31 @@ export interface MappedComponentTypes {
|
||||
*/
|
||||
[ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>;
|
||||
/**
|
||||
* The button component type is associated with an {@link ButtonBuilder}.
|
||||
* The button component type is associated with a {@link ButtonBuilder}.
|
||||
*/
|
||||
[ComponentType.Button]: ButtonBuilder;
|
||||
/**
|
||||
* The string select component type is associated with an {@link StringSelectMenuBuilder}.
|
||||
* The string select component type is associated with a {@link StringSelectMenuBuilder}.
|
||||
*/
|
||||
[ComponentType.StringSelect]: StringSelectMenuBuilder;
|
||||
/**
|
||||
* The text inpiut component type is associated with an {@link TextInputBuilder}.
|
||||
* The text input component type is associated with a {@link TextInputBuilder}.
|
||||
*/
|
||||
[ComponentType.TextInput]: TextInputBuilder;
|
||||
/**
|
||||
* The user select component type is associated with an {@link UserSelectMenuBuilder}.
|
||||
* The user select component type is associated with a {@link UserSelectMenuBuilder}.
|
||||
*/
|
||||
[ComponentType.UserSelect]: UserSelectMenuBuilder;
|
||||
/**
|
||||
* The role select component type is associated with an {@link RoleSelectMenuBuilder}.
|
||||
* The role select component type is associated with a {@link RoleSelectMenuBuilder}.
|
||||
*/
|
||||
[ComponentType.RoleSelect]: RoleSelectMenuBuilder;
|
||||
/**
|
||||
* The mentionable select component type is associated with an {@link MentionableSelectMenuBuilder}.
|
||||
* The mentionable select component type is associated with a {@link MentionableSelectMenuBuilder}.
|
||||
*/
|
||||
[ComponentType.MentionableSelect]: MentionableSelectMenuBuilder;
|
||||
/**
|
||||
* The channel select component type is associated with an {@link ChannelSelectMenuBuilder}.
|
||||
* The channel select component type is associated with a {@link ChannelSelectMenuBuilder}.
|
||||
*/
|
||||
[ComponentType.ChannelSelect]: ChannelSelectMenuBuilder;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {
|
||||
ComponentType,
|
||||
type APIMessageComponentEmoji,
|
||||
type APIButtonComponent,
|
||||
type APIButtonComponentWithURL,
|
||||
type APIButtonComponentWithCustomId,
|
||||
type APIButtonComponentWithSKUId,
|
||||
type APIButtonComponentWithURL,
|
||||
type APIMessageComponentEmoji,
|
||||
type ButtonStyle,
|
||||
type Snowflake,
|
||||
} from 'discord-api-types/v10';
|
||||
import {
|
||||
buttonLabelValidator,
|
||||
@@ -88,13 +90,24 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the SKU id that represents a purchasable SKU for this button.
|
||||
*
|
||||
* @remarks Only available when using premium-style buttons.
|
||||
* @param skuId - The SKU id to use
|
||||
*/
|
||||
public setSKUId(skuId: Snowflake) {
|
||||
(this.data as APIButtonComponentWithSKUId).sku_id = skuId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the emoji to display on this button.
|
||||
*
|
||||
* @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 +127,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,9 +137,10 @@ 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 APIButtonComponentWithSKUId).sku_id,
|
||||
(this.data as APIButtonComponentWithURL).url,
|
||||
);
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ export class ChannelSelectMenuBuilder extends BaseSelectMenuBuilder<APIChannelSe
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default channels to this auto populated select menu.
|
||||
* Sets default channels for this auto populated select menu.
|
||||
*
|
||||
* @param channels - The channels to set
|
||||
*/
|
||||
|
||||
@@ -98,7 +98,7 @@ export class MentionableSelectMenuBuilder extends BaseSelectMenuBuilder<APIMenti
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default values to this auto populated select menu.
|
||||
* Sets default values for this auto populated select menu.
|
||||
*
|
||||
* @param values - The values to set
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ export class RoleSelectMenuBuilder extends BaseSelectMenuBuilder<APIRoleSelectCo
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default roles to this auto populated select menu.
|
||||
* Sets default roles for this auto populated select menu.
|
||||
*
|
||||
* @param roles - The roles to set
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ export class UserSelectMenuBuilder extends BaseSelectMenuBuilder<APIUserSelectCo
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default users to this auto populated select menu.
|
||||
* Sets default users for this auto populated select menu.
|
||||
*
|
||||
* @param users - The users to set
|
||||
*/
|
||||
|
||||
@@ -26,16 +26,16 @@ export class TextInputBuilder
|
||||
*
|
||||
* @param data - The API data to create this text input with
|
||||
* @example
|
||||
* Creating a select menu option from an API data object:
|
||||
* Creating a text input from an API data object:
|
||||
* ```ts
|
||||
* const textInput = new TextInputBuilder({
|
||||
* custom_id: 'a cool select menu',
|
||||
* custom_id: 'a cool text input',
|
||||
* label: 'Type something',
|
||||
* style: TextInputStyle.Short,
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a select menu option using setters and API data:
|
||||
* Creating a text input using setters and API data:
|
||||
* ```ts
|
||||
* const textInput = new TextInputBuilder({
|
||||
* label: 'Type something else',
|
||||
@@ -140,7 +140,7 @@ export class TextInputBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Equatable.equals}
|
||||
* Whether this is equal to another structure.
|
||||
*/
|
||||
public equals(other: APITextInputComponent | JSONEncodable<APITextInputComponent>): boolean {
|
||||
if (isJSONEncodable(other)) {
|
||||
|
||||
@@ -54,6 +54,7 @@ export * from './interactions/slashCommands/mixins/ApplicationCommandOptionWithC
|
||||
export * from './interactions/slashCommands/mixins/NameAndDescription.js';
|
||||
export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions.js';
|
||||
export * from './interactions/slashCommands/mixins/SharedSubcommands.js';
|
||||
export * from './interactions/slashCommands/mixins/SharedSlashCommand.js';
|
||||
|
||||
export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions.js';
|
||||
export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder.js';
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
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';
|
||||
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions.js';
|
||||
import { SharedSlashCommandSubcommands } from './mixins/SharedSubcommands.js';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for slash commands.
|
||||
*/
|
||||
@mix(SharedSlashCommandOptions, SharedNameAndDescription, SharedSlashCommandSubcommands)
|
||||
@mix(SharedSlashCommandOptions, SharedNameAndDescription, SharedSlashCommandSubcommands, SharedSlashCommand)
|
||||
export class SlashCommandBuilder {
|
||||
/**
|
||||
* The name of this command.
|
||||
@@ -34,10 +41,15 @@ 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.
|
||||
*
|
||||
* @deprecated Use {@link SharedSlashCommandSubcommands.setDefaultMemberPermissions} or {@link SharedSlashCommandSubcommands.setDMPermission} instead.
|
||||
* @deprecated Use {@link SharedSlashCommand.setDefaultMemberPermissions} or {@link SharedSlashCommand.setDMPermission} instead.
|
||||
*/
|
||||
public readonly default_permission: boolean | undefined = undefined;
|
||||
|
||||
@@ -54,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.
|
||||
*/
|
||||
@@ -63,14 +80,16 @@ export class SlashCommandBuilder {
|
||||
export interface SlashCommandBuilder
|
||||
extends SharedNameAndDescription,
|
||||
SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>,
|
||||
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder> {}
|
||||
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder>,
|
||||
SharedSlashCommand {}
|
||||
|
||||
/**
|
||||
* An interface specifically for slash command subcommands.
|
||||
*/
|
||||
export interface SlashCommandSubcommandsOnlyBuilder
|
||||
extends SharedNameAndDescription,
|
||||
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder> {}
|
||||
SharedSlashCommandSubcommands<SlashCommandSubcommandsOnlyBuilder>,
|
||||
SharedSlashCommand {}
|
||||
|
||||
/**
|
||||
* An interface specifically for slash command options.
|
||||
@@ -78,7 +97,7 @@ export interface SlashCommandSubcommandsOnlyBuilder
|
||||
export interface SlashCommandOptionsOnlyBuilder
|
||||
extends SharedNameAndDescription,
|
||||
SharedSlashCommandOptions<SlashCommandOptionsOnlyBuilder>,
|
||||
ToAPIApplicationCommandOptions {}
|
||||
SharedSlashCommand {}
|
||||
|
||||
/**
|
||||
* An interface that ensures the `toJSON()` call will return something
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
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,
|
||||
validateLocalizationMap,
|
||||
validateNSFW,
|
||||
validateRequiredParameters,
|
||||
} from '../Assertions.js';
|
||||
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder.js';
|
||||
|
||||
/**
|
||||
* This mixin holds symbols that can be shared in slashcommands independent of options or subcommands.
|
||||
*/
|
||||
export class SharedSlashCommand {
|
||||
public readonly name: string = undefined!;
|
||||
|
||||
public readonly name_localizations?: LocalizationMap;
|
||||
|
||||
public readonly description: string = undefined!;
|
||||
|
||||
public readonly description_localizations?: LocalizationMap;
|
||||
|
||||
public readonly options: ToAPIApplicationCommandOptions[] = [];
|
||||
|
||||
public readonly contexts?: InteractionContextType[];
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link SharedSlashCommand.setDefaultMemberPermissions} or {@link SharedSlashCommand.setDMPermission} instead.
|
||||
*/
|
||||
public readonly default_permission: boolean | undefined = undefined;
|
||||
|
||||
public readonly default_member_permissions: Permissions | null | undefined = undefined;
|
||||
|
||||
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.
|
||||
*
|
||||
* @remarks
|
||||
* If set to `false`, you will have to later `PUT` the permissions for this command.
|
||||
* @param value - Whether or not to enable this command by default
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
|
||||
* @deprecated Use {@link SharedSlashCommand.setDefaultMemberPermissions} or {@link SharedSlashCommand.setDMPermission} instead.
|
||||
*/
|
||||
public setDefaultPermission(value: boolean) {
|
||||
// Assert the value matches the conditions
|
||||
validateDefaultPermission(value);
|
||||
|
||||
Reflect.set(this, 'default_permission', value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default permissions a member should have in order to run the command.
|
||||
*
|
||||
* @remarks
|
||||
* You can set this to `'0'` to disable the command by default.
|
||||
* @param permissions - The permissions bit field to set
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
|
||||
*/
|
||||
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
|
||||
// Assert the value and parse it
|
||||
const permissionValue = validateDefaultMemberPermissions(permissions);
|
||||
|
||||
Reflect.set(this, 'default_member_permissions', permissionValue);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the command is available in direct messages with the application.
|
||||
*
|
||||
* @remarks
|
||||
* By default, commands are visible. This method is only for global commands.
|
||||
* @param enabled - Whether the command should be enabled in direct messages
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
|
||||
*/
|
||||
public setDMPermission(enabled: boolean | null | undefined) {
|
||||
// Assert the value matches the conditions
|
||||
validateDMPermission(enabled);
|
||||
|
||||
Reflect.set(this, 'dm_permission', enabled);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this command is NSFW.
|
||||
*
|
||||
* @param nsfw - Whether this command is NSFW
|
||||
*/
|
||||
public setNSFW(nsfw = true) {
|
||||
// Assert the value matches the conditions
|
||||
validateNSFW(nsfw);
|
||||
Reflect.set(this, 'nsfw', nsfw);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this builder to API-compatible JSON data.
|
||||
*
|
||||
* @remarks
|
||||
* This method runs validations on the data before serializing it.
|
||||
* As such, it may throw an error if the data is invalid.
|
||||
*/
|
||||
public toJSON(): RESTPostAPIChatInputApplicationCommandsJSONBody {
|
||||
validateRequiredParameters(this.name, this.description, this.options);
|
||||
|
||||
validateLocalizationMap(this.name_localizations);
|
||||
validateLocalizationMap(this.description_localizations);
|
||||
|
||||
return {
|
||||
...this,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
import type {
|
||||
LocalizationMap,
|
||||
Permissions,
|
||||
RESTPostAPIChatInputApplicationCommandsJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import {
|
||||
assertReturnOfBuilder,
|
||||
validateDMPermission,
|
||||
validateDefaultMemberPermissions,
|
||||
validateDefaultPermission,
|
||||
validateLocalizationMap,
|
||||
validateMaxOptionsLength,
|
||||
validateNSFW,
|
||||
validateRequiredParameters,
|
||||
} from '../Assertions.js';
|
||||
import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions.js';
|
||||
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder.js';
|
||||
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from '../SlashCommandSubcommands.js';
|
||||
|
||||
@@ -24,80 +10,8 @@ import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } fro
|
||||
export class SharedSlashCommandSubcommands<
|
||||
TypeAfterAddingSubcommands extends SharedSlashCommandSubcommands<TypeAfterAddingSubcommands>,
|
||||
> {
|
||||
public readonly name: string = undefined!;
|
||||
|
||||
public readonly name_localizations?: LocalizationMap;
|
||||
|
||||
public readonly description: string = undefined!;
|
||||
|
||||
public readonly description_localizations?: LocalizationMap;
|
||||
|
||||
public readonly options: ToAPIApplicationCommandOptions[] = [];
|
||||
|
||||
/**
|
||||
* Sets whether the command is enabled by default when the application is added to a guild.
|
||||
*
|
||||
* @remarks
|
||||
* If set to `false`, you will have to later `PUT` the permissions for this command.
|
||||
* @param value - Whether or not to enable this command by default
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
|
||||
* @deprecated Use {@link SharedSlashCommandSubcommands.setDefaultMemberPermissions} or {@link SharedSlashCommandSubcommands.setDMPermission} instead.
|
||||
*/
|
||||
public setDefaultPermission(value: boolean) {
|
||||
// Assert the value matches the conditions
|
||||
validateDefaultPermission(value);
|
||||
|
||||
Reflect.set(this, 'default_permission', value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default permissions a member should have in order to run the command.
|
||||
*
|
||||
* @remarks
|
||||
* You can set this to `'0'` to disable the command by default.
|
||||
* @param permissions - The permissions bit field to set
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
|
||||
*/
|
||||
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
|
||||
// Assert the value and parse it
|
||||
const permissionValue = validateDefaultMemberPermissions(permissions);
|
||||
|
||||
Reflect.set(this, 'default_member_permissions', permissionValue);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the command is available in direct messages with the application.
|
||||
*
|
||||
* @remarks
|
||||
* By default, commands are visible. This method is only for global commands.
|
||||
* @param enabled - Whether the command should be enabled in direct messages
|
||||
* @see {@link https://discord.com/developers/docs/interactions/application-commands#permissions}
|
||||
*/
|
||||
public setDMPermission(enabled: boolean | null | undefined) {
|
||||
// Assert the value matches the conditions
|
||||
validateDMPermission(enabled);
|
||||
|
||||
Reflect.set(this, 'dm_permission', enabled);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this command is NSFW.
|
||||
*
|
||||
* @param nsfw - Whether this command is NSFW
|
||||
*/
|
||||
public setNSFW(nsfw = true) {
|
||||
// Assert the value matches the conditions
|
||||
validateNSFW(nsfw);
|
||||
Reflect.set(this, 'nsfw', nsfw);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new subcommand group to this command.
|
||||
*
|
||||
@@ -149,23 +63,4 @@ export class SharedSlashCommandSubcommands<
|
||||
|
||||
return this as unknown as TypeAfterAddingSubcommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this builder to API-compatible JSON data.
|
||||
*
|
||||
* @remarks
|
||||
* This method runs validations on the data before serializing it.
|
||||
* As such, it may throw an error if the data is invalid.
|
||||
*/
|
||||
public toJSON(): RESTPostAPIChatInputApplicationCommandsJSONBody {
|
||||
validateRequiredParameters(this.name, this.description, this.options);
|
||||
|
||||
validateLocalizationMap(this.name_localizations);
|
||||
validateLocalizationMap(this.description_localizations);
|
||||
|
||||
return {
|
||||
...this,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,12 @@ describe('each() tests', () => {
|
||||
expectInvalidFunctionError(() => coll.each(123), 123);
|
||||
});
|
||||
|
||||
test('binds the thisArg', () => {
|
||||
coll.each(function each() {
|
||||
expect(this).toBeNull();
|
||||
}, null);
|
||||
});
|
||||
|
||||
test('iterate over each item', () => {
|
||||
const coll = createTestCollection();
|
||||
const a: [string, number][] = [];
|
||||
@@ -964,6 +970,10 @@ describe('findLast() tests', () => {
|
||||
expect(coll.findLast((value) => value % 2 === 1)).toStrictEqual(3);
|
||||
});
|
||||
|
||||
test('returns undefined if no item matches', () => {
|
||||
expect(coll.findLast((value) => value === 10)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('throws if fn is not a function', () => {
|
||||
// @ts-expect-error: Invalid function
|
||||
expectInvalidFunctionError(() => createCollection().findLast());
|
||||
@@ -985,6 +995,10 @@ describe('findLastKey() tests', () => {
|
||||
expect(coll.findLastKey((value) => value % 2 === 1)).toStrictEqual('c');
|
||||
});
|
||||
|
||||
test('returns undefined if no item matches', () => {
|
||||
expect(coll.findLastKey((value) => value === 10)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('throws if fn is not a function', () => {
|
||||
// @ts-expect-error: Invalid function
|
||||
expectInvalidFunctionError(() => createCollection().findLastKey());
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"extends": "../../api-extractor.json",
|
||||
"bundledPackages": ["discord-api-types"],
|
||||
"docModel": {
|
||||
"projectFolderUrl": "https://github.com/discordjs/discord.js/tree/main/packages/core"
|
||||
}
|
||||
|
||||
@@ -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.119"
|
||||
},
|
||||
"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"
|
||||
|
||||
@@ -387,10 +387,11 @@ export class ChannelsAPI {
|
||||
public async followAnnouncements(
|
||||
channelId: Snowflake,
|
||||
webhookChannelId: Snowflake,
|
||||
{ signal }: Pick<RequestData, 'signal'> = {},
|
||||
{ reason, signal }: Pick<RequestData, 'reason' | 'signal'> = {},
|
||||
) {
|
||||
return this.rest.post(Routes.channelFollowers(channelId), {
|
||||
body: { webhook_channel_id: webhookChannelId },
|
||||
reason,
|
||||
signal,
|
||||
}) as Promise<RESTPostAPIChannelFollowersResult>;
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@ export class InteractionsAPI {
|
||||
* @param interactionId - The id of the interaction
|
||||
* @param interactionToken - The token of the interaction
|
||||
* @param options - The options for sending the premium required response
|
||||
* @deprecated Sending a premium-style button is the new Discord behaviour.
|
||||
*/
|
||||
public async sendPremiumRequired(
|
||||
interactionId: Snowflake,
|
||||
|
||||
@@ -47,6 +47,7 @@ export class OAuth2API {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
auth: false,
|
||||
signal,
|
||||
}) as Promise<RESTPostOAuth2AccessTokenResult>;
|
||||
}
|
||||
@@ -68,6 +69,7 @@ export class OAuth2API {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
auth: false,
|
||||
signal,
|
||||
}) as Promise<RESTPostOAuth2RefreshTokenResult>;
|
||||
}
|
||||
@@ -91,6 +93,7 @@ export class OAuth2API {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
auth: false,
|
||||
signal,
|
||||
}) as Promise<RESTPostOAuth2ClientCredentialsResult>;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -201,8 +201,8 @@ export class Client extends AsyncEventEmitter<MappedEvents> {
|
||||
|
||||
this.gateway.on(WebSocketShardEvents.Dispatch, ({ data: dispatch, shardId }) => {
|
||||
this.emit(
|
||||
// @ts-expect-error ws/1.1.
|
||||
dispatch.t,
|
||||
// @ts-expect-error event props can't be resolved properly, but they are correct
|
||||
this.wrapIntrinsicProps(dispatch.d, shardId),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -2,6 +2,38 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
# [14.15.3](https://github.com/discordjs/discord.js/compare/14.15.2...14.15.3) - (2024-06-02)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Message:** Properly compare `attachments` and `embeds` (#10282) ([a468ae8](https://github.com/discordjs/discord.js/commit/a468ae8bb5a9de9cb34d40493c59693e84c2812a))
|
||||
- Throw error on no message id for `Message#fetchReference()` (#10295) ([638b896](https://github.com/discordjs/discord.js/commit/638b896efaf0a01b477f91c17170214ad96b1602))
|
||||
- **ThreadChannel:** Invalid owner fetch option (#10292) ([27d0659](https://github.com/discordjs/discord.js/commit/27d0659a45c44f0c5986688d16f28e75e99abcc1))
|
||||
- **Action:** Ensure all properties on `getChannel()` are passed (#10278) ([92c1a51](https://github.com/discordjs/discord.js/commit/92c1a511dc0d9b552b797ef25c7aed2eb36b4386))
|
||||
- **docs:** Some link tags didn't resolve correctly (#10269) ([914cc4b](https://github.com/discordjs/discord.js/commit/914cc4ba5441cde5aa6dc8ec6406a283855d6828))
|
||||
- **actions:** Handle missing poll object (#10266) ([7816ec2](https://github.com/discordjs/discord.js/commit/7816ec2e6b28daf400eaa9cb050fb72908e6f7c6))
|
||||
|
||||
## Refactor
|
||||
|
||||
- **GuildChannelManager:** Improve addFollower errors (#10277) ([555961b](https://github.com/discordjs/discord.js/commit/555961b3b8da8759349cd0e88f89f98d2e8a6363))
|
||||
|
||||
## Typings
|
||||
|
||||
- Forum starter messages do not support polls (#10276) ([35207b0](https://github.com/discordjs/discord.js/commit/35207b0b31929558eee69f4bd53a6f9adadb0362))
|
||||
- Add `defaultValues` to respective select menu components data (#10265) ([c2432d5](https://github.com/discordjs/discord.js/commit/c2432d5704e4e178c044bc0d02f2dabe51450d19))
|
||||
|
||||
# [14.15.2](https://github.com/discordjs/discord.js/compare/14.15.1...14.15.2) - (2024-05-05)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **PollAnswer:** FetchVoters route changed to MessageManager (#10251) ([30d79e8](https://github.com/discordjs/discord.js/commit/30d79e85fb8502aee5c63fe7effd9029e347d266))
|
||||
|
||||
# [14.15.1](https://github.com/discordjs/discord.js/compare/14.15.0...14.15.1) - (2024-05-04)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **MessageManager:** Poll methods don't need a channel id (#10249) ([0474a43](https://github.com/discordjs/discord.js/commit/0474a4375146b57b35074dadbaa83274416f899e))
|
||||
|
||||
# [14.15.0](https://github.com/discordjs/discord.js/compare/14.14.1...14.15.0) - (2024-05-04)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
{
|
||||
"extends": "../../api-extractor.json",
|
||||
"mainEntryPointFilePath": "<projectFolder>/typings/index.d.ts",
|
||||
"bundledPackages": [
|
||||
"discord-api-types",
|
||||
"@discordjs/builders",
|
||||
"@discordjs/formatters",
|
||||
"@discordjs/rest",
|
||||
"@discordjs/util",
|
||||
"@discordjs/ws"
|
||||
],
|
||||
"docModel": {
|
||||
"projectFolderUrl": "https://github.com/discordjs/discord.js/tree/main/packages/discord.js"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "discord.js",
|
||||
"version": "14.15.0",
|
||||
"version": "14.15.3",
|
||||
"description": "A powerful library for interacting with the Discord API",
|
||||
"scripts": {
|
||||
"test": "pnpm run docs:test && pnpm run test:typescript",
|
||||
@@ -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.119",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"tslib": "2.6.2",
|
||||
"undici": "6.13.0"
|
||||
"undici": "6.21.1"
|
||||
},
|
||||
"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": {
|
||||
|
||||
@@ -31,21 +31,17 @@ class GenericAction {
|
||||
const payloadData = {};
|
||||
const id = data.channel_id ?? data.id;
|
||||
|
||||
if ('recipients' in data) {
|
||||
payloadData.recipients = data.recipients;
|
||||
} else {
|
||||
if (!('recipients' in data)) {
|
||||
// Try to resolve the recipient, but do not add the client user.
|
||||
const recipient = data.author ?? data.user ?? { id: data.user_id };
|
||||
if (recipient.id !== this.client.user.id) payloadData.recipients = [recipient];
|
||||
}
|
||||
|
||||
if (id !== undefined) payloadData.id = id;
|
||||
if ('guild_id' in data) payloadData.guild_id = data.guild_id;
|
||||
if ('last_message_id' in data) payloadData.last_message_id = data.last_message_id;
|
||||
|
||||
return (
|
||||
data[this.client.actions.injectedChannel] ??
|
||||
this.getPayload(payloadData, this.client.channels, id, Partials.Channel)
|
||||
this.getPayload({ ...data, ...payloadData }, this.client.channels, id, Partials.Channel)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class MessagePollVoteAddAction extends Action {
|
||||
|
||||
const { poll } = message;
|
||||
|
||||
const answer = poll.answers.get(data.answer_id);
|
||||
const answer = poll?.answers.get(data.answer_id);
|
||||
if (!answer) return false;
|
||||
|
||||
answer.voteCount++;
|
||||
|
||||
@@ -13,7 +13,7 @@ class MessagePollVoteRemoveAction extends Action {
|
||||
|
||||
const { poll } = message;
|
||||
|
||||
const answer = poll.answers.get(data.answer_id);
|
||||
const answer = poll?.answers.get(data.answer_id);
|
||||
if (!answer) return false;
|
||||
|
||||
answer.voteCount--;
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -98,17 +98,29 @@ class GuildChannelManager extends CachedManager {
|
||||
return super.resolveId(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to a News Channel object. This can be:
|
||||
* * A NewsChannel object
|
||||
* * A Snowflake
|
||||
* @typedef {NewsChannel|Snowflake} NewsChannelResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds the target channel to a channel's followers.
|
||||
* @param {NewsChannel|Snowflake} channel The channel to follow
|
||||
* @param {NewsChannelResolvable} channel The channel to follow
|
||||
* @param {TextChannelResolvable} targetChannel The channel where published announcements will be posted at
|
||||
* @param {string} [reason] Reason for creating the webhook
|
||||
* @returns {Promise<Snowflake>} Returns created target webhook id.
|
||||
*/
|
||||
async addFollower(channel, targetChannel, reason) {
|
||||
const channelId = this.resolveId(channel);
|
||||
if (!channelId) {
|
||||
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'NewsChannelResolvable');
|
||||
}
|
||||
const targetChannelId = this.resolveId(targetChannel);
|
||||
if (!channelId || !targetChannelId) throw new Error(ErrorCodes.GuildChannelResolve);
|
||||
if (!targetChannelId) {
|
||||
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'targetChannel', 'TextChannelResolvable');
|
||||
}
|
||||
const { webhook_id } = await this.client.rest.post(Routes.channelFollowers(channelId), {
|
||||
body: { webhook_channel_id: targetChannelId },
|
||||
reason,
|
||||
@@ -272,13 +284,13 @@ class GuildChannelManager extends CachedManager {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async edit(channel, options) {
|
||||
channel = this.resolve(channel);
|
||||
if (!channel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
|
||||
const resolvedChannel = this.resolve(channel);
|
||||
if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
|
||||
|
||||
const parent = options.parent && this.client.channels.resolveId(options.parent);
|
||||
|
||||
if (options.position !== undefined) {
|
||||
await this.setPosition(channel, options.position, { position: options.position, reason: options.reason });
|
||||
await this.setPosition(resolvedChannel, options.position, { position: options.position, reason: options.reason });
|
||||
}
|
||||
|
||||
let permission_overwrites = options.permissionOverwrites?.map(overwrite =>
|
||||
@@ -293,22 +305,22 @@ class GuildChannelManager extends CachedManager {
|
||||
PermissionOverwrites.resolve(overwrite, this.guild),
|
||||
);
|
||||
}
|
||||
} else if (channel.parent) {
|
||||
permission_overwrites = channel.parent.permissionOverwrites.cache.map(overwrite =>
|
||||
} else if (resolvedChannel.parent) {
|
||||
permission_overwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite =>
|
||||
PermissionOverwrites.resolve(overwrite, this.guild),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const newData = await this.client.rest.patch(Routes.channel(channel.id), {
|
||||
const newData = await this.client.rest.patch(Routes.channel(resolvedChannel.id), {
|
||||
body: {
|
||||
name: (options.name ?? channel.name).trim(),
|
||||
name: options.name,
|
||||
type: options.type,
|
||||
topic: options.topic,
|
||||
nsfw: options.nsfw,
|
||||
bitrate: options.bitrate ?? channel.bitrate,
|
||||
user_limit: options.userLimit ?? channel.userLimit,
|
||||
rtc_region: 'rtcRegion' in options ? options.rtcRegion : channel.rtcRegion,
|
||||
bitrate: options.bitrate,
|
||||
user_limit: options.userLimit,
|
||||
rtc_region: options.rtcRegion,
|
||||
video_quality_mode: options.videoQualityMode,
|
||||
parent_id: parent,
|
||||
lock_permissions: options.lockPermissions,
|
||||
|
||||
@@ -97,7 +97,7 @@ class GuildManager extends CachedManager {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a GuildResolvable to a Guild object.
|
||||
* Resolves a {@link GuildResolvable} to a {@link Guild} object.
|
||||
* @method resolve
|
||||
* @memberof GuildManager
|
||||
* @instance
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -17,7 +17,7 @@ class GuildTextThreadManager extends ThreadManager {
|
||||
|
||||
/**
|
||||
* Options for creating a thread. <warn>Only one of `startMessage` or `type` can be defined.</warn>
|
||||
* @typedef {StartThreadOptions} ThreadCreateOptions
|
||||
* @typedef {StartThreadOptions} GuildTextThreadCreateOptions
|
||||
* @property {MessageResolvable} [startMessage] The message to start a thread from.
|
||||
* <warn>If this is defined, then the `type` of thread gets inferred automatically and cannot be changed.</warn>
|
||||
* @property {ThreadChannelTypes} [type] The type of thread to create.
|
||||
@@ -30,7 +30,7 @@ class GuildTextThreadManager extends ThreadManager {
|
||||
|
||||
/**
|
||||
* Creates a new thread in the channel.
|
||||
* @param {ThreadCreateOptions} [options] Options to create a new thread
|
||||
* @param {GuildTextThreadCreateOptions} [options] Options to create a new thread
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Create a new public thread
|
||||
|
||||
@@ -269,19 +269,17 @@ class MessageManager extends CachedManager {
|
||||
|
||||
/**
|
||||
* Ends a poll.
|
||||
* @param {Snowflake} channelId The id of the channel
|
||||
* @param {Snowflake} messageId The id of the message
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async endPoll(channelId, messageId) {
|
||||
const message = await this.client.rest.post(Routes.expirePoll(channelId, messageId));
|
||||
async endPoll(messageId) {
|
||||
const message = await this.client.rest.post(Routes.expirePoll(this.channel.id, messageId));
|
||||
return this._add(message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options used for fetching voters of an answer in a poll.
|
||||
* @typedef {BaseFetchPollAnswerVotersOptions} FetchPollAnswerVotersOptions
|
||||
* @param {Snowflake} channelId The id of the channel
|
||||
* @param {Snowflake} messageId The id of the message
|
||||
* @param {number} answerId The id of the answer
|
||||
*/
|
||||
@@ -291,8 +289,8 @@ class MessageManager extends CachedManager {
|
||||
* @param {FetchPollAnswerVotersOptions} options The options for fetching the poll answer voters
|
||||
* @returns {Promise<Collection<Snowflake, User>>}
|
||||
*/
|
||||
async fetchPollAnswerVoters({ channelId, messageId, answerId, after, limit }) {
|
||||
const voters = await this.client.rest.get(Routes.pollAnswerVoters(channelId, messageId, answerId), {
|
||||
async fetchPollAnswerVoters({ messageId, answerId, after, limit }) {
|
||||
const voters = await this.client.rest.get(Routes.pollAnswerVoters(this.channel.id, messageId, answerId), {
|
||||
query: makeURLSearchParams({ limit, after }),
|
||||
});
|
||||
|
||||
|
||||
@@ -63,20 +63,6 @@ class ThreadManager extends CachedManager {
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for creating a thread. <warn>Only one of `startMessage` or `type` can be defined.</warn>
|
||||
* @typedef {StartThreadOptions} ThreadCreateOptions
|
||||
* @property {MessageResolvable} [startMessage] The message to start a thread from. <warn>If this is defined then type
|
||||
* of thread gets automatically defined and cannot be changed. The provided `type` field will be ignored</warn>
|
||||
* @property {ChannelType.AnnouncementThread|ChannelType.PublicThread|ChannelType.PrivateThread} [type]
|
||||
* The type of thread to create.
|
||||
* Defaults to {@link ChannelType.PublicThread} if created in a {@link TextChannel}
|
||||
* <warn>When creating threads in a {@link NewsChannel} this is ignored and is always
|
||||
* {@link ChannelType.AnnouncementThread}</warn>
|
||||
* @property {boolean} [invitable] Whether non-moderators can add other non-moderators to the thread
|
||||
* <info>Can only be set when type will be {@link ChannelType.PrivateThread}</info>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for fetching multiple threads.
|
||||
* @typedef {Object} FetchThreadsOptions
|
||||
|
||||
@@ -23,7 +23,7 @@ class ShardingManager extends EventEmitter {
|
||||
/**
|
||||
* The mode to spawn shards with for a {@link ShardingManager}. Can be either one of:
|
||||
* * 'process' to use child processes
|
||||
* * 'worker' to use [Worker threads](https://nodejs.org/api/worker_threads.html)
|
||||
* * 'worker' to use {@link Worker} threads
|
||||
* @typedef {string} ShardingManagerMode
|
||||
*/
|
||||
|
||||
|
||||
@@ -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>}
|
||||
*/
|
||||
|
||||
@@ -164,7 +164,7 @@ class Guild extends AnonymousGuild {
|
||||
|
||||
if ('large' in data) {
|
||||
/**
|
||||
* Whether the guild is "large" (has more than {@link WebsocketOptions large_threshold} members, 50 by default)
|
||||
* Whether the guild is "large" (has more than {@link WebSocketOptions large_threshold} members, 50 by default)
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.large = Boolean(data.large);
|
||||
@@ -291,7 +291,8 @@ class Guild extends AnonymousGuild {
|
||||
if ('max_presences' in data) {
|
||||
/**
|
||||
* The maximum amount of presences the guild can have (this is `null` for all but the largest of guilds)
|
||||
* <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
|
||||
* <info>You will need to fetch the guild using {@link BaseGuild#fetch} if you want to receive
|
||||
* this parameter</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.maximumPresences = data.max_presences;
|
||||
@@ -322,7 +323,8 @@ class Guild extends AnonymousGuild {
|
||||
if ('approximate_member_count' in data) {
|
||||
/**
|
||||
* The approximate amount of members the guild has
|
||||
* <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
|
||||
* <info>You will need to fetch the guild using {@link BaseGuild#fetch} if you want to receive
|
||||
* this parameter</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.approximateMemberCount = data.approximate_member_count;
|
||||
@@ -333,7 +335,8 @@ class Guild extends AnonymousGuild {
|
||||
if ('approximate_presence_count' in data) {
|
||||
/**
|
||||
* The approximate amount of presences the guild has
|
||||
* <info>You will need to fetch the guild using {@link Guild#fetch} if you want to receive this parameter</info>
|
||||
* <info>You will need to fetch the guild using {@link BaseGuild#fetch} if you want to receive
|
||||
* this parameter</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.approximatePresenceCount = data.approximate_presence_count;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { AuditLogOptionsType, AuditLogEvent } = require('discord-api-types/v10');
|
||||
const AutoModerationRule = require('./AutoModerationRule');
|
||||
const { GuildOnboardingPrompt } = require('./GuildOnboardingPrompt');
|
||||
const { GuildScheduledEvent } = require('./GuildScheduledEvent');
|
||||
const Integration = require('./Integration');
|
||||
const Invite = require('./Invite');
|
||||
@@ -29,6 +30,8 @@ const Targets = {
|
||||
Thread: 'Thread',
|
||||
ApplicationCommand: 'ApplicationCommand',
|
||||
AutoModeration: 'AutoModeration',
|
||||
GuildOnboarding: 'GuildOnboarding',
|
||||
GuildOnboardingPrompt: 'GuildOnboardingPrompt',
|
||||
Unknown: 'Unknown',
|
||||
};
|
||||
|
||||
@@ -49,10 +52,11 @@ const Targets = {
|
||||
* * A thread
|
||||
* * An application command
|
||||
* * An auto moderation rule
|
||||
* * A guild onboarding prompt
|
||||
* * An object with an id key if target was deleted or fake entity
|
||||
* * An object where the keys represent either the new value or the old value
|
||||
* @typedef {?(Object|Guild|BaseChannel|User|Role|Invite|Webhook|GuildEmoji|Message|Integration|StageInstance|Sticker|
|
||||
* GuildScheduledEvent|ApplicationCommand|AutoModerationRule)} AuditLogEntryTarget
|
||||
* GuildScheduledEvent|ApplicationCommand|AutoModerationRule|GuildOnboardingPrompt)} AuditLogEntryTarget
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -80,6 +84,9 @@ const Targets = {
|
||||
* * Thread
|
||||
* * GuildScheduledEvent
|
||||
* * ApplicationCommandPermission
|
||||
* * GuildOnboarding
|
||||
* * GuildOnboardingPrompt
|
||||
* * Unknown
|
||||
* @typedef {string} AuditLogTargetType
|
||||
*/
|
||||
|
||||
@@ -349,6 +356,13 @@ class GuildAuditLogsEntry {
|
||||
changesReduce(this.changes, { id: data.target_id, guild_id: guild.id }),
|
||||
guild,
|
||||
);
|
||||
} else if (targetType === Targets.GuildOnboardingPrompt) {
|
||||
this.target =
|
||||
data.action_type === AuditLogEvent.OnboardingPromptCreate
|
||||
? new GuildOnboardingPrompt(guild.client, changesReduce(this.changes, { id: data.target_id }), guild.id)
|
||||
: changesReduce(this.changes, { id: data.target_id });
|
||||
} else if (targetType === Targets.GuildOnboarding) {
|
||||
this.target = changesReduce(this.changes, { id: data.target_id });
|
||||
} else if (data.target_id) {
|
||||
this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) ?? { id: data.target_id };
|
||||
}
|
||||
@@ -375,6 +389,8 @@ class GuildAuditLogsEntry {
|
||||
if (target < 120) return Targets.Thread;
|
||||
if (target < 130) return Targets.ApplicationCommand;
|
||||
if (target >= 140 && target < 150) return Targets.AutoModeration;
|
||||
if (target >= 163 && target <= 165) return Targets.GuildOnboardingPrompt;
|
||||
if (target >= 160 && target < 170) return Targets.GuildOnboarding;
|
||||
return Targets.Unknown;
|
||||
}
|
||||
|
||||
@@ -402,6 +418,8 @@ class GuildAuditLogsEntry {
|
||||
AuditLogEvent.ThreadCreate,
|
||||
AuditLogEvent.AutoModerationRuleCreate,
|
||||
AuditLogEvent.AutoModerationBlockMessage,
|
||||
AuditLogEvent.OnboardingPromptCreate,
|
||||
AuditLogEvent.OnboardingCreate,
|
||||
].includes(action)
|
||||
) {
|
||||
return 'Create';
|
||||
@@ -428,6 +446,7 @@ class GuildAuditLogsEntry {
|
||||
AuditLogEvent.GuildScheduledEventDelete,
|
||||
AuditLogEvent.ThreadDelete,
|
||||
AuditLogEvent.AutoModerationRuleDelete,
|
||||
AuditLogEvent.OnboardingPromptDelete,
|
||||
].includes(action)
|
||||
) {
|
||||
return 'Delete';
|
||||
@@ -452,6 +471,8 @@ class GuildAuditLogsEntry {
|
||||
AuditLogEvent.ThreadUpdate,
|
||||
AuditLogEvent.ApplicationCommandPermissionUpdate,
|
||||
AuditLogEvent.AutoModerationRuleUpdate,
|
||||
AuditLogEvent.OnboardingPromptUpdate,
|
||||
AuditLogEvent.OnboardingUpdate,
|
||||
].includes(action)
|
||||
) {
|
||||
return 'Update';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user