Compare commits

..

1 Commits

Author SHA1 Message Date
iCrawl
444d7c98dd chore(ws): release @discordjs/ws@0.1.0 2022-07-27 14:15:35 +02:00
459 changed files with 9619 additions and 15290 deletions

View File

@@ -1,12 +1,9 @@
{
"root": true,
"extends": ["neon/common", "neon/node", "neon/typescript", "neon/prettier"],
"extends": "marine/prettier/node",
"parserOptions": {
"project": "./tsconfig.eslint.json"
},
"rules": {
"@typescript-eslint/consistent-type-definitions": ["error", "interface"]
},
"ignorePatterns": ["**/dist/*"],
"env": {
"jest": true

11
.github/.kodiak.toml vendored
View File

@@ -1,11 +0,0 @@
version = 1
[merge]
require_automerge_label = false
blocking_labels = ['blocked']
method = 'squash'
[merge.message]
title = 'pull_request_title'
strip_html_comments = true
include_coauthors = true

View File

@@ -52,7 +52,7 @@ body:
id: djs-version
attributes:
label: Package version
description: Which version of the package are you using? Run `npm list <package>` in your project directory and paste the output.
description: Which version of are you using? Run `npm list <package>` in your project directory and paste the output.
placeholder: We no longer support version 12 or earlier of discord.js
validations:
required: true

View File

@@ -5,7 +5,7 @@ body:
- type: markdown
attributes:
value: |
We can only implement features that Discord publishes, documents, and merges into the Discord API documentation.
We can only implement features that Discord publishes, documents and merges into the Discord API documentation.
We do not implement unreleased features.
Use Discord for questions: https://discord.gg/djs
- type: dropdown

13
.github/check_deploy_branch.sh vendored Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
git diff HEAD^ HEAD --quiet .
if [[ "$VERCEL_GIT_COMMIT_REF" == "main" || $? -eq 1 ]]; then
# Proceed with the build
echo "✅ - Proceed"
exit 1;
else
# Don't build
echo "🛑 - Build cancelled"
exit 0;
fi

4
.github/labeler.yml vendored
View File

@@ -1,3 +1,7 @@
chore:
- any: ['*']
all: ['!packages/*', '!packages/**/*']
'packages:builders':
- packages/builders/*
- packages/builders/**/*

View File

@@ -3,6 +3,8 @@ on:
push:
branches:
- 'main'
- 'stable'
- '!docs'
tags:
- '**'
workflow_dispatch:
@@ -10,28 +12,15 @@ on:
ref:
description: 'The branch, tag or SHA to checkout'
required: true
ref_type:
type: choice
description: 'Branch or tag'
options:
- branch
- tag
required: true
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build documentation
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
if: github.repository_owner == 'discordjs'
outputs:
BRANCH_NAME: ${{ steps.env.outputs.BRANCH_NAME }}
BRANCH_OR_TAG: ${{ steps.env.outputs.BRANCH_OR_TAG }}
SHA: ${{ steps.env.outputs.SHA }}
if: github.repository_owner == 'discordjs'
steps:
- name: Checkout repository
uses: actions/checkout@v3
@@ -43,6 +32,7 @@ jobs:
with:
node-version: 16
cache: 'yarn'
cache-dependency-path: yarn.lock
- name: Install dependencies
run: yarn --immutable
@@ -82,10 +72,8 @@ jobs:
package: ['builders', 'collection', 'discord.js', 'proxy', 'rest', 'voice', 'ws']
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
BRANCH_NAME: ${{ github.event.inputs.ref || needs.build.outputs.BRANCH_NAME }}
BRANCH_OR_TAG: ${{ github.event.inputs.ref_type || needs.build.outputs.BRANCH_OR_TAG }}
BRANCH_OR_TAG: ${{ needs.build.outputs.BRANCH_OR_TAG }}
SHA: ${{ needs.build.outputs.SHA }}
steps:
- name: Checkout repository
@@ -96,12 +84,13 @@ jobs:
with:
node-version: 16
cache: 'yarn'
cache-dependency-path: yarn.lock
- name: Install dependencies
run: yarn --immutable
- name: Build actions
run: yarn workspace @discordjs/actions build
run: yarn build
- name: Download docgen artifacts
uses: actions/download-artifact@v3
@@ -123,14 +112,14 @@ jobs:
path: 'out'
- name: Extract package and semver from tag
if: ${{ env.BRANCH_OR_TAG == 'tag' }}
if: ${{ github.event.inputs.ref || env.BRANCH_OR_TAG == 'tag' }}
id: extract-tag
uses: ./packages/actions/src/formatTag
with:
tag: ${{ env.BRANCH_NAME }}
- name: Move docs to correct directory
if: ${{ env.BRANCH_OR_TAG == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
if: ${{ (github.event.inputs.ref || env.BRANCH_OR_TAG == 'tag') && matrix.package == steps.extract-tag.outputs.package }}
env:
PACKAGE: ${{ steps.extract-tag.outputs.package }}
SEMVER: ${{ steps.extract-tag.outputs.semver }}
@@ -142,7 +131,7 @@ jobs:
fi
- name: Move docs to correct directory
if: ${{ env.BRANCH_OR_TAG == 'branch' }}
if: ${{ !github.event.inputs.ref && env.BRANCH_OR_TAG == 'branch' }}
env:
PACKAGE: ${{ matrix.package }}
run: |

View File

@@ -1,8 +1,8 @@
name: npm auto deprecate
on:
workflow_dispatch:
schedule:
- cron: '0 1 * * *'
workflow_dispatch:
jobs:
npm-auto-deprecate:
name: npm auto deprecate
@@ -17,6 +17,7 @@ jobs:
with:
node-version: 16
cache: 'yarn'
cache-dependency-path: yarn.lock
- name: Install dependencies
run: yarn --immutable

View File

@@ -1,8 +1,8 @@
name: Publish dev docker images
on:
workflow_dispatch:
schedule:
- cron: '0 */12 * * *'
workflow_dispatch:
jobs:
docker-publish:
name: Docker publish

View File

@@ -1,8 +1,8 @@
name: Publish dev
on:
workflow_dispatch:
schedule:
- cron: '0 */12 * * *'
workflow_dispatch:
jobs:
npm-publish:
name: npm publish
@@ -25,9 +25,6 @@ jobs:
- package: '@discordjs/ws'
folder: 'ws'
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
if: github.repository_owner == 'discordjs'
steps:
- name: Checkout repository
@@ -39,6 +36,7 @@ jobs:
node-version: 16
registry-url: https://registry.npmjs.org/
cache: 'yarn'
cache-dependency-path: yarn.lock
- name: Install dependencies
run: yarn --immutable

View File

@@ -1,18 +1,9 @@
name: Tests
on:
push:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
on: [push, pull_request]
jobs:
tests:
name: Tests
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
NEXT_PUBLIC_LOCAL_DEV: true
steps:
- name: Checkout repository
uses: actions/checkout@v3
@@ -22,9 +13,10 @@ jobs:
with:
node-version: 16
cache: 'yarn'
cache-dependency-path: yarn.lock
- name: Install dependencies
run: yarn install --immutable
run: yarn --immutable
- name: Build dependencies
run: yarn build
@@ -36,5 +28,5 @@ jobs:
run: yarn test
- name: Upload Coverage
if: github.repository_owner == 'discordjs'
uses: ./packages/actions/src/uploadCoverage
if: github.repository_owner == 'discordjs'

View File

@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn build && yarn workspace @discordjs/website run build:local && yarn lint-staged
yarn build && yarn lint-staged

View File

@@ -1,5 +1,5 @@
{
"*": "prettier --ignore-unknown --write",
"{src/**,__tests__/**}.{mjs,js,ts}": "eslint --ext mjs,js,ts --fix",
"src/**.ts": "vitest related --run"
"src/**.ts": "vitest related"
}

View File

@@ -0,0 +1,36 @@
diff --git a/dist/rollup-plugin-typescript2.cjs.js b/dist/rollup-plugin-typescript2.cjs.js
index 9ab972b041cc76f8f786d6a20f3efb53c364cad6..13e056a3c0971eb18b307d91fad096a9f3b9de79 100644
--- a/dist/rollup-plugin-typescript2.cjs.js
+++ b/dist/rollup-plugin-typescript2.cjs.js
@@ -29799,6 +29799,13 @@ const typescript = (options) => {
declarations[key] = { type: result.dts, map: result.dtsmap };
context.debug(() => `${safe.exports.blue("generated declarations")} for '${key}'`);
}
+ // if a user sets this compilerOption, they probably want another plugin (e.g. Babel, ESBuild) to transform their TS instead, while rpt2 just type-checks and/or outputs declarations
+ // note that result.code is non-existent if emitDeclarationOnly per https://github.com/ezolenko/rollup-plugin-typescript2/issues/268
+ if (parsedConfig.options.emitDeclarationOnly)
+ {
+ context.debug(() => `${blue("emitDeclarationOnly")} enabled, not transforming TS'`);
+ return undefined;
+ }
const transformResult = { code: result.code, map: { mappings: "" } };
if (result.map) {
if (pluginOptions.sourceMapCallback)
diff --git a/dist/rollup-plugin-typescript2.es.js b/dist/rollup-plugin-typescript2.es.js
index e43bf8f03bc6792b61d8352e04bb6466712426c2..420e8f0d0d109076bc72e9d60240077235a9ba11 100644
--- a/dist/rollup-plugin-typescript2.es.js
+++ b/dist/rollup-plugin-typescript2.es.js
@@ -29770,6 +29770,13 @@ const typescript = (options) => {
declarations[key] = { type: result.dts, map: result.dtsmap };
context.debug(() => `${safe.exports.blue("generated declarations")} for '${key}'`);
}
+ // if a user sets this compilerOption, they probably want another plugin (e.g. Babel, ESBuild) to transform their TS instead, while rpt2 just type-checks and/or outputs declarations
+ // note that result.code is non-existent if emitDeclarationOnly per https://github.com/ezolenko/rollup-plugin-typescript2/issues/268
+ if (parsedConfig.options.emitDeclarationOnly)
+ {
+ context.debug(() => `${blue("emitDeclarationOnly")} enabled, not transforming TS'`);
+ return undefined;
+ }
const transformResult = { code: result.code, map: { mappings: "" } };
if (result.map) {
if (pluginOptions.sourceMapCallback)

View File

@@ -46,18 +46,19 @@ pnpm add discord.js
## Example usage
Install discord.js:
Install all required dependencies:
```sh-session
npm install discord.js
yarn add discord.js
pnpm add discord.js
npm install discord.js @discordjs/rest
yarn add discord.js @discordjs/rest
pnpm add discord.js @discordjs/rest
```
Register a slash command against the Discord API:
```js
const { REST, Routes } = require('discord.js');
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord.js');
const commands = [
{
@@ -72,7 +73,7 @@ const rest = new REST({ version: '10' }).setToken('token');
try {
console.log('Started refreshing application (/) commands.');
await rest.put(Routes.applicationCommands(CLIENT_ID), { body: commands });
await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), { body: commands });
console.log('Successfully reloaded application (/) commands.');
} catch (error) {

93
build.config.ts Normal file
View File

@@ -0,0 +1,93 @@
import { relative, resolve } from 'node:path';
import glob from 'fast-glob';
import typescript from 'rollup-plugin-typescript2';
import { defineBuildConfig, BuildEntry } from 'unbuild';
interface ConfigOptions {
entries: (BuildEntry | string)[];
minify: boolean;
emitCJS: boolean;
externals: string[];
cjsBridge: boolean;
sourcemap: boolean;
preserveModules: boolean;
preserveModulesRoot: string;
declaration: boolean;
typeCheck: boolean;
}
export function createUnbuildConfig({
entries = [{ builder: 'rollup', input: 'src/index' }],
minify = false,
emitCJS = true,
cjsBridge = true,
externals = [],
sourcemap = true,
preserveModules = true,
preserveModulesRoot = 'src',
declaration = true,
typeCheck = false,
}: Partial<ConfigOptions> = {}) {
const files = glob
.sync('**', { cwd: 'src' })
.map((file) => [`${file.slice(0, -2)}cjs`, `${file.slice(0, -2)}mjs`])
.flat();
return defineBuildConfig({
entries,
clean: true,
rollup: {
esbuild: {
minify,
minifyIdentifiers: false,
},
emitCJS,
cjsBridge,
json: {
namedExports: false,
},
},
externals: [...files, ...externals],
hooks: {
'rollup:options': (_, options) => {
// @ts-expect-error: This will always be an array
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
options.output![0] = {
// @ts-expect-error: This will always be an array
...options.output![0],
sourcemap,
preserveModules,
preserveModulesRoot,
};
if (emitCJS) {
// @ts-expect-error: This will always be an array
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
options.output![1] = {
// @ts-expect-error: This will always be an array
...options.output![1],
sourcemap,
preserveModules,
preserveModulesRoot,
};
}
if (declaration) {
options.plugins?.unshift(
typescript({
check: typeCheck,
tsconfig: relative(__dirname, resolve(process.cwd(), 'tsconfig.json')),
tsconfigOverride: {
compilerOptions: {
emitDeclarationOnly: true,
},
},
}),
);
}
},
},
});
}

View File

@@ -4,13 +4,13 @@
"description": "A powerful library for interacting with the Discord API",
"private": true,
"scripts": {
"build": "turbo run build",
"test": "turbo run test --parallel",
"lint": "turbo run lint --parallel",
"format": "turbo run format --parallel",
"fmt": "turbo run format --parallel",
"build": "yarn workspaces foreach --parallel --topological run build",
"test": "yarn workspaces foreach --parallel --topological run test",
"lint": "yarn workspaces foreach --parallel --topological run lint",
"format": "yarn workspaces foreach --parallel --topological run format",
"fmt": "yarn format",
"postinstall": "is-ci || husky install",
"docs": "turbo run docs --parallel",
"docs": "yarn workspaces foreach --parallel --topological run docs",
"update": "yarn upgrade-interactive"
},
"contributors": [
@@ -18,7 +18,7 @@
"Amish Shah <amishshah.2k@gmail.com>",
"Vlad Frangu <kingdgrizzle@gmail.com>",
"SpaceEEC <spaceeec@yahoo.com>",
"Aura Román <kyradiscord@gmail.com>"
"Antonio Roman <kyradiscord@gmail.com>"
],
"keywords": [
"discord",
@@ -37,19 +37,27 @@
},
"homepage": "https://discord.js.org",
"devDependencies": {
"@commitlint/cli": "^17.1.2",
"@commitlint/config-angular": "^17.1.0",
"@favware/cliff-jumper": "^1.8.7",
"@favware/npm-deprecate": "^1.0.5",
"@commitlint/cli": "^17.0.3",
"@commitlint/config-angular": "^17.0.3",
"@favware/cliff-jumper": "^1.8.5",
"@favware/npm-deprecate": "^1.0.4",
"@typescript-eslint/eslint-plugin": "^5.31.0",
"@typescript-eslint/parser": "^5.31.0",
"conventional-changelog-cli": "^2.2.2",
"eslint": "^8.20.0",
"eslint-config-marine": "^9.4.1",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.3.0",
"eslint-plugin-import": "^2.26.0",
"fast-glob": "^3.2.11",
"husky": "^8.0.1",
"is-ci": "^3.0.1",
"lint-staged": "^13.0.3",
"tsup": "^6.2.3",
"turbo": "^1.5.3",
"typescript": "^4.8.3"
"prettier": "^2.7.1",
"typescript": "^4.7.4"
},
"resolutions": {
"rollup-plugin-typescript2@0.32.1": "patch:rollup-plugin-typescript2@npm:0.32.1#.yarn/patches/rollup-plugin-typescript2-npm-0.32.1-b5887420f2.patch",
"@microsoft/tsdoc-config": "patch:@microsoft/tsdoc-config@npm:0.16.1#.yarn/patches/@microsoft-tsdoc-config-npm-0.16.1-81031b1bbf.patch"
},
"engines": {

View File

@@ -5,4 +5,5 @@ dist/
docs/**/*
!docs/index.yml
!docs/README.md
coverage/
coverage/
tsup.config.*.mjs

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest';
import { formatTag } from '../src/index.js';
import { formatTag } from '../src';
describe('Format Tag', () => {
test('GIVEN tag with a prefix THEN format tag to not contain the prefix', () => {

View File

@@ -0,0 +1,3 @@
import { createUnbuildConfig } from '../../build.config';
export default createUnbuildConfig({ minify: true });

View File

@@ -5,13 +5,19 @@
"private": true,
"scripts": {
"test": "vitest run",
"build": "tsup",
"lint": "prettier --check . && cross-env TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && cross-env TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format"
},
"main": "./dist/index.mjs",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"directories": {
"lib": "src",
"test": "__tests__"
@@ -39,19 +45,19 @@
},
"homepage": "https://discord.js.org",
"dependencies": {
"@actions/core": "^1.9.1",
"@actions/core": "^1.9.0",
"tslib": "^2.4.0"
},
"devDependencies": {
"@types/node": "^16.11.60",
"@vitest/coverage-c8": "^0.23.4",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-config-neon": "^0.1.33",
"@types/node": "^16.11.45",
"c8": "^7.12.0",
"eslint": "^8.20.0",
"prettier": "^2.7.1",
"tsup": "^6.2.3",
"typescript": "^4.8.3",
"vitest": "^0.23.4"
"rollup-plugin-typescript2": "0.32.1",
"tsup": "^6.2.0",
"typescript": "^4.7.4",
"unbuild": "^0.7.6",
"vitest": "^0.19.1"
},
"engines": {
"node": ">=16.9.0"

View File

@@ -11,4 +11,4 @@ outputs:
description: 'The semver string that was extracted from this tag'
runs:
using: node16
main: ../../dist/index.mjs
main: ../../dist/formatTag/index.js

View File

@@ -1,5 +1,4 @@
export function formatTag(tag: string) {
// eslint-disable-next-line unicorn/no-unsafe-regex, prefer-named-capture-group
const parsed = /(^@.*\/(?<package>.*)@v?)?(?<semver>\d+.\d+.\d+)-?.*/.exec(tag);
if (parsed?.groups) {

View File

@@ -1,5 +1,5 @@
import { getInput, setOutput } from '@actions/core';
import { formatTag } from './formatTag.js';
import { formatTag } from './formatTag';
const tag = getInput('tag', { required: true });
const parsed = formatTag(tag);

View File

@@ -1 +1 @@
export * from './formatTag/formatTag.js';
export * from './formatTag/formatTag';

View File

@@ -1,7 +0,0 @@
import { createTsupConfig } from '../../tsup.config.js';
export default createTsupConfig({
entry: ['src/index.ts', 'src/formatTag/index.ts'],
format: ['esm'],
minify: true,
});

View File

@@ -0,0 +1,9 @@
import { createTsupConfig } from '../../tsup.config';
export default createTsupConfig({
entry: ['src/index.ts', 'src/formatTag/index.ts'],
format: ['cjs'],
skipNodeModulesBundle: false,
noExternal: ['@actions/core'],
minify: true,
});

View File

@@ -1,3 +0,0 @@
{
"extends": "../../.eslintrc.json"
}

View File

@@ -1,27 +0,0 @@
# Packages
node_modules/
# Log files
logs/
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Env
.env
# Dist
dist/
typings/
docs/**/*
!docs/index.json
!docs/README.md
# Miscellaneous
.tmp/
coverage/
tsconfig.tsbuildinfo

View File

@@ -1,8 +0,0 @@
# Autogenerated
CHANGELOG.md
.turbo
dist/
docs/**/*
!docs/index.yml
!docs/README.md
coverage/

View File

@@ -1,190 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2022 Noel Buechler
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,34 +0,0 @@
<div align="center">
<br />
<p>
<a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
</p>
<br />
<p>
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
</p>
</div>
## Links
- [Website](https://discord.js.org/) ([source](https://github.com/discordjs/discord.js/tree/main/packages/website))
- [Documentation](https://discord.js.org/#/docs)
- [Guide](https://discordjs.guide/) ([source](https://github.com/discordjs/guide))
See also the [Update Guide](https://discordjs.guide/additional-info/changes-in-v14.html), including updated and removed items in the library.
- [discord.js Discord server](https://discord.gg/djs)
- [Discord API Discord server](https://discord.gg/discord-api)
- [GitHub](https://github.com/discordjs/discord.js/tree/main/packages/scripts)
- [Related libraries](https://discord.com/developers/docs/topics/community-resources#libraries)
## Contributing
See [the contribution guide](https://github.com/discordjs/discord.js/blob/main/.github/CONTRIBUTING.md) if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
nudge in the right direction, please don't hesitate to join our official [discord.js Server](https://discord.gg/djs).

View File

@@ -1,52 +0,0 @@
{
"name": "@discordjs/api-extractor-utils",
"version": "1.0.0",
"description": "Utilities for api-extractor",
"private": true,
"scripts": {
"build": "tsup",
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext mjs,js,ts",
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext mjs,js,ts --fix",
"fmt": "yarn format"
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"directories": {
"lib": "src"
},
"files": [
"dist"
],
"contributors": [
"Suneet Tipirneni <suneettipirneni@icloud.com>"
],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/discordjs/discord.js.git"
},
"bugs": {
"url": "https://github.com/discordjs/discord.js/issues"
},
"homepage": "https://discord.js.org",
"dependencies": {
"@microsoft/api-extractor-model": "7.24.0",
"@microsoft/tsdoc": "0.14.1"
},
"devDependencies": {
"@types/node": "^16.11.60",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-config-neon": "^0.1.33",
"prettier": "^2.7.1",
"tsup": "^6.2.3",
"typescript": "^4.8.3"
},
"engines": {
"node": ">=16.9.0"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -1,416 +0,0 @@
import type { ApiReturnTypeMixin } from '@microsoft/api-extractor-model';
import {
type ApiModel,
ApiDeclaredItem,
type ApiPropertyItem,
type ApiMethod,
type ApiParameterListMixin,
type ApiTypeParameterListMixin,
type ApiClass,
type ApiFunction,
ApiItemKind,
type ApiTypeAlias,
type ApiEnum,
type ApiInterface,
type ApiMethodSignature,
type ApiPropertySignature,
type ApiVariable,
type ApiItem,
type ApiConstructor,
type ApiItemContainerMixin,
} from '@microsoft/api-extractor-model';
import { generateTypeParamData } from './TypeParameterJSONEncoder.js';
import { type TokenDocumentation, resolveName, genReference, genToken, genParameter, generatePath } from './parse.js';
import type { DocBlockJSON } from './tsdoc/CommentBlock.js';
import type { AnyDocNodeJSON } from './tsdoc/CommentNode.js';
import { type DocNodeContainerJSON, nodeContainer } from './tsdoc/CommentNodeContainer.js';
import { createCommentNode } from './tsdoc/index.js';
export interface ReferenceData {
name: string;
path: string;
}
export interface InheritanceData {
parentKey: string;
parentName: string;
path: string;
}
export interface ApiInheritableJSON {
inheritanceData: InheritanceData | null;
}
export interface ApiItemJSON {
comment: AnyDocNodeJSON | null;
containerKey: string;
deprecated: DocNodeContainerJSON | null;
excerpt: string;
excerptTokens: TokenDocumentation[];
kind: string;
name: string;
path: string[];
referenceData: ReferenceData;
remarks: DocNodeContainerJSON | null;
summary: DocNodeContainerJSON | null;
}
export interface ApiPropertyItemJSON extends ApiItemJSON, ApiInheritableJSON {
optional: boolean;
propertyTypeTokens: TokenDocumentation[];
readonly: boolean;
}
export interface ApiTypeParameterListJSON {
typeParameters: ApiTypeParameterJSON[];
}
export interface ApiTypeParameterJSON {
commentBlock: DocBlockJSON | null;
constraintTokens: TokenDocumentation[];
defaultTokens: TokenDocumentation[];
name: string;
optional: boolean;
}
export interface ApiParameterListJSON {
parameters: ApiParameterJSON[];
}
export interface ApiMethodSignatureJSON
extends ApiItemJSON,
ApiTypeParameterListJSON,
ApiParameterListJSON,
ApiInheritableJSON {
mergedSiblings: ApiMethodSignatureJSON[];
optional: boolean;
overloadIndex: number;
returnTypeTokens: TokenDocumentation[];
}
export interface ApiMethodJSON extends ApiMethodSignatureJSON {
mergedSiblings: ApiMethodJSON[];
protected: boolean;
static: boolean;
}
export interface ApiParameterJSON {
isOptional: boolean;
name: string;
paramCommentBlock: DocBlockJSON | null;
tokens: TokenDocumentation[];
}
export interface ApiClassJSON extends ApiItemJSON, ApiTypeParameterListJSON {
constructor: ApiConstructorJSON | null;
extendsTokens: TokenDocumentation[];
implementsTokens: TokenDocumentation[][];
methods: ApiMethodJSON[];
properties: ApiPropertyItemJSON[];
}
export interface ApiTypeAliasJSON extends ApiItemJSON, ApiTypeParameterListJSON {
typeTokens: TokenDocumentation[];
}
export interface EnumMemberData {
initializerTokens: TokenDocumentation[];
name: string;
summary: DocNodeContainerJSON | null;
}
export interface ApiEnumJSON extends ApiItemJSON {
members: EnumMemberData[];
}
export interface ApiInterfaceJSON extends ApiItemJSON, ApiTypeParameterListJSON {
extendsTokens: TokenDocumentation[][] | null;
methods: ApiMethodSignatureJSON[];
properties: ApiPropertyItemJSON[];
}
export interface ApiVariableJSON extends ApiItemJSON {
readonly: boolean;
typeTokens: TokenDocumentation[];
}
export interface ApiFunctionJSON extends ApiItemJSON, ApiTypeParameterListJSON, ApiParameterListJSON {
mergedSiblings: ApiFunctionJSON[];
overloadIndex: number;
returnTypeTokens: TokenDocumentation[];
}
export interface ApiConstructorJSON extends ApiItemJSON, ApiParameterListJSON {
protected: boolean;
}
export type FunctionLike = ApiDeclaredItem & ApiParameterListMixin & ApiReturnTypeMixin & ApiTypeParameterListMixin;
export class ApiNodeJSONEncoder {
public static encode(model: ApiModel, node: ApiItem, version: string) {
if (!(node instanceof ApiDeclaredItem)) {
console.log(`Cannot serialize node of type ${node.kind}`);
return undefined;
}
switch (node.kind) {
case ApiItemKind.Class:
return this.encodeClass(model, node as ApiClass, version);
case ApiItemKind.Function:
return this.encodeFunction(model, node as ApiFunction, version);
case ApiItemKind.Interface:
return this.encodeInterface(model, node as ApiInterface, version);
case ApiItemKind.TypeAlias:
return this.encodeTypeAlias(model, node as ApiTypeAlias, version);
case ApiItemKind.Enum:
return this.encodeEnum(model, node as ApiEnum, version);
case ApiItemKind.Variable:
return this.encodeVariable(model, node as ApiVariable, version);
default:
// console.log(`Unknown API item kind: ${node.kind}`);
return undefined;
}
}
public static encodeItem(model: ApiModel, item: ApiDeclaredItem, version: string): ApiItemJSON {
const path = [];
for (const _item of item.getHierarchy()) {
switch (_item.kind) {
case 'None':
case 'EntryPoint':
case 'Model':
break;
default:
path.push(resolveName(_item));
}
}
return {
kind: item.kind,
name: resolveName(item),
referenceData: genReference(item, version),
excerpt: item.excerpt.text,
excerptTokens: item.excerpt.spannedTokens.map((token) => genToken(model, token, version)),
remarks: item.tsdocComment?.remarksBlock
? (createCommentNode(item.tsdocComment.remarksBlock, model, version, item.parent) as DocNodeContainerJSON)
: null,
summary: item.tsdocComment?.summarySection
? (createCommentNode(item.tsdocComment.summarySection, model, version, item.parent) as DocNodeContainerJSON)
: null,
deprecated: item.tsdocComment?.deprecatedBlock
? (createCommentNode(item.tsdocComment.deprecatedBlock, model, version, item.parent) as DocNodeContainerJSON)
: null,
path,
containerKey: item.containerKey,
comment: item.tsdocComment ? createCommentNode(item.tsdocComment, model, version, item.parent) : null,
};
}
public static encodeParameterList(
model: ApiModel,
item: ApiDeclaredItem & ApiParameterListMixin,
version: string,
): { parameters: ApiParameterJSON[] } {
return {
parameters: item.parameters.map((param) => genParameter(model, param, version)),
};
}
public static encodeTypeParameterList(
model: ApiModel,
item: ApiDeclaredItem & ApiTypeParameterListMixin,
version: string,
): ApiTypeParameterListJSON {
return {
typeParameters: item.typeParameters.map((param) => generateTypeParamData(model, param, version, item.parent)),
};
}
public static encodeProperty(
model: ApiModel,
item: ApiPropertyItem,
parent: ApiItemContainerMixin,
version: string,
): ApiPropertyItemJSON {
return {
...this.encodeItem(model, item, version),
...this.encodeInheritanceData(item, parent, version),
propertyTypeTokens: item.propertyTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)),
readonly: item.isReadonly,
optional: item.isOptional,
};
}
public static encodeInheritanceData(
item: ApiDeclaredItem,
parent: ApiItemContainerMixin,
version: string,
): ApiInheritableJSON {
return {
inheritanceData:
item.parent && item.parent.containerKey !== parent.containerKey
? {
parentKey: item.parent.containerKey,
parentName: item.parent.displayName,
path: generatePath(item.parent.getHierarchy(), version),
}
: null,
};
}
public static encodeFunctionLike(model: ApiModel, item: FunctionLike, version: string) {
return {
...this.encodeItem(model, item, version),
...this.encodeParameterList(model, item, version),
...this.encodeTypeParameterList(model, item, version),
returnTypeTokens: item.returnTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)),
overloadIndex: item.overloadIndex,
};
}
public static encodeFunction(model: ApiModel, item: FunctionLike, version: string, nested = false): ApiFunctionJSON {
return {
...this.encodeFunctionLike(model, item, version),
mergedSiblings: nested
? []
: item.getMergedSiblings().map((item) => this.encodeFunction(model, item as ApiFunction, version, true)),
};
}
public static encodeMethodSignature(
model: ApiModel,
item: ApiMethodSignature,
parent: ApiItemContainerMixin,
version: string,
nested = false,
): ApiMethodSignatureJSON {
return {
...this.encodeFunctionLike(model, item, version),
...this.encodeInheritanceData(item, parent, version),
optional: item.isOptional,
mergedSiblings: nested
? []
: item
.getMergedSiblings()
.map((item) => this.encodeMethodSignature(model, item as ApiMethodSignature, parent, version, true)),
};
}
public static encodeMethod(
model: ApiModel,
item: ApiMethod,
parent: ApiItemContainerMixin,
version: string,
nested = false,
): ApiMethodJSON {
return {
...this.encodeMethodSignature(model, item, parent, version),
static: item.isStatic,
protected: item.isProtected,
mergedSiblings: nested
? []
: item.getMergedSiblings().map((item) => this.encodeMethod(model, item as ApiMethod, parent, version, true)),
};
}
public static encodeClass(model: ApiModel, item: ApiClass, version: string): ApiClassJSON {
const extendsExcerpt = item.extendsType?.excerpt;
const methods: ApiMethodJSON[] = [];
const properties: ApiPropertyItemJSON[] = [];
let constructor: ApiConstructor | undefined;
for (const member of item.findMembersWithInheritance().items) {
switch (member.kind) {
case ApiItemKind.Method:
methods.push(this.encodeMethod(model, member as ApiMethod, item, version));
break;
case ApiItemKind.Property:
properties.push(this.encodeProperty(model, member as ApiPropertyItem, item, version));
break;
case ApiItemKind.Constructor:
constructor = member as ApiConstructor;
break;
default:
break;
}
}
return {
...this.encodeItem(model, item, version),
...this.encodeTypeParameterList(model, item, version),
constructor: constructor ? this.encodeConstructor(model, constructor, version) : null,
extendsTokens: extendsExcerpt ? extendsExcerpt.spannedTokens.map((token) => genToken(model, token, version)) : [],
implementsTokens: item.implementsTypes.map((excerpt) =>
excerpt.excerpt.spannedTokens.map((token) => genToken(model, token, version)),
),
methods,
properties,
};
}
public static encodeTypeAlias(model: ApiModel, item: ApiTypeAlias, version: string): ApiTypeAliasJSON {
return {
...this.encodeItem(model, item, version),
...this.encodeTypeParameterList(model, item, version),
typeTokens: item.typeExcerpt.spannedTokens.map((token) => genToken(model, token, version)),
};
}
public static encodeEnum(model: ApiModel, item: ApiEnum, version: string): ApiEnumJSON {
return {
...this.encodeItem(model, item, version),
members: item.members.map((member) => ({
name: member.name,
initializerTokens:
member.initializerExcerpt?.spannedTokens.map((token) => genToken(model, token, version)) ?? [],
summary: member.tsdocComment ? nodeContainer(member.tsdocComment.summarySection, model, version, member) : null,
})),
};
}
public static encodeInterface(model: ApiModel, item: ApiInterface, version: string): ApiInterfaceJSON {
const methods: ApiMethodSignatureJSON[] = [];
const properties: ApiPropertyItemJSON[] = [];
for (const member of item.findMembersWithInheritance().items) {
switch (member.kind) {
case ApiItemKind.MethodSignature:
methods.push(this.encodeMethodSignature(model, member as ApiMethodSignature, item, version));
break;
case ApiItemKind.PropertySignature:
properties.push(this.encodeProperty(model, member as ApiPropertySignature, item, version));
break;
default:
break;
}
}
return {
...this.encodeItem(model, item, version),
...this.encodeTypeParameterList(model, item, version),
extendsTokens: item.extendsTypes.map((excerpt) =>
excerpt.excerpt.spannedTokens.map((token) => genToken(model, token, version)),
),
methods,
properties,
};
}
public static encodeVariable(model: ApiModel, item: ApiVariable, version: string): ApiVariableJSON {
return {
...this.encodeItem(model, item, version),
typeTokens: item.variableTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)),
readonly: item.isReadonly,
};
}
public static encodeConstructor(model: ApiModel, item: ApiConstructor, version: string): ApiConstructorJSON {
return {
...this.encodeItem(model, item, version),
...this.encodeParameterList(model, item, version),
protected: item.isProtected,
};
}
}

View File

@@ -1,31 +0,0 @@
import type { TypeParameter, ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import { type TokenDocumentation, genToken } from './parse.js';
import { type DocBlockJSON, block } from './tsdoc/CommentBlock.js';
export interface TypeParameterData {
commentBlock: DocBlockJSON | null;
constraintTokens: TokenDocumentation[];
defaultTokens: TokenDocumentation[];
name: string;
optional: boolean;
}
export function generateTypeParamData(
model: ApiModel,
typeParam: TypeParameter,
version: string,
parentItem?: ApiItem,
): TypeParameterData {
const constraintTokens = typeParam.constraintExcerpt.spannedTokens.map((token) => genToken(model, token, version));
const defaultTokens = typeParam.defaultTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version));
return {
name: typeParam.name,
constraintTokens,
defaultTokens,
optional: typeParam.isOptional,
commentBlock: typeParam.tsdocTypeParamBlock
? block(typeParam.tsdocTypeParamBlock, model, version, parentItem)
: null,
};
}

View File

@@ -1,4 +0,0 @@
export * from './ApiNodeJSONEncoder.js';
export * from './parse.js';
export * from './tsdoc/index.js';
export * from './TypeParameterJSONEncoder.js';

View File

@@ -1,18 +0,0 @@
import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import type { DocBlock } from '@microsoft/tsdoc';
import { blockTag, type DocBlockTagJSON } from './CommentBlockTag.js';
import { type AnyDocNodeJSON, type DocNodeJSON, node } from './CommentNode.js';
import { createCommentNode } from '.';
export interface DocBlockJSON extends DocNodeJSON {
content: AnyDocNodeJSON[];
tag: DocBlockTagJSON;
}
export function block(block: DocBlock, model: ApiModel, version: string, parentItem?: ApiItem) {
return {
...node(block),
content: block.content.nodes.map((node) => createCommentNode(node, model, version, parentItem)),
tag: blockTag(block.blockTag),
};
}

View File

@@ -1,13 +0,0 @@
import type { DocBlockTag } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocBlockTagJSON extends DocNodeJSON {
tagName: string;
}
export function blockTag(blockTag: DocBlockTag): DocBlockTagJSON {
return {
...node(blockTag),
tagName: blockTag.tagName,
};
}

View File

@@ -1,13 +0,0 @@
import type { DocCodeSpan } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocCodeSpanJSON extends DocNodeJSON {
code: string;
}
export function codeSpan(codeSpan: DocCodeSpan): DocCodeSpanJSON {
return {
...node(codeSpan),
code: codeSpan.code,
};
}

View File

@@ -1,28 +0,0 @@
import type { DocNode, DocNodeKind } from '@microsoft/tsdoc';
import type { DocBlockJSON } from './CommentBlock.js';
import type { DocCodeSpanJSON } from './CommentCodeSpan.js';
import type { DocNodeContainerJSON } from './CommentNodeContainer.js';
import type { DocFencedCodeJSON } from './FencedCodeCommentNode.js';
import type { DocLinkTagJSON } from './LinkTagCommentNode.js';
import type { DocPlainTextJSON } from './PlainTextCommentNode.js';
import type { DocCommentJSON } from './RootComment.js';
export interface DocNodeJSON {
kind: DocNodeKind;
}
export type AnyDocNodeJSON =
| DocBlockJSON
| DocCodeSpanJSON
| DocCommentJSON
| DocFencedCodeJSON
| DocLinkTagJSON
| DocNodeContainerJSON
| DocNodeJSON
| DocPlainTextJSON;
export function node(node: DocNode): DocNodeJSON {
return {
kind: node.kind as DocNodeKind,
};
}

View File

@@ -1,20 +0,0 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocNodeContainer } from '@microsoft/tsdoc';
import { type AnyDocNodeJSON, type DocNodeJSON, node } from './CommentNode.js';
import { createCommentNode } from '.';
export interface DocNodeContainerJSON extends DocNodeJSON {
nodes: AnyDocNodeJSON[];
}
export function nodeContainer(
container: DocNodeContainer,
model: ApiModel,
version: string,
parentItem?: ApiItem,
): DocNodeContainerJSON {
return {
...node(container),
nodes: container.nodes.map((node) => createCommentNode(node, model, version, parentItem)),
};
}

View File

@@ -1,15 +0,0 @@
import type { DocFencedCode } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocFencedCodeJSON extends DocNodeJSON {
code: string;
language: string;
}
export function fencedCode(fencedCode: DocFencedCode) {
return {
...node(fencedCode),
language: fencedCode.language,
code: fencedCode.code,
};
}

View File

@@ -1,60 +0,0 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocDeclarationReference, DocLinkTag } from '@microsoft/tsdoc';
import { resolveName, generatePath } from '../parse.js';
import { type DocNodeJSON, node } from './CommentNode.js';
interface LinkTagCodeLink {
kind: string;
name: string;
path: string;
}
export interface DocLinkTagJSON extends DocNodeJSON {
codeDestination: LinkTagCodeLink | null;
text: string | null;
urlDestination: string | null;
}
export function genLinkToken(
model: ApiModel,
ref: DocDeclarationReference,
context: ApiItem | null,
version: string,
): LinkTagCodeLink | null {
const item = model.resolveDeclarationReference(ref, context ?? undefined).resolvedApiItem ?? null;
if (!item) {
return null;
}
return {
name: resolveName(item),
kind: item.kind,
path: generatePath(item.getHierarchy(), version),
};
}
export function linkTagNode(
linkNode: DocLinkTag,
model: ApiModel,
version: string,
parentItem?: ApiItem,
): DocLinkTagJSON {
// If we weren't provided a parent object, fallback to the package entrypoint.
const packageEntryPoint = linkNode.codeDestination?.importPath
? model.getAssociatedPackage()?.findEntryPointsByPath(linkNode.codeDestination.importPath)[0]
: null;
const codeDestination = linkNode.codeDestination
? genLinkToken(model, linkNode.codeDestination, parentItem ?? packageEntryPoint ?? null, version)
: null;
const text = linkNode.linkText ?? null;
const urlDestination = linkNode.urlDestination ?? null;
return {
...node(linkNode),
text,
codeDestination,
urlDestination,
};
}

View File

@@ -1,19 +0,0 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocParamBlock } from '@microsoft/tsdoc';
import { block, type DocBlockJSON } from './CommentBlock.js';
interface DocParamBlockJSON extends DocBlockJSON {
name: string;
}
export function paramBlock(
paramBlock: DocParamBlock,
model: ApiModel,
version: string,
parentItem?: ApiItem,
): DocParamBlockJSON {
return {
...block(paramBlock, model, version, parentItem),
name: paramBlock.parameterName,
};
}

View File

@@ -1,13 +0,0 @@
import type { DocPlainText } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocPlainTextJSON extends DocNodeJSON {
text: string;
}
export function plainTextNode(plainTextNode: DocPlainText): DocPlainTextJSON {
return {
...node(plainTextNode),
text: plainTextNode.text,
};
}

View File

@@ -1,24 +0,0 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocComment } from '@microsoft/tsdoc';
import { block, type DocBlockJSON } from './CommentBlock.js';
import { type DocNodeJSON, node } from './CommentNode.js';
import { createCommentNode } from '.';
export interface DocCommentJSON extends DocNodeJSON {
customBlocks: DocBlockJSON[];
deprecated: DocNodeJSON[];
remarks: DocNodeJSON[];
summary: DocNodeJSON[];
}
export function comment(comment: DocComment, model: ApiModel, version: string, parentItem?: ApiItem): DocCommentJSON {
return {
...node(comment),
summary: comment.summarySection.nodes.map((node) => createCommentNode(node, model, version, parentItem)),
remarks:
comment.remarksBlock?.content.nodes.map((node) => createCommentNode(node, model, version, parentItem)) ?? [],
deprecated:
comment.deprecatedBlock?.content.nodes.map((node) => createCommentNode(node, model, version, parentItem)) ?? [],
customBlocks: comment.customBlocks.map((_block) => block(_block, model, version, parentItem)),
};
}

View File

@@ -1,62 +0,0 @@
import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import {
type DocNode,
type DocPlainText,
type DocLinkTag,
type DocParagraph,
type DocFencedCode,
DocNodeKind,
type DocBlock,
type DocComment,
type DocCodeSpan,
type DocParamBlock,
} from '@microsoft/tsdoc';
import { block } from './CommentBlock.js';
import { codeSpan } from './CommentCodeSpan.js';
import { node as _node, type AnyDocNodeJSON } from './CommentNode.js';
import { nodeContainer } from './CommentNodeContainer.js';
import { fencedCode } from './FencedCodeCommentNode.js';
import { linkTagNode } from './LinkTagCommentNode.js';
import { paramBlock } from './ParamBlock.js';
import { plainTextNode } from './PlainTextCommentNode.js';
import { comment } from './RootComment.js';
export function createCommentNode(
node: DocNode,
model: ApiModel,
version: string,
parentItem?: ApiItem,
): AnyDocNodeJSON {
switch (node.kind) {
case DocNodeKind.PlainText:
return plainTextNode(node as DocPlainText);
case DocNodeKind.LinkTag:
return linkTagNode(node as DocLinkTag, model, version, parentItem);
case DocNodeKind.Paragraph:
case DocNodeKind.Section:
return nodeContainer(node as DocParagraph, model, version, parentItem);
case DocNodeKind.FencedCode:
return fencedCode(node as DocFencedCode);
case DocNodeKind.CodeSpan:
return codeSpan(node as DocCodeSpan);
case DocNodeKind.Block:
return block(node as DocBlock, model, version, parentItem);
case DocNodeKind.ParamBlock:
return paramBlock(node as DocParamBlock, model, version, parentItem);
case DocNodeKind.Comment:
return comment(node as DocComment, model, version, parentItem);
default:
return _node(node);
}
}
export * from './CommentNode.js';
export * from './CommentNodeContainer.js';
export * from './CommentBlock.js';
export * from './CommentBlockTag.js';
export * from './CommentCodeSpan.js';
export * from './FencedCodeCommentNode.js';
export * from './LinkTagCommentNode.js';
export * from './ParamBlock.js';
export * from './PlainTextCommentNode.js';
export * from './RootComment.js';

View File

@@ -1,20 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"allowJs": true
},
"include": [
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.mjs",
"**/*.jsx",
"**/*.test.ts",
"**/*.test.js",
"**/*.test.mjs",
"**/*.spec.ts",
"**/*.spec.js",
"**/*.spec.mjs"
],
"exclude": []
}

View File

@@ -1,8 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"skipDefaultLibCheck": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}

View File

@@ -1,5 +0,0 @@
import { createTsupConfig } from '../../tsup.config.js';
export default createTsupConfig({
minify: true,
});

View File

@@ -5,4 +5,5 @@ dist/
docs/**/*
!docs/index.yml
!docs/README.md
coverage/
coverage/
tsup.config.*.mjs

View File

@@ -2,35 +2,6 @@
All notable changes to this project will be documented in this file.
# [@discordjs/builders@1.2.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.1.0...@discordjs/builders@1.2.0) - (2022-08-22)
## Features
- **website:** Show `constructor` information (#8540) ([e42fd16](https://github.com/discordjs/discord.js/commit/e42fd1636973b10dd7ed6fb4280ee1a4a8f82007))
- **website:** Show descriptions for `@typeParam` blocks (#8523) ([e475b63](https://github.com/discordjs/discord.js/commit/e475b63f257f6261d73cb89fee9ecbcdd84e2a6b))
- **website:** Show parameter descriptions (#8519) ([7f415a2](https://github.com/discordjs/discord.js/commit/7f415a2502bf7ce2025dbcfed9017b0635a19966))
- **WebSocketShard:** Support new resume url (#8480) ([bc06cc6](https://github.com/discordjs/discord.js/commit/bc06cc638d2f57ab5c600e8cdb6afc8eb2180166))
## Refactor
- Docs design (#8487) ([4ab1d09](https://github.com/discordjs/discord.js/commit/4ab1d09997a18879a9eb9bda39df6f15aa22557e))
# [@discordjs/builders@1.1.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.0.0...@discordjs/builders@1.1.0) - (2022-07-29)
## Bug Fixes
- Use proper format for `@link` text (#8384) ([2655639](https://github.com/discordjs/discord.js/commit/26556390a3800e954974a00c1328ff47d3e67e9a))
- **Formatters:** Add newline in `codeBlock` (#8369) ([5d8bd03](https://github.com/discordjs/discord.js/commit/5d8bd030d60ef364de3ef5f9963da8bda5c4efd4))
- **selectMenu:** Allow json to be used for select menu options (#8322) ([6a2d0d8](https://github.com/discordjs/discord.js/commit/6a2d0d8e96d157d5b85cee7f17bffdfff4240074))
## Documentation
- Use link tags (#8382) ([5494791](https://github.com/discordjs/discord.js/commit/549479131318c659f86f0eb18578d597e22522d3))
## Features
- Add channel & message URL formatters (#8371) ([a7deb8f](https://github.com/discordjs/discord.js/commit/a7deb8f89830ead6185c5fb46a49688b6d209ed1))
# [@discordjs/builders@1.0.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@0.16.0...@discordjs/builders@1.0.0) - (2022-07-17)
## Info

View File

@@ -1,9 +1,4 @@
import {
ButtonStyle,
ComponentType,
type APIActionRowComponent,
type APIMessageActionRowComponent,
} from 'discord-api-types/v10';
import { APIActionRowComponent, APIMessageActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
ActionRowBuilder,
@@ -11,7 +6,7 @@ import {
createComponentBuilder,
SelectMenuBuilder,
SelectMenuOptionBuilder,
} from '../../src/index.js';
} from '../../src';
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
type: ComponentType.ActionRow,

View File

@@ -1,12 +1,12 @@
import {
APIButtonComponentWithCustomId,
APIButtonComponentWithURL,
ButtonStyle,
ComponentType,
type APIButtonComponentWithCustomId,
type APIButtonComponentWithURL,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions.js';
import { ButtonBuilder } from '../../src/components/button/Button.js';
import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions';
import { ButtonBuilder } from '../../src/components/button/Button';
const buttonComponent = () => new ButtonBuilder();
@@ -31,7 +31,7 @@ describe('Button Components', () => {
expect(() => buttonStyleValidator.parse(ButtonStyle.Secondary)).not.toThrowError();
});
test('GIVEN invalid style THEN validator does throw', () => {
test('GIVEN invalid style THEN validator does not throw', () => {
expect(() => buttonStyleValidator.parse(7)).toThrowError();
});
@@ -71,7 +71,7 @@ describe('Button Components', () => {
}).toThrowError();
expect(() => {
// @ts-expect-error: Invalid emoji
// @ts-expect-error
const button = buttonComponent().setEmoji('test');
button.toJSON();
}).toThrowError();
@@ -103,9 +103,9 @@ describe('Button Components', () => {
expect(() => buttonComponent().setStyle(24)).toThrowError();
expect(() => buttonComponent().setLabel(longStr)).toThrowError();
// @ts-expect-error: Invalid parameter for disabled
// @ts-expect-error
expect(() => buttonComponent().setDisabled(0)).toThrowError();
// @ts-expect-error: Invalid emoji
// @ts-expect-error
expect(() => buttonComponent().setEmoji('foo')).toThrowError();
expect(() => buttonComponent().setURL('foobar')).toThrowError();

View File

@@ -1,12 +1,12 @@
import {
APIActionRowComponent,
APIButtonComponent,
APIMessageActionRowComponent,
APISelectMenuComponent,
APITextInputComponent,
ButtonStyle,
ComponentType,
TextInputStyle,
type APIButtonComponent,
type APIMessageActionRowComponent,
type APISelectMenuComponent,
type APITextInputComponent,
type APIActionRowComponent,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
@@ -15,7 +15,7 @@ import {
createComponentBuilder,
SelectMenuBuilder,
TextInputBuilder,
} from '../../src/index.js';
} from '../../src/index';
describe('createComponentBuilder', () => {
test.each([ButtonBuilder, SelectMenuBuilder, TextInputBuilder])(
@@ -67,7 +67,7 @@ describe('createComponentBuilder', () => {
});
test('GIVEN an unknown component type THEN throws error', () => {
// @ts-expect-error: Unknown component type
// @ts-expect-error
expect(() => createComponentBuilder({ type: 'invalid' })).toThrowError();
});
});

View File

@@ -1,6 +1,6 @@
import { ComponentType, type APISelectMenuComponent, type APISelectMenuOption } from 'discord-api-types/v10';
import { APISelectMenuComponent, APISelectMenuOption, ComponentType } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { SelectMenuBuilder, SelectMenuOptionBuilder } from '../../src/index.js';
import { SelectMenuBuilder, SelectMenuOptionBuilder } from '../../src/index';
const selectMenu = () => new SelectMenuBuilder();
const selectMenuOption = () => new SelectMenuOptionBuilder();
@@ -74,8 +74,7 @@ describe('Select Menu Components', () => {
]),
).not.toThrowError();
const options = Array.from<APISelectMenuOption>({ length: 25 }).fill({ label: 'test', value: 'test' });
const options = new Array<APISelectMenuOption>(25).fill({ label: 'test', value: 'test' });
expect(() => selectMenu().addOptions(...options)).not.toThrowError();
expect(() => selectMenu().setOptions(...options)).not.toThrowError();
expect(() => selectMenu().addOptions(options)).not.toThrowError();
@@ -84,13 +83,12 @@ describe('Select Menu Components', () => {
expect(() =>
selectMenu()
.addOptions({ label: 'test', value: 'test' })
.addOptions(...Array.from<APISelectMenuOption>({ length: 24 }).fill({ label: 'test', value: 'test' })),
.addOptions(...new Array<APISelectMenuOption>(24).fill({ label: 'test', value: 'test' })),
).not.toThrowError();
expect(() =>
selectMenu()
.addOptions([{ label: 'test', value: 'test' }])
.addOptions(Array.from<APISelectMenuOption>({ length: 24 }).fill({ label: 'test', value: 'test' })),
.addOptions(new Array<APISelectMenuOption>(24).fill({ label: 'test', value: 'test' })),
).not.toThrowError();
});
@@ -98,34 +96,33 @@ describe('Select Menu Components', () => {
expect(() => selectMenu().setCustomId(longStr)).toThrowError();
expect(() => selectMenu().setMaxValues(30)).toThrowError();
expect(() => selectMenu().setMinValues(-20)).toThrowError();
// @ts-expect-error: Invalid disabled value
// @ts-expect-error
expect(() => selectMenu().setDisabled(0)).toThrowError();
expect(() => selectMenu().setPlaceholder(longStr)).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions({ label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: longStr, value: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ value: longStr, label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', description: longStr })).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', default: 100 })).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions({ value: 'test' })).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions({ default: true })).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', description: longStr }])).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', default: 100 }])).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions([{ value: 'test' }])).toThrowError();
// @ts-expect-error: Invalid option
// @ts-expect-error
expect(() => selectMenu().addOptions([{ default: true }])).toThrowError();
const tooManyOptions = Array.from<APISelectMenuOption>({ length: 26 }).fill({ label: 'test', value: 'test' });
const tooManyOptions = new Array<APISelectMenuOption>(26).fill({ label: 'test', value: 'test' });
expect(() => selectMenu().setOptions(...tooManyOptions)).toThrowError();
expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError();
@@ -144,9 +141,9 @@ describe('Select Menu Components', () => {
selectMenuOption()
.setLabel(longStr)
.setValue(longStr)
// @ts-expect-error: Invalid default value
// @ts-expect-error
.setDefault(-1)
// @ts-expect-error: Invalid emoji
// @ts-expect-error
.setEmoji({ name: 1 })
.setDescription(longStr);
}).toThrowError();

View File

@@ -1,4 +1,4 @@
import { ComponentType, TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import { APITextInputComponent, ComponentType, TextInputStyle } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
labelValidator,
@@ -7,10 +7,10 @@ import {
placeholderValidator,
valueValidator,
textInputStyleValidator,
} from '../../src/components/textInput/Assertions.js';
import { TextInputBuilder } from '../../src/components/textInput/TextInput.js';
} from '../../src/components/textInput/Assertions';
import { TextInputBuilder } from '../../src/components/textInput/TextInput';
const superLongStr = 'a'.repeat(5_000);
const superLongStr = 'a'.repeat(5000);
const textInputComponent = () => new TextInputBuilder();
@@ -47,7 +47,7 @@ describe('Text Input Components', () => {
});
test('GIVEN invalid min length THEN validator does throw 2', () => {
expect(() => maxLengthValidator.parse(4_001)).toThrowError();
expect(() => maxLengthValidator.parse(4001)).toThrowError();
});
test('GIVEN valid value THEN validator does not throw', () => {
@@ -85,7 +85,7 @@ describe('Text Input Components', () => {
expect(() => {
// Issue #8107
// @ts-expect-error: Shapeshift maps the enum key to the value when parsing
// @ts-expect-error: shapeshift maps the enum key to the value when parsing
textInputComponent().setCustomId('Custom').setLabel('Guess').setStyle('Short').toJSON();
}).not.toThrowError();
});

View File

@@ -1,6 +1,6 @@
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { ContextMenuCommandAssertions, ContextMenuCommandBuilder } from '../../src/index.js';
import { ContextMenuCommandAssertions, ContextMenuCommandBuilder } from '../../src/index';
const getBuilder = () => new ContextMenuCommandBuilder();
@@ -105,9 +105,9 @@ describe('Context Menu Commands', () => {
});
test('GIVEN invalid name localizations THEN does throw error', () => {
// @ts-expect-error: Invalid localization
// @ts-expect-error
expect(() => getBuilder().setNameLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error: Invalid localization
// @ts-expect-error
expect(() => getBuilder().setNameLocalizations({ 'en-U': 'foobar' })).toThrowError();
});

View File

@@ -1,15 +1,15 @@
import {
APIApplicationCommandAttachmentOption,
APIApplicationCommandBooleanOption,
APIApplicationCommandChannelOption,
APIApplicationCommandIntegerOption,
APIApplicationCommandMentionableOption,
APIApplicationCommandNumberOption,
APIApplicationCommandRoleOption,
APIApplicationCommandStringOption,
APIApplicationCommandUserOption,
ApplicationCommandOptionType,
ChannelType,
type APIApplicationCommandAttachmentOption,
type APIApplicationCommandBooleanOption,
type APIApplicationCommandChannelOption,
type APIApplicationCommandIntegerOption,
type APIApplicationCommandMentionableOption,
type APIApplicationCommandNumberOption,
type APIApplicationCommandRoleOption,
type APIApplicationCommandStringOption,
type APIApplicationCommandUserOption,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
@@ -22,7 +22,7 @@ import {
SlashCommandRoleOption,
SlashCommandStringOption,
SlashCommandUserOption,
} from '../../../src/index.js';
} from '../../../src/index';
const getBooleanOption = () =>
new SlashCommandBooleanOption().setName('owo').setDescription('Testing 123').setRequired(true);
@@ -101,8 +101,7 @@ describe('Application Command toJSON() results', () => {
max_value: 10,
min_value: -1,
autocomplete: true,
// TODO
// @ts-expect-error You *can* send an empty array with autocomplete: true, should correct that in types
// @ts-expect-error TODO: you *can* send an empty array with autocomplete: true, should correct that in types
choices: [],
});
@@ -146,8 +145,7 @@ describe('Application Command toJSON() results', () => {
max_value: 10,
min_value: -1.23,
autocomplete: true,
// TODO
// @ts-expect-error You *can* send an empty array with autocomplete: true, should correct that in types
// @ts-expect-error TODO: you *can* send an empty array with autocomplete: true, should correct that in types
choices: [],
});
@@ -189,8 +187,7 @@ describe('Application Command toJSON() results', () => {
type: ApplicationCommandOptionType.String,
required: true,
autocomplete: true,
// TODO
// @ts-expect-error you *can* send an empty array with autocomplete: true, should correct that in types
// @ts-expect-error TODO: you *can* send an empty array with autocomplete: true, should correct that in types
choices: [],
});

View File

@@ -1,4 +1,4 @@
import { ChannelType, PermissionFlagsBits, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import { APIApplicationCommandOptionChoice, ChannelType, PermissionFlagsBits } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
SlashCommandAssertions,
@@ -14,7 +14,7 @@ import {
SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder,
SlashCommandUserOption,
} from '../../../src/index.js';
} from '../../../src/index';
const largeArray = Array.from({ length: 26 }, () => 1 as unknown as APIApplicationCommandOptionChoice);
@@ -33,7 +33,9 @@ const getSubcommandGroup = () => new SlashCommandSubcommandGroupBuilder().setNam
const getSubcommand = () => new SlashCommandSubcommandBuilder().setName('owo').setDescription('Testing 123');
class Collection {
public readonly [Symbol.toStringTag] = 'Map';
public get [Symbol.toStringTag]() {
return 'Map';
}
}
describe('Slash Commands', () => {
@@ -176,7 +178,7 @@ describe('Slash Commands', () => {
});
test('GIVEN a builder with invalid autocomplete THEN does throw an error', () => {
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addStringOption(getStringOption().setAutocomplete('not a boolean'))).toThrowError();
});
@@ -234,7 +236,7 @@ describe('Slash Commands', () => {
expect(() => {
getBuilder().addChannelOption(
getChannelOption().addChannelTypes(ChannelType.GuildAnnouncement, ChannelType.GuildText),
getChannelOption().addChannelTypes(ChannelType.GuildNews, ChannelType.GuildText),
);
}).not.toThrowError();
});
@@ -246,16 +248,16 @@ describe('Slash Commands', () => {
});
test('GIVEN a builder with invalid number min/max options THEN does throw an error', () => {
// @ts-expect-error: Invalid max value
// @ts-expect-error
expect(() => getBuilder().addNumberOption(getNumberOption().setMaxValue('test'))).toThrowError();
// @ts-expect-error: Invalid max value
// @ts-expect-error
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMaxValue('test'))).toThrowError();
// @ts-expect-error: Invalid min value
// @ts-expect-error
expect(() => getBuilder().addNumberOption(getNumberOption().setMinValue('test'))).toThrowError();
// @ts-expect-error: Invalid min value
// @ts-expect-error
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMinValue('test'))).toThrowError();
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMinValue(1.5))).toThrowError();
@@ -292,10 +294,10 @@ describe('Slash Commands', () => {
});
test('GIVEN no valid return for an addOption method THEN throw error', () => {
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption()).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption(getRoleOption())).toThrowError();
});
@@ -316,18 +318,18 @@ describe('Slash Commands', () => {
});
test('GIVEN invalid returns for builder THEN throw error', () => {
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption(true)).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption(null)).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption(undefined)).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption(() => SlashCommandStringOption)).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addBooleanOption(() => new Collection())).toThrowError();
});
@@ -371,18 +373,6 @@ describe('Slash Commands', () => {
).not.toThrowError();
});
test('GIVEN builder with subcommand THEN has regular slash command fields', () => {
expect(() =>
getBuilder()
.setName('name')
.setDescription('description')
.addSubcommand((option) => option.setName('ye').setDescription('ye'))
.addSubcommand((option) => option.setName('no').setDescription('no'))
.setDMPermission(false)
.setDefaultMemberPermissions(1n),
).not.toThrowError();
});
test('GIVEN builder with already built subcommand group THEN does not throw error', () => {
expect(() => getNamedBuilder().addSubcommandGroup(getSubcommandGroup())).not.toThrowError();
});
@@ -399,29 +389,30 @@ describe('Slash Commands', () => {
test('GIVEN builder with a subcommand that tries to add an invalid result THEN throw error', () => {
expect(() =>
// @ts-expect-error: Checking if check works JS-side too
// @ts-expect-error Checking if check works JS-side too
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
getNamedBuilder().addSubcommand(getSubcommand()).addInteger(getInteger()),
).toThrowError();
});
test('GIVEN no valid return for an addSubcommand(Group) method THEN throw error', () => {
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addSubcommandGroup()).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addSubcommand()).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getBuilder().addSubcommand(getSubcommandGroup())).toThrowError();
});
});
describe('Subcommand group builder', () => {
test('GIVEN no valid subcommand THEN throw error', () => {
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getSubcommandGroup().addSubcommand()).toThrowError();
// @ts-expect-error: Checking if not providing anything, or an invalid return type causes an error
// @ts-expect-error Checking if not providing anything, or an invalid return type causes an error
expect(() => getSubcommandGroup().addSubcommand(getSubcommandGroup())).toThrowError();
});
@@ -453,9 +444,9 @@ describe('Slash Commands', () => {
});
test('GIVEN invalid name localizations THEN does throw error', () => {
// @ts-expect-error: Invalid localization
// @ts-expect-error
expect(() => getBuilder().setNameLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error: Invalid localization
// @ts-expect-error
expect(() => getBuilder().setNameLocalizations({ 'en-U': 'foobar' })).toThrowError();
});
@@ -476,9 +467,9 @@ describe('Slash Commands', () => {
});
test('GIVEN invalid description localizations THEN does throw error', () => {
// @ts-expect-error: Invalid localization description
// @ts-expect-error
expect(() => getBuilder().setDescriptionLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error: Invalid localization description
// @ts-expect-error
expect(() => getBuilder().setDescriptionLocalizations({ 'en-U': 'foobar' })).toThrowError();
});

View File

@@ -1,22 +1,22 @@
import {
APIModalInteractionResponseCallbackData,
APITextInputComponent,
ComponentType,
TextInputStyle,
type APIModalInteractionResponseCallbackData,
type APITextInputComponent,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
ActionRowBuilder,
ButtonBuilder,
ModalBuilder,
ModalActionRowComponentBuilder,
TextInputBuilder,
type ModalActionRowComponentBuilder,
} from '../../src/index.js';
} from '../../src';
import {
componentsValidator,
titleValidator,
validateRequiredParameters,
} from '../../src/interactions/modals/Assertions.js';
} from '../../src/interactions/modals/Assertions';
const modal = () => new ModalBuilder();
@@ -46,7 +46,7 @@ describe('Modals', () => {
test('GIVEN invalid required parameters THEN validator does throw', () => {
expect(() =>
// @ts-expect-error: Missing required parameter
// @ts-expect-error
validateRequiredParameters('123', undefined, [new ActionRowBuilder(), new ButtonBuilder()]),
).toThrowError();
});
@@ -66,7 +66,7 @@ describe('Modals', () => {
test('GIVEN invalid fields THEN builder does throw', () => {
expect(() => modal().setTitle('test').setCustomId('foobar').toJSON()).toThrowError();
// @ts-expect-error: CustomId is invalid
// @ts-expect-error
expect(() => modal().setTitle('test').setCustomId(42).toJSON()).toThrowError();
});

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest';
import { EmbedBuilder, embedLength } from '../../src/index.js';
import { EmbedBuilder, embedLength } from '../../src';
const alpha = 'abcdefghijklmnopqrstuvwxyz';
@@ -74,7 +74,7 @@ describe('Embed', () => {
test('GIVEN an embed with an invalid description THEN throws error', () => {
const embed = new EmbedBuilder();
expect(() => embed.setDescription('a'.repeat(4_097))).toThrowError();
expect(() => embed.setDescription('a'.repeat(4097))).toThrowError();
});
});
@@ -130,11 +130,11 @@ describe('Embed', () => {
test('GIVEN an embed with an invalid color THEN throws error', () => {
const embed = new EmbedBuilder();
// @ts-expect-error: Invalid color
// @ts-expect-error
expect(() => embed.setColor('RED')).toThrowError();
// @ts-expect-error: Invalid color
// @ts-expect-error
expect(() => embed.setColor([42, 36])).toThrowError();
expect(() => embed.setColor([42, 36, 1_000])).toThrowError();
expect(() => embed.setColor([42, 36, 1000])).toThrowError();
});
});
@@ -307,7 +307,7 @@ describe('Embed', () => {
test('GIVEN an embed with invalid footer text THEN throws error', () => {
const embed = new EmbedBuilder();
expect(() => embed.setFooter({ text: 'a'.repeat(2_049) })).toThrowError();
expect(() => embed.setFooter({ text: 'a'.repeat(2049) })).toThrowError();
});
});
@@ -411,7 +411,7 @@ describe('Embed', () => {
test('4', () => {
const embed = new EmbedBuilder();
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1_025) })).toThrowError();
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1025) })).toThrowError();
});
});
});

View File

@@ -1,11 +1,8 @@
/* eslint-disable no-template-curly-in-string */
import { URL } from 'node:url';
import { describe, test, expect, vitest } from 'vitest';
import {
chatInputApplicationCommandMention,
blockQuote,
bold,
channelLink,
channelMention,
codeBlock,
Faces,
@@ -14,7 +11,6 @@ import {
hyperlink,
inlineCode,
italic,
messageLink,
quote,
roleMention,
spoiler,
@@ -23,7 +19,7 @@ import {
TimestampStyles,
underscore,
userMention,
} from '../../src/index.js';
} from '../../src';
describe('Message formatters', () => {
describe('codeBlock', () => {
@@ -138,26 +134,6 @@ describe('Message formatters', () => {
expect(roleMention('815434166602170409')).toEqual('<@&815434166602170409>');
});
});
describe('chatInputApplicationCommandMention', () => {
test('GIVEN commandName and commandId THEN returns "</[commandName]:[commandId]>"', () => {
expect(chatInputApplicationCommandMention('airhorn', '815434166602170409')).toEqual(
'</airhorn:815434166602170409>',
);
});
test('GIVEN commandName, subcommandName, and commandId THEN returns "</[commandName] [subcommandName]:[commandId]>"', () => {
expect(chatInputApplicationCommandMention('airhorn', 'sub', '815434166602170409')).toEqual(
'</airhorn sub:815434166602170409>',
);
});
test('GIVEN commandName, subcommandGroupName, subcommandName, and commandId THEN returns "</[commandName] [subcommandGroupName] [subcommandName]:[commandId]>"', () => {
expect(chatInputApplicationCommandMention('airhorn', 'group', 'sub', '815434166602170409')).toEqual(
'</airhorn group sub:815434166602170409>',
);
});
});
});
describe('formatEmoji', () => {
@@ -174,38 +150,10 @@ describe('Message formatters', () => {
});
});
describe('channelLink', () => {
test('GIVEN channelId THEN returns "https://discord.com/channels/@me/${channelId}"', () => {
expect<'https://discord.com/channels/@me/123456789012345678'>(channelLink('123456789012345678')).toEqual(
'https://discord.com/channels/@me/123456789012345678',
);
});
test('GIVEN channelId WITH guildId THEN returns "https://discord.com/channels/${guildId}/${channelId}"', () => {
expect<'https://discord.com/channels/987654321987654/123456789012345678'>(
channelLink('123456789012345678', '987654321987654'),
).toEqual('https://discord.com/channels/987654321987654/123456789012345678');
});
});
describe('messageLink', () => {
test('GIVEN channelId AND messageId THEN returns "https://discord.com/channels/@me/${channelId}/${messageId}"', () => {
expect<'https://discord.com/channels/@me/123456789012345678/102938475657483'>(
messageLink('123456789012345678', '102938475657483'),
).toEqual('https://discord.com/channels/@me/123456789012345678/102938475657483');
});
test('GIVEN channelId AND messageId WITH guildId THEN returns "https://discord.com/channels/${guildId}/${channelId}/${messageId}"', () => {
expect<'https://discord.com/channels/987654321987654/123456789012345678/102938475657483'>(
messageLink('123456789012345678', '102938475657483', '987654321987654'),
).toEqual('https://discord.com/channels/987654321987654/123456789012345678/102938475657483');
});
});
describe('time', () => {
test('GIVEN no arguments THEN returns "<t:${bigint}>"', () => {
vitest.useFakeTimers();
vitest.setSystemTime(1_566_424_897_579);
vitest.setSystemTime(1566424897579);
expect<`<t:${bigint}>`>(time()).toEqual('<t:1566424897>');
@@ -213,29 +161,29 @@ describe('Message formatters', () => {
});
test('GIVEN a date THEN returns "<t:${bigint}>"', () => {
expect<`<t:${bigint}>`>(time(new Date(1_867_424_897_579))).toEqual('<t:1867424897>');
expect<`<t:${bigint}>`>(time(new Date(1867424897579))).toEqual('<t:1867424897>');
});
test('GIVEN a date and a style from string THEN returns "<t:${bigint}:${style}>"', () => {
expect<`<t:${bigint}:d>`>(time(new Date(1_867_424_897_579), 'd')).toEqual('<t:1867424897:d>');
expect<`<t:${bigint}:d>`>(time(new Date(1867424897579), 'd')).toEqual('<t:1867424897:d>');
});
test('GIVEN a date and a format from enum THEN returns "<t:${bigint}:${style}>"', () => {
expect<`<t:${bigint}:R>`>(time(new Date(1_867_424_897_579), TimestampStyles.RelativeTime)).toEqual(
expect<`<t:${bigint}:R>`>(time(new Date(1867424897579), TimestampStyles.RelativeTime)).toEqual(
'<t:1867424897:R>',
);
});
test('GIVEN a date THEN returns "<t:${time}>"', () => {
expect<'<t:1867424897>'>(time(1_867_424_897)).toEqual('<t:1867424897>');
expect<'<t:1867424897>'>(time(1867424897)).toEqual('<t:1867424897>');
});
test('GIVEN a date and a style from string THEN returns "<t:${time}:${style}>"', () => {
expect<'<t:1867424897:d>'>(time(1_867_424_897, 'd')).toEqual('<t:1867424897:d>');
expect<'<t:1867424897:d>'>(time(1867424897, 'd')).toEqual('<t:1867424897:d>');
});
test('GIVEN a date and a format from enum THEN returns "<t:${time}:${style}>"', () => {
expect<'<t:1867424897:R>'>(time(1_867_424_897, TimestampStyles.RelativeTime)).toEqual('<t:1867424897:R>');
expect<'<t:1867424897:R>'>(time(1867424897, TimestampStyles.RelativeTime)).toEqual('<t:1867424897:R>');
});
});

View File

@@ -6,7 +6,7 @@ import {
enableValidators,
disableValidators,
isValidationEnabled,
} from '../src/index.js';
} from '../src/index';
describe('isEquatable', () => {
test('returns true if the object is equatable', () => {

View File

@@ -0,0 +1,3 @@
import { createUnbuildConfig } from '../../build.config';
export default createUnbuildConfig();

View File

@@ -59,5 +59,5 @@ commit_parsers = [
filter_commits = true
tag_pattern = "@discordjs/builders@[0-9]*"
ignore_tags = ""
date_order = true
topo_order = false
sort_commits = "newest"

View File

@@ -1,24 +1,24 @@
{
"name": "@discordjs/builders",
"version": "1.2.0",
"version": "1.0.0",
"description": "A set of builders that you can use when creating your bot",
"scripts": {
"test": "vitest run",
"build": "tsup",
"lint": "prettier --check . && cross-env TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && cross-env TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format",
"docs": "downlevel-dts dist docs/dist --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn lint && yarn test && yarn build",
"changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/builders/*'",
"release": "cliff-jumper"
},
"main": "./dist/index.js",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"directories": {
@@ -33,7 +33,7 @@
"Crawl <icrawltogo@gmail.com>",
"Amish Shah <amishshah.2k@gmail.com>",
"SpaceEEC <spaceeec@yahoo.com>",
"Aura Román <kyradiscord@gmail.com>"
"Antonio Roman <kyradiscord@gmail.com>"
],
"license": "Apache-2.0",
"keywords": [
@@ -54,26 +54,26 @@
},
"homepage": "https://discord.js.org",
"dependencies": {
"@sapphire/shapeshift": "^3.6.0",
"discord-api-types": "^0.37.10",
"@sapphire/shapeshift": "^3.5.1",
"discord-api-types": "^0.36.3",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.1",
"tslib": "^2.4.0"
},
"devDependencies": {
"@discordjs/docgen": "workspace:^",
"@favware/cliff-jumper": "^1.8.7",
"@microsoft/api-extractor": "^7.31.2",
"@types/node": "^16.11.60",
"@vitest/coverage-c8": "^0.23.4",
"cross-env": "^7.0.3",
"downlevel-dts": "^0.10.1",
"eslint": "^8.24.0",
"eslint-config-neon": "^0.1.33",
"@favware/cliff-jumper": "^1.8.5",
"@microsoft/api-extractor": "^7.28.6",
"@types/node": "^16.11.45",
"c8": "^7.12.0",
"downlevel-dts": "^0.10.0",
"eslint": "^8.20.0",
"prettier": "^2.7.1",
"tsup": "^6.2.3",
"typescript": "^4.8.3",
"vitest": "^0.23.4"
"rollup-plugin-typescript2": "0.32.1",
"tsup": "^6.2.0",
"typescript": "^4.7.4",
"unbuild": "^0.7.6",
"vitest": "^0.19.1"
},
"engines": {
"node": ">=16.9.0"

View File

@@ -1,31 +1,27 @@
/* eslint-disable jsdoc/check-param-names */
import {
type APIActionRowComponent,
ComponentType,
type APIMessageActionRowComponent,
type APIModalActionRowComponent,
type APIActionRowComponentTypes,
APIMessageActionRowComponent,
APIModalActionRowComponent,
APIActionRowComponentTypes,
} from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
import { ComponentBuilder } from './Component.js';
import { createComponentBuilder } from './Components.js';
import type { ButtonBuilder } from './button/Button.js';
import type { SelectMenuBuilder } from './selectMenu/SelectMenu.js';
import type { TextInputBuilder } from './textInput/TextInput.js';
import { ComponentBuilder } from './Component';
import { createComponentBuilder } from './Components';
import type { ButtonBuilder } from './button/Button';
import type { SelectMenuBuilder } from './selectMenu/SelectMenu';
import type { TextInputBuilder } from './textInput/TextInput';
import { normalizeArray, type RestOrArray } from '../util/normalizeArray';
export type MessageComponentBuilder =
| ActionRowBuilder<MessageActionRowComponentBuilder>
| MessageActionRowComponentBuilder;
export type ModalComponentBuilder = ActionRowBuilder<ModalActionRowComponentBuilder> | ModalActionRowComponentBuilder;
| MessageActionRowComponentBuilder
| ActionRowBuilder<MessageActionRowComponentBuilder>;
export type ModalComponentBuilder = ModalActionRowComponentBuilder | ActionRowBuilder<ModalActionRowComponentBuilder>;
export type MessageActionRowComponentBuilder = ButtonBuilder | SelectMenuBuilder;
export type ModalActionRowComponentBuilder = TextInputBuilder;
export type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder;
/**
* Represents an action row component
*
* @typeParam T - The types of components this action row holds
*/
export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBuilder<
APIActionRowComponent<APIMessageActionRowComponent | APIModalActionRowComponent>
@@ -35,43 +31,9 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
*/
public readonly components: T[];
/**
* Creates a new action row from API data
*
* @param data - The API data to create this action row with
* @example
* Creating an action row from an API data object
* ```ts
* const actionRow = new ActionRowBuilder({
* components: [
* {
* custom_id: "custom id",
* label: "Type something",
* style: TextInputStyle.Short,
* type: ComponentType.TextInput,
* },
* ],
* });
* ```
* @example
* Creating an action row using setters and API data
* ```ts
* const actionRow = new ActionRowBuilder({
* components: [
* {
* custom_id: "custom id",
* label: "Click me",
* style: ButtonStyle.Primary,
* type: ComponentType.Button,
* },
* ],
* })
* .addComponents(button2, button3);
* ```
*/
public constructor({ components, ...data }: Partial<APIActionRowComponent<APIActionRowComponentTypes>> = {}) {
super({ type: ComponentType.ActionRow, ...data });
this.components = (components?.map((component) => createComponentBuilder(component)) ?? []) as T[];
this.components = (components?.map((c) => createComponentBuilder(c)) ?? []) as T[];
}
/**
@@ -94,10 +56,8 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIActionRowComponent<ReturnType<T['toJSON']>> {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
components: this.components.map((component) => component.toJSON()),

View File

@@ -1,7 +1,7 @@
import { s } from '@sapphire/shapeshift';
import { ButtonStyle, type APIMessageComponentEmoji } from 'discord-api-types/v10';
import { isValidationEnabled } from '../util/validation.js';
import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption.js';
import { APIMessageComponentEmoji, ButtonStyle } from 'discord-api-types/v10';
import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption';
import { isValidationEnabled } from '../util/validation';
export const customIdValidator = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -6,12 +6,10 @@ import type {
} from 'discord-api-types/v10';
import type { JSONEncodable } from '../util/jsonEncodable';
export type AnyAPIActionRowComponent = APIActionRowComponent<APIActionRowComponentTypes> | APIActionRowComponentTypes;
export type AnyAPIActionRowComponent = APIActionRowComponentTypes | APIActionRowComponent<APIActionRowComponentTypes>;
/**
* Represents a discord component
*
* @typeParam DataType - The type of internal API data that is stored within the component
*/
export abstract class ComponentBuilder<
DataType extends Partial<APIBaseComponent<ComponentType>> = APIBaseComponent<ComponentType>,
@@ -22,13 +20,6 @@ export abstract class ComponentBuilder<
*/
public readonly data: Partial<DataType>;
/**
* Serializes this component to an API-compatible JSON object
*
* @remarks
* This method runs validations on the data before serializing it.
* As such, it may throw an error if the data is invalid.
*/
public abstract toJSON(): AnyAPIActionRowComponent;
public constructor(data: Partial<DataType>) {

View File

@@ -1,14 +1,14 @@
import { ComponentType, type APIMessageComponent, type APIModalComponent } from 'discord-api-types/v10';
import { APIMessageComponent, APIModalComponent, ComponentType } from 'discord-api-types/v10';
import {
ActionRowBuilder,
type AnyComponentBuilder,
type MessageComponentBuilder,
type ModalComponentBuilder,
} from './ActionRow.js';
import { ComponentBuilder } from './Component.js';
import { ButtonBuilder } from './button/Button.js';
import { SelectMenuBuilder } from './selectMenu/SelectMenu.js';
import { TextInputBuilder } from './textInput/TextInput.js';
} from './ActionRow';
import { ComponentBuilder } from './Component';
import { ButtonBuilder } from './button/Button';
import { SelectMenuBuilder } from './selectMenu/SelectMenu';
import { TextInputBuilder } from './textInput/TextInput';
export interface MappedComponentTypes {
[ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>;
@@ -23,8 +23,7 @@ export interface MappedComponentTypes {
* @param data - The api data to transform to a component class
*/
export function createComponentBuilder<T extends keyof MappedComponentTypes>(
// eslint-disable-next-line @typescript-eslint/sort-type-union-intersection-members
data: (APIModalComponent | APIMessageComponent) & { type: T },
data: (APIMessageComponent | APIModalComponent) & { type: T },
): MappedComponentTypes[T];
export function createComponentBuilder<C extends MessageComponentBuilder | ModalComponentBuilder>(data: C): C;
export function createComponentBuilder(
@@ -44,7 +43,8 @@ export function createComponentBuilder(
case ComponentType.TextInput:
return new TextInputBuilder(data);
default:
// @ts-expect-error: This case can still occur if we get a newer unsupported component type
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Cannot properly serialize component type: ${data.type}`);
}
}

View File

@@ -1,10 +1,10 @@
import {
ComponentType,
ButtonStyle,
type APIMessageComponentEmoji,
type APIButtonComponent,
type APIButtonComponentWithURL,
type APIButtonComponentWithCustomId,
type ButtonStyle,
} from 'discord-api-types/v10';
import {
buttonLabelValidator,
@@ -14,41 +14,13 @@ import {
emojiValidator,
urlValidator,
validateRequiredButtonParameters,
} from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
} from '../Assertions';
import { ComponentBuilder } from '../Component';
/**
* Represents a button component
*/
export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Creates a new button from API data
*
* @param data - The API data to create this button with
* @example
* Creating a button from an API data object
* ```ts
* const button = new ButtonBuilder({
* custom_id: 'a cool button',
* style: ButtonStyle.Primary,
* label: 'Click Me',
* emoji: {
* name: 'smile',
* id: '123456789012345678',
* },
* });
* ```
* @example
* Creating a button using setters and API data
* ```ts
* const button = new ButtonBuilder({
* style: ButtonStyle.Secondary,
* label: 'Click Me',
* })
* .setEmoji({ name: '🙂' })
* .setCustomId('another cool button');
* ```
*/
public constructor(data?: Partial<APIButtonComponent>) {
super({ type: ComponentType.Button, ...data });
}
@@ -66,9 +38,6 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Sets the URL for this button
*
* @remarks
* This method is only available to buttons using the `Link` button style.
* Only three types of URL schemes are currently supported: `https://`, `http://` and `discord://`
* @param url - The URL to open when this button is clicked
*/
public setURL(url: string) {
@@ -79,8 +48,6 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Sets the custom id for this button
*
* @remarks
* This method is only applicable to buttons that are not using the `Link` button style.
* @param customId - The custom id to use for this button
*/
public setCustomId(customId: string) {
@@ -118,9 +85,6 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIButtonComponent {
validateRequiredButtonParameters(
this.data.style,
@@ -129,7 +93,7 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
(this.data as APIButtonComponentWithCustomId).custom_id,
(this.data as APIButtonComponentWithURL).url,
);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
} as APIButtonComponent;

View File

@@ -1,5 +1,6 @@
import { ComponentType, type APISelectMenuComponent, type APISelectMenuOption } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { APISelectMenuOption, ComponentType, type APISelectMenuComponent } from 'discord-api-types/v10';
import { SelectMenuOptionBuilder } from './SelectMenuOption';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
import {
customIdValidator,
disabledValidator,
@@ -8,9 +9,8 @@ import {
optionsLengthValidator,
placeholderValidator,
validateRequiredSelectMenuParameters,
} from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
import { SelectMenuOptionBuilder } from './SelectMenuOption.js';
} from '../Assertions';
import { ComponentBuilder } from '../Component';
/**
* Represents a select menu component
@@ -21,41 +21,10 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
*/
public readonly options: SelectMenuOptionBuilder[];
/**
* Creates a new select menu from API data
*
* @param data - The API data to create this select menu with
* @example
* Creating a select menu from an API data object
* ```ts
* const selectMenu = new SelectMenuBuilder({
* custom_id: 'a cool select menu',
* placeholder: 'select an option',
* max_values: 2,
* options: [
* { label: 'option 1', value: '1' },
* { label: 'option 2', value: '2' },
* { label: 'option 3', value: '3' },
* ],
* });
* ```
* @example
* Creating a select menu using setters and API data
* ```ts
* const selectMenu = new SelectMenuBuilder({
* custom_id: 'a cool select menu',
* })
* .setMinValues(1)
* .addOptions({
* label: 'Catchy',
* value: 'catch',
* });
* ```
*/
public constructor(data?: Partial<APISelectMenuComponent>) {
const { options, ...initData } = data ?? {};
super({ type: ComponentType.SelectMenu, ...initData });
this.options = options?.map((option) => new SelectMenuOptionBuilder(option)) ?? [];
this.options = options?.map((o) => new SelectMenuOptionBuilder(o)) ?? [];
}
/**
@@ -114,8 +83,7 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
* @param options - The options to add to this select menu
* @returns
*/
public addOptions(...options: RestOrArray<APISelectMenuOption | SelectMenuOptionBuilder>) {
// eslint-disable-next-line no-param-reassign
public addOptions(...options: RestOrArray<SelectMenuOptionBuilder | APISelectMenuOption>) {
options = normalizeArray(options);
optionsLengthValidator.parse(this.options.length + options.length);
this.options.push(
@@ -133,8 +101,7 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
*
* @param options - The options to set on this select menu
*/
public setOptions(...options: RestOrArray<APISelectMenuOption | SelectMenuOptionBuilder>) {
// eslint-disable-next-line no-param-reassign
public setOptions(...options: RestOrArray<SelectMenuOptionBuilder | APISelectMenuOption>) {
options = normalizeArray(options);
optionsLengthValidator.parse(options.length);
this.options.splice(
@@ -149,15 +116,12 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APISelectMenuComponent {
validateRequiredSelectMenuParameters(this.options, this.data.custom_id);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
options: this.options.map((option) => option.toJSON()),
options: this.options.map((o) => o.toJSON()),
} as APISelectMenuComponent;
}
}

View File

@@ -1,38 +1,16 @@
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
import type { JSONEncodable } from '../../util/jsonEncodable.js';
import {
defaultValidator,
emojiValidator,
labelValueDescriptionValidator,
validateRequiredSelectMenuOptionParameters,
} from '../Assertions.js';
} from '../Assertions';
/**
* Represents a option within a select menu component
*/
export class SelectMenuOptionBuilder implements JSONEncodable<APISelectMenuOption> {
/**
* Creates a new select menu option from API data
*
* @param data - The API data to create this select menu option with
* @example
* Creating a select menu option from an API data object
* ```ts
* const selectMenuOption = new SelectMenuOptionBuilder({
* label: 'catchy label',
* value: '1',
* });
* ```
* @example
* Creating a select menu option using setters and API data
* ```ts
* const selectMenuOption = new SelectMenuOptionBuilder({
* default: true,
* value: '1',
* })
* .setLabel('woah')
* ```
*/
export class SelectMenuOptionBuilder {
public constructor(public data: Partial<APISelectMenuOption> = {}) {}
/**
@@ -85,12 +63,9 @@ export class SelectMenuOptionBuilder implements JSONEncodable<APISelectMenuOptio
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APISelectMenuOption {
validateRequiredSelectMenuOptionParameters(this.data.label, this.data.value);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
} as APISelectMenuOption;

View File

@@ -1,19 +1,19 @@
import { s } from '@sapphire/shapeshift';
import { TextInputStyle } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { customIdValidator } from '../Assertions.js';
import { isValidationEnabled } from '../../util/validation';
import { customIdValidator } from '../Assertions';
export const textInputStyleValidator = s.nativeEnum(TextInputStyle);
export const minLengthValidator = s.number.int
.greaterThanOrEqual(0)
.lessThanOrEqual(4_000)
.lessThanOrEqual(4000)
.setValidationEnabled(isValidationEnabled);
export const maxLengthValidator = s.number.int
.greaterThanOrEqual(1)
.lessThanOrEqual(4_000)
.lessThanOrEqual(4000)
.setValidationEnabled(isValidationEnabled);
export const requiredValidator = s.boolean;
export const valueValidator = s.string.lengthLessThanOrEqual(4_000).setValidationEnabled(isValidationEnabled);
export const valueValidator = s.string.lengthLessThanOrEqual(4000).setValidationEnabled(isValidationEnabled);
export const placeholderValidator = s.string.lengthLessThanOrEqual(100).setValidationEnabled(isValidationEnabled);
export const labelValidator = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -1,9 +1,5 @@
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import isEqual from 'fast-deep-equal';
import type { Equatable } from '../../util/equatable';
import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable.js';
import { customIdValidator } from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
import {
maxLengthValidator,
minLengthValidator,
@@ -13,35 +9,12 @@ import {
validateRequiredParameters,
labelValidator,
textInputStyleValidator,
} from './Assertions.js';
} from './Assertions';
import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable';
import { customIdValidator } from '../Assertions';
import { ComponentBuilder } from '../Component';
export class TextInputBuilder
extends ComponentBuilder<APITextInputComponent>
implements Equatable<APITextInputComponent | JSONEncodable<APITextInputComponent>>
{
/**
* Creates a new text input from API data
*
* @param data - The API data to create this text input with
* @example
* Creating a select menu option from an API data object
* ```ts
* const textInput = new TextInputBuilder({
* custom_id: 'a cool select menu',
* label: 'Type something',
* style: TextInputStyle.Short,
* });
* ```
* @example
* Creating a select menu option using setters and API data
* ```ts
* const textInput = new TextInputBuilder({
* label: 'Type something else',
* })
* .setCustomId('woah')
* .setStyle(TextInputStyle.Paragraph);
* ```
*/
export class TextInputBuilder extends ComponentBuilder<APITextInputComponent> {
public constructor(data?: APITextInputComponent & { type?: ComponentType.TextInput }) {
super({ type: ComponentType.TextInput, ...data });
}
@@ -126,21 +99,15 @@ export class TextInputBuilder
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APITextInputComponent {
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
} as APITextInputComponent;
}
/**
* {@inheritDoc Equatable.equals}
*/
public equals(other: APITextInputComponent | JSONEncodable<APITextInputComponent>): boolean {
public equals(other: JSONEncodable<APITextInputComponent> | APITextInputComponent): boolean {
if (isJSONEncodable(other)) {
return isEqual(other.toJSON(), this.data);
}

View File

@@ -1,43 +1,43 @@
export * as EmbedAssertions from './messages/embed/Assertions.js';
export * from './messages/embed/Embed.js';
export * from './messages/formatters.js';
export * as EmbedAssertions from './messages/embed/Assertions';
export * from './messages/embed/Embed';
export * from './messages/formatters';
export * as ComponentAssertions from './components/Assertions.js';
export * from './components/ActionRow.js';
export * from './components/button/Button.js';
export * from './components/Component.js';
export * from './components/Components.js';
export * from './components/textInput/TextInput.js';
export * as TextInputAssertions from './components/textInput/Assertions.js';
export * from './interactions/modals/Modal.js';
export * as ModalAssertions from './interactions/modals/Assertions.js';
export * from './components/selectMenu/SelectMenu.js';
export * from './components/selectMenu/SelectMenuOption.js';
export * as ComponentAssertions from './components/Assertions';
export * from './components/ActionRow';
export * from './components/button/Button';
export * from './components/Component';
export * from './components/Components';
export * from './components/textInput/TextInput';
export * as TextInputAssertions from './components/textInput/Assertions';
export * from './interactions/modals/Modal';
export * as ModalAssertions from './interactions/modals/Assertions';
export * from './components/selectMenu/SelectMenu';
export * from './components/selectMenu/SelectMenuOption';
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions.js';
export * from './interactions/slashCommands/SlashCommandBuilder.js';
export * from './interactions/slashCommands/SlashCommandSubcommands.js';
export * from './interactions/slashCommands/options/boolean.js';
export * from './interactions/slashCommands/options/channel.js';
export * from './interactions/slashCommands/options/integer.js';
export * from './interactions/slashCommands/options/mentionable.js';
export * from './interactions/slashCommands/options/number.js';
export * from './interactions/slashCommands/options/role.js';
export * from './interactions/slashCommands/options/attachment.js';
export * from './interactions/slashCommands/options/string.js';
export * from './interactions/slashCommands/options/user.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionBase.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
export * from './interactions/slashCommands/mixins/NameAndDescription.js';
export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions.js';
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions';
export * from './interactions/slashCommands/SlashCommandBuilder';
export * from './interactions/slashCommands/SlashCommandSubcommands';
export * from './interactions/slashCommands/options/boolean';
export * from './interactions/slashCommands/options/channel';
export * from './interactions/slashCommands/options/integer';
export * from './interactions/slashCommands/options/mentionable';
export * from './interactions/slashCommands/options/number';
export * from './interactions/slashCommands/options/role';
export * from './interactions/slashCommands/options/attachment';
export * from './interactions/slashCommands/options/string';
export * from './interactions/slashCommands/options/user';
export * from './interactions/slashCommands/mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionBase';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
export * from './interactions/slashCommands/mixins/NameAndDescription';
export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions';
export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions.js';
export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder.js';
export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions';
export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder';
export * from './util/jsonEncodable.js';
export * from './util/equatable.js';
export * from './util/componentUtil.js';
export * from './util/normalizeArray.js';
export * from './util/validation.js';
export * from './util/jsonEncodable';
export * from './util/equatable';
export * from './util/componentUtil';
export * from './util/normalizeArray';
export * from './util/validation';

View File

@@ -1,12 +1,11 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder.js';
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder';
import { isValidationEnabled } from '../../util/validation';
const namePredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(32)
// eslint-disable-next-line prefer-named-capture-group, unicorn/no-unsafe-regex
.regex(/^( *[\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+ *)+$/u)
.setValidationEnabled(isValidationEnabled);
const typePredicate = s

View File

@@ -3,9 +3,8 @@ import type {
LocaleString,
LocalizationMap,
Permissions,
RESTPostAPIContextMenuApplicationCommandsJSONBody,
RESTPostAPIApplicationCommandsJSONBody,
} from 'discord-api-types/v10';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions.js';
import {
validateRequiredParameters,
validateName,
@@ -13,7 +12,8 @@ import {
validateDefaultPermission,
validateDefaultMemberPermissions,
validateDMPermission,
} from './Assertions.js';
} from './Assertions';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions';
export class ContextMenuCommandBuilder {
/**
@@ -35,7 +35,7 @@ export class ContextMenuCommandBuilder {
* Whether the command is enabled by default when the app is added to a guild
*
* @deprecated This property is deprecated and will be removed in the future.
* You should use {@link ContextMenuCommandBuilder.setDefaultMemberPermissions} or {@link ContextMenuCommandBuilder.setDMPermission} instead.
* You should use `setDefaultMemberPermissions` or `setDMPermission` instead.
*/
public readonly default_permission: boolean | undefined = undefined;
@@ -81,11 +81,12 @@ export class ContextMenuCommandBuilder {
/**
* 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.
* **Note**: 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 https://discord.com/developers/docs/interactions/application-commands#permissions
* @deprecated Use {@link ContextMenuCommandBuilder.setDefaultMemberPermissions} or {@link ContextMenuCommandBuilder.setDMPermission} instead.
* @deprecated Use `setDefaultMemberPermissions` or `setDMPermission` instead.
*/
public setDefaultPermission(value: boolean) {
// Assert the value matches the conditions
@@ -99,9 +100,10 @@ export class ContextMenuCommandBuilder {
/**
* 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.
* **Note:** You can set this to `'0'` to disable the command by default.
*
* @param permissions - The permissions bit field to set
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
@@ -118,6 +120,7 @@ export class ContextMenuCommandBuilder {
* By default, commands are visible.
*
* @param enabled - If the command should be enabled in DMs
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDMPermission(enabled: boolean | null | undefined) {
@@ -166,19 +169,18 @@ export class ContextMenuCommandBuilder {
Reflect.set(this, 'name_localizations', {});
for (const args of Object.entries(localizedNames))
this.setNameLocalization(...(args as [LocaleString, string | null]));
Object.entries(localizedNames).forEach((args) =>
this.setNameLocalization(...(args as [LocaleString, string | null])),
);
return this;
}
/**
* Returns the final data that should be sent to Discord.
*
* @remarks
* This method runs validations on the data before serializing it.
* As such, it may throw an error if the data is invalid.
* **Note:** Calling this function will validate required properties based on their conditions.
*/
public toJSON(): RESTPostAPIContextMenuApplicationCommandsJSONBody {
public toJSON(): RESTPostAPIApplicationCommandsJSONBody {
validateRequiredParameters(this.name, this.type);
validateLocalizationMap(this.name_localizations);
@@ -187,4 +189,4 @@ export class ContextMenuCommandBuilder {
}
}
export type ContextMenuCommandType = ApplicationCommandType.Message | ApplicationCommandType.User;
export type ContextMenuCommandType = ApplicationCommandType.User | ApplicationCommandType.Message;

View File

@@ -1,7 +1,7 @@
import { s } from '@sapphire/shapeshift';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { isValidationEnabled } from '../../util/validation.js';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow';
import { customIdValidator } from '../../components/Assertions';
import { isValidationEnabled } from '../../util/validation';
export const titleValidator = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -3,21 +3,20 @@ import type {
APIModalActionRowComponent,
APIModalInteractionResponseCallbackData,
} from 'discord-api-types/v10';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { createComponentBuilder } from '../../components/Components.js';
import { titleValidator, validateRequiredParameters } from './Assertions';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow';
import { customIdValidator } from '../../components/Assertions';
import { createComponentBuilder } from '../../components/Components';
import type { JSONEncodable } from '../../util/jsonEncodable';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { titleValidator, validateRequiredParameters } from './Assertions.js';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
public readonly data: Partial<APIModalInteractionResponseCallbackData>;
public readonly components: ActionRowBuilder<ModalActionRowComponentBuilder>[] = [];
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
this.data = { ...data };
this.components = (components?.map((component) => createComponentBuilder(component)) ??
this.components = (components?.map((c) => createComponentBuilder(c)) ??
[]) as ActionRowBuilder<ModalActionRowComponentBuilder>[];
}
@@ -71,12 +70,9 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIModalInteractionResponseCallbackData {
validateRequiredParameters(this.data.custom_id, this.data.title, this.components);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
components: this.components.map((component) => component.toJSON()),

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { 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';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase.js';
import { type APIApplicationCommandOptionChoice, Locale, LocalizationMap } from 'discord-api-types/v10';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import { isValidationEnabled } from '../../util/validation';
const namePredicate = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -2,7 +2,7 @@ import type {
APIApplicationCommandOption,
LocalizationMap,
Permissions,
RESTPostAPIChatInputApplicationCommandsJSONBody,
RESTPostAPIApplicationCommandsJSONBody,
} from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import {
@@ -13,10 +13,10 @@ import {
validateDMPermission,
validateMaxOptionsLength,
validateRequiredParameters,
} from './Assertions.js';
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands.js';
import { SharedNameAndDescription } from './mixins/NameAndDescription.js';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions.js';
} from './Assertions';
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
import { SharedNameAndDescription } from './mixins/NameAndDescription';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions';
@mix(SharedSlashCommandOptions, SharedNameAndDescription)
export class SlashCommandBuilder {
@@ -49,7 +49,7 @@ export class SlashCommandBuilder {
* Whether the command is enabled by default when the app is added to a guild
*
* @deprecated This property is deprecated and will be removed in the future.
* You should use {@link (SlashCommandBuilder:class).setDefaultMemberPermissions} or {@link (SlashCommandBuilder:class).setDMPermission} instead.
* You should use `setDefaultMemberPermissions` or `setDMPermission` instead.
*/
public readonly default_permission: boolean | undefined = undefined;
@@ -67,11 +67,9 @@ export class SlashCommandBuilder {
/**
* Returns the final data that should be sent to Discord.
*
* @remarks
* This method runs validations on the data before serializing it.
* As such, it may throw an error if the data is invalid.
* **Note:** Calling this function will validate required properties based on their conditions.
*/
public toJSON(): RESTPostAPIChatInputApplicationCommandsJSONBody {
public toJSON(): RESTPostAPIApplicationCommandsJSONBody {
validateRequiredParameters(this.name, this.description, this.options);
validateLocalizationMap(this.name_localizations);
@@ -86,11 +84,12 @@ export class SlashCommandBuilder {
/**
* 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.
* **Note**: 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 https://discord.com/developers/docs/interactions/application-commands#permissions
* @deprecated Use {@link (SlashCommandBuilder:class).setDefaultMemberPermissions} or {@link (SlashCommandBuilder:class).setDMPermission} instead.
* @deprecated Use `setDefaultMemberPermissions` or `setDMPermission` instead.
*/
public setDefaultPermission(value: boolean) {
// Assert the value matches the conditions
@@ -104,9 +103,10 @@ export class SlashCommandBuilder {
/**
* 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.
* **Note:** You can set this to `'0'` to disable the command by default.
*
* @param permissions - The permissions bit field to set
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
@@ -123,6 +123,7 @@ export class SlashCommandBuilder {
* By default, commands are visible.
*
* @param enabled - If the command should be enabled in DMs
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDMPermission(enabled: boolean | null | undefined) {
@@ -190,7 +191,8 @@ export class SlashCommandBuilder {
export interface SlashCommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions {}
export interface SlashCommandSubcommandsOnlyBuilder
extends Omit<SlashCommandBuilder, Exclude<keyof SharedSlashCommandOptions, 'options'>> {}
extends SharedNameAndDescription,
Pick<SlashCommandBuilder, 'toJSON' | 'addSubcommand' | 'addSubcommandGroup'> {}
export interface SlashCommandOptionsOnlyBuilder
extends SharedNameAndDescription,
@@ -198,5 +200,5 @@ export interface SlashCommandOptionsOnlyBuilder
Pick<SlashCommandBuilder, 'toJSON'> {}
export interface ToAPIApplicationCommandOptions {
toJSON(): APIApplicationCommandOption;
toJSON: () => APIApplicationCommandOption;
}

View File

@@ -1,14 +1,14 @@
import {
APIApplicationCommandSubcommandGroupOption,
APIApplicationCommandSubcommandOption,
ApplicationCommandOptionType,
type APIApplicationCommandSubcommandGroupOption,
type APIApplicationCommandSubcommandOption,
} from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions.js';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder.js';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase.js';
import { SharedNameAndDescription } from './mixins/NameAndDescription.js';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions.js';
import { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import { SharedNameAndDescription } from './mixins/NameAndDescription';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions';
/**
* Represents a folder for subcommands

View File

@@ -1,6 +1,5 @@
export abstract class ApplicationCommandNumericOptionMinMaxValueMixin {
public readonly max_value?: number;
public readonly min_value?: number;
/**

View File

@@ -1,6 +1,6 @@
import type { APIApplicationCommandBasicOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { validateRequiredParameters, validateRequired, validateLocalizationMap } from '../Assertions.js';
import { SharedNameAndDescription } from './NameAndDescription.js';
import { SharedNameAndDescription } from './NameAndDescription';
import { validateRequiredParameters, validateRequired, validateLocalizationMap } from '../Assertions';
export abstract class ApplicationCommandOptionBase extends SharedNameAndDescription {
public abstract readonly type: ApplicationCommandOptionType;

View File

@@ -6,12 +6,11 @@ const allowedChannelTypes = [
ChannelType.GuildText,
ChannelType.GuildVoice,
ChannelType.GuildCategory,
ChannelType.GuildAnnouncement,
ChannelType.AnnouncementThread,
ChannelType.PublicThread,
ChannelType.PrivateThread,
ChannelType.GuildNews,
ChannelType.GuildNewsThread,
ChannelType.GuildPublicThread,
ChannelType.GuildPrivateThread,
ChannelType.GuildStageVoice,
ChannelType.GuildForum,
] as const;
export type ApplicationCommandOptionAllowedChannelTypes = typeof allowedChannelTypes[number];

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import { localizationMapPredicate, validateChoicesLength } from '../Assertions.js';
import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { localizationMapPredicate, validateChoicesLength } from '../Assertions';
const stringPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
const numberPredicate = s.number.greaterThan(Number.NEGATIVE_INFINITY).lessThan(Number.POSITIVE_INFINITY);
const numberPredicate = s.number.greaterThan(-Infinity).lessThan(Infinity);
const choicesPredicate = s.object({
name: stringPredicate,
name_localizations: localizationMapPredicate,
@@ -11,9 +11,8 @@ const choicesPredicate = s.object({
}).array;
const booleanPredicate = s.boolean;
export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends number | string> {
export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends string | number> {
public readonly choices?: APIApplicationCommandOptionChoice<T>[];
public readonly autocomplete?: boolean;
// Since this is present and this is a mixin, this is needed
@@ -66,7 +65,6 @@ export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends n
/**
* Marks the option as autocompletable
*
* @param autocomplete - If this option should be autocompletable
*/
public setAutocomplete(autocomplete: boolean): this {

View File

@@ -1,13 +1,10 @@
import type { LocaleString, LocalizationMap } from 'discord-api-types/v10';
import { validateDescription, validateLocale, validateName } from '../Assertions.js';
import { validateDescription, validateLocale, validateName } from '../Assertions';
export class SharedNameAndDescription {
public readonly name!: string;
public readonly name_localizations?: LocalizationMap;
public readonly description!: string;
public readonly description_localizations?: LocalizationMap;
/**
@@ -75,10 +72,9 @@ export class SharedNameAndDescription {
Reflect.set(this, 'name_localizations', {});
for (const args of Object.entries(localizedNames)) {
this.setNameLocalization(...(args as [LocaleString, string | null]));
}
Object.entries(localizedNames).forEach((args) =>
this.setNameLocalization(...(args as [LocaleString, string | null])),
);
return this;
}
@@ -118,10 +114,9 @@ export class SharedNameAndDescription {
}
Reflect.set(this, 'description_localizations', {});
for (const args of Object.entries(localizedDescriptions)) {
this.setDescriptionLocalization(...(args as [LocaleString, string | null]));
}
Object.entries(localizedDescriptions).forEach((args) =>
this.setDescriptionLocalization(...(args as [LocaleString, string | null])),
);
return this;
}
}

View File

@@ -1,15 +1,15 @@
import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions.js';
import type { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase';
import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions';
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';
import { SlashCommandAttachmentOption } from '../options/attachment.js';
import { SlashCommandBooleanOption } from '../options/boolean.js';
import { SlashCommandChannelOption } from '../options/channel.js';
import { SlashCommandIntegerOption } from '../options/integer.js';
import { SlashCommandMentionableOption } from '../options/mentionable.js';
import { SlashCommandNumberOption } from '../options/number.js';
import { SlashCommandRoleOption } from '../options/role.js';
import { SlashCommandStringOption } from '../options/string.js';
import { SlashCommandUserOption } from '../options/user.js';
import type { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase.js';
import { SlashCommandAttachmentOption } from '../options/attachment';
import { SlashCommandBooleanOption } from '../options/boolean';
import { SlashCommandChannelOption } from '../options/channel';
import { SlashCommandIntegerOption } from '../options/integer';
import { SlashCommandMentionableOption } from '../options/mentionable';
import { SlashCommandNumberOption } from '../options/number';
import { SlashCommandRoleOption } from '../options/role';
import { SlashCommandStringOption } from '../options/string';
import { SlashCommandUserOption } from '../options/user';
export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
public readonly options!: ToAPIApplicationCommandOptions[];
@@ -83,15 +83,15 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
*/
public addStringOption(
input:
| Omit<SlashCommandStringOption, 'addChoices'>
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoices'>
| ((
builder: SlashCommandStringOption,
) =>
| Omit<SlashCommandStringOption, 'addChoices'>
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| SlashCommandStringOption),
| Omit<SlashCommandStringOption, 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandStringOption);
}
@@ -103,15 +103,15 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
*/
public addIntegerOption(
input:
| Omit<SlashCommandIntegerOption, 'addChoices'>
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoices'>
| ((
builder: SlashCommandIntegerOption,
) =>
| Omit<SlashCommandIntegerOption, 'addChoices'>
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| SlashCommandIntegerOption),
| Omit<SlashCommandIntegerOption, 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandIntegerOption);
}
@@ -123,25 +123,25 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
*/
public addNumberOption(
input:
| Omit<SlashCommandNumberOption, 'addChoices'>
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoices'>
| ((
builder: SlashCommandNumberOption,
) =>
| Omit<SlashCommandNumberOption, 'addChoices'>
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| SlashCommandNumberOption),
| Omit<SlashCommandNumberOption, 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandNumberOption);
}
private _sharedAddOptionMethod<T extends ApplicationCommandOptionBase>(
input:
| Omit<T, 'addChoices'>
| Omit<T, 'setAutocomplete'>
| T
| ((builder: T) => Omit<T, 'addChoices'> | Omit<T, 'setAutocomplete'> | T),
| Omit<T, 'setAutocomplete'>
| Omit<T, 'addChoices'>
| ((builder: T) => T | Omit<T, 'setAutocomplete'> | Omit<T, 'addChoices'>),
Instance: new () => T,
): ShouldOmitSubcommandFunctions extends true ? Omit<this, 'addSubcommand' | 'addSubcommandGroup'> : this {
const { options } = this;

View File

@@ -1,5 +1,5 @@
import { ApplicationCommandOptionType, type APIApplicationCommandAttachmentOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { APIApplicationCommandAttachmentOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandAttachmentOption extends ApplicationCommandOptionBase {
public override readonly type = ApplicationCommandOptionType.Attachment as const;

View File

@@ -1,5 +1,5 @@
import { ApplicationCommandOptionType, type APIApplicationCommandBooleanOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { APIApplicationCommandBooleanOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandBooleanOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Boolean as const;

View File

@@ -1,7 +1,7 @@
import { ApplicationCommandOptionType, type APIApplicationCommandChannelOption } from 'discord-api-types/v10';
import { APIApplicationCommandChannelOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin';
@mix(ApplicationCommandOptionChannelTypesMixin)
export class SlashCommandChannelOption extends ApplicationCommandOptionBase {

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandOptionType, type APIApplicationCommandIntegerOption } from 'discord-api-types/v10';
import { APIApplicationCommandIntegerOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
const numberValidator = s.number.int;
@@ -14,9 +14,6 @@ export class SlashCommandIntegerOption
{
public readonly type = ApplicationCommandOptionType.Integer as const;
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMaxValue}
*/
public setMaxValue(max: number): this {
numberValidator.parse(max);
@@ -25,9 +22,6 @@ export class SlashCommandIntegerOption
return this;
}
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMinValue}
*/
public setMinValue(min: number): this {
numberValidator.parse(min);

View File

@@ -1,5 +1,5 @@
import { ApplicationCommandOptionType, type APIApplicationCommandMentionableOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { APIApplicationCommandMentionableOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandMentionableOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Mentionable as const;

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandOptionType, type APIApplicationCommandNumberOption } from 'discord-api-types/v10';
import { APIApplicationCommandNumberOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
const numberValidator = s.number;
@@ -14,9 +14,6 @@ export class SlashCommandNumberOption
{
public readonly type = ApplicationCommandOptionType.Number as const;
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMaxValue}
*/
public setMaxValue(max: number): this {
numberValidator.parse(max);
@@ -25,9 +22,6 @@ export class SlashCommandNumberOption
return this;
}
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMinValue}
*/
public setMinValue(min: number): this {
numberValidator.parse(min);

View File

@@ -1,5 +1,5 @@
import { ApplicationCommandOptionType, type APIApplicationCommandRoleOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { APIApplicationCommandRoleOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandRoleOption extends ApplicationCommandOptionBase {
public override readonly type = ApplicationCommandOptionType.Role as const;

View File

@@ -1,18 +1,16 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandOptionType, type APIApplicationCommandStringOption } from 'discord-api-types/v10';
import { APIApplicationCommandStringOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
const minLengthValidator = s.number.greaterThanOrEqual(0).lessThanOrEqual(6_000);
const maxLengthValidator = s.number.greaterThanOrEqual(1).lessThanOrEqual(6_000);
const minLengthValidator = s.number.greaterThanOrEqual(0).lessThanOrEqual(6000);
const maxLengthValidator = s.number.greaterThanOrEqual(1).lessThanOrEqual(6000);
@mix(ApplicationCommandOptionWithChoicesAndAutocompleteMixin)
export class SlashCommandStringOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.String as const;
public readonly max_length?: number;
public readonly min_length?: number;
/**

View File

@@ -1,5 +1,5 @@
import { ApplicationCommandOptionType, type APIApplicationCommandUserOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { APIApplicationCommandUserOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandUserOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.User as const;

View File

@@ -1,6 +1,6 @@
import { s } from '@sapphire/shapeshift';
import type { APIEmbedField } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { isValidationEnabled } from '../../util/validation';
export const fieldNamePredicate = s.string
.lengthGreaterThanOrEqual(1)
@@ -9,7 +9,7 @@ export const fieldNamePredicate = s.string
export const fieldValuePredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(1_024)
.lengthLessThanOrEqual(1024)
.setValidationEnabled(isValidationEnabled);
export const fieldInlinePredicate = s.boolean.optional;
@@ -64,12 +64,12 @@ export const colorPredicate = s.number.int
export const descriptionPredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(4_096)
.lengthLessThanOrEqual(4096)
.nullable.setValidationEnabled(isValidationEnabled);
export const footerTextPredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(2_048)
.lengthLessThanOrEqual(2048)
.nullable.setValidationEnabled(isValidationEnabled);
export const embedFooterPredicate = s

Some files were not shown because too many files have changed in this diff Show More