diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index c810fdb13..4999ac274 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -22,21 +22,13 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-test:e2e-${{ inputs.package }}-${{ github.sha }} - - name: Build dist cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build-${{ github.sha }} + cache-prefix: turbo-cache- - name: E2E Test if: ${{ inputs.event_name != 'pull_request' }} - run: yarn test:e2e --cache-dir=".turbo" --filter=./packages/${{ inputs.package }} + run: yarn test:e2e --filter=./packages/${{ inputs.package }} timeout-minutes: 5 env: DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 316f52e9e..3f5357640 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -19,20 +19,12 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-test:integration-${{ inputs.package }}-${{ github.sha }} - - name: Build dist cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build-${{ github.sha }} + cache-prefix: turbo-cache- - name: Integration Test - run: yarn test:integration --cache-dir=".turbo" --filter=./packages/${{ inputs.package }} + run: yarn test:integration --filter=./packages/${{ inputs.package }} timeout-minutes: 3 - name: Collect and upload the coverage report uses: codecov/codecov-action@v5 diff --git a/.github/workflows/lib-check.yml b/.github/workflows/lib-check.yml index 2b07e8bd0..8f4aa44c1 100644 --- a/.github/workflows/lib-check.yml +++ b/.github/workflows/lib-check.yml @@ -6,8 +6,6 @@ on: push: branches: - main - paths-ignore: - - 'website/**' merge_group: jobs: @@ -21,14 +19,12 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-build:type-${{ github.sha }} + cache-prefix: turbo-cache- - name: Build Types - run: yarn build:type --cache-dir=".turbo" + run: yarn build:type build-dist: name: Build Dist @@ -40,14 +36,12 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-build-${{ github.sha }} - - name: Type Test - run: yarn build --cache-dir=".turbo" + cache-prefix: turbo-cache- + - name: Build Dist + run: yarn build format: name: Format @@ -60,13 +54,12 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Build type cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-build:type-${{ github.sha }} + cache-prefix: turbo-cache- - name: Build Types - run: yarn build:type --cache-dir=".turbo" + run: yarn build:type - name: Check Formatting run: yarn biome ci --reporter=github @@ -81,20 +74,12 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-unit-and-integration-test:test-type-${{ github.sha }} - - name: Build type cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build:type-${{ github.sha }} + cache-prefix: turbo-cache- - name: Test Type Test - run: yarn test:test-type --cache-dir=".turbo" + run: yarn test:test-type # Not using matrix because test later on cant needs a specific job bot-unit-test: diff --git a/.github/workflows/other-runtime-unit-test.yml b/.github/workflows/other-runtime-unit-test.yml index 0b5061ab0..d6007d344 100644 --- a/.github/workflows/other-runtime-unit-test.yml +++ b/.github/workflows/other-runtime-unit-test.yml @@ -22,20 +22,12 @@ jobs: with: deno-version: 'v2.1.x' - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-test:deno-unit-${{ inputs.package }}-${{ github.sha }} - - name: Build dist cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build-${{ github.sha }} + cache-prefix: turbo-cache- - name: Deno Unit Test - run: yarn test:deno-unit --cache-dir=".turbo" --filter=./packages/${{ inputs.package }} + run: yarn test:deno-unit --filter=./packages/${{ inputs.package }} bun-unit-test: name: Bun Unit Test runs-on: ubuntu-latest @@ -49,18 +41,10 @@ jobs: with: bun-version: '1.1.42' - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-test:bun-unit-${{ inputs.package }}-${{ github.sha }} - - name: Build dist cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build-${{ github.sha }} + cache-prefix: turbo-cache- - name: Bun Unit Test - run: yarn test:bun-unit --cache-dir=".turbo" --filter=./packages/${{ inputs.package }} + run: yarn test:bun-unit --filter=./packages/${{ inputs.package }} timeout-minutes: 1 diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml index 79c07575a..43af9cd55 100644 --- a/.github/workflows/site.yml +++ b/.github/workflows/site.yml @@ -4,12 +4,6 @@ permissions: on: push: branches: ['main'] - paths: - - 'website/**' - - '.github/workflows/site.yml' - - 'typedoc.json' - - 'package.json' - - 'packages/**' pull_request: workflow_dispatch: @@ -31,10 +25,19 @@ jobs: website/yarn.lock - name: yarn install (root) run: yarn install --immutable + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 + with: + cache-prefix: turbo-cache- + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 + with: + cache-prefix: turbo-cache-website- + server-port: 41231 # The default is 41230, but we use a different cache key so we need a different port - name: Build all pkg run: yarn release-build - name: Build docs - run: yarn build:doc + run: yarn turbo build:doc --single-package - run: yarn install --immutable working-directory: ./website - name: Restore bundler cache @@ -45,8 +48,10 @@ jobs: restore-keys: | ${{ runner.os }}-docusaurus-bundler- - name: Build website - run: yarn build + run: yarn turbo build working-directory: ./website + env: + TURBO_API: http://localhost:41231 - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: @@ -64,10 +69,14 @@ jobs: website/yarn.lock - name: yarn install (root) run: yarn install --immutable + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 + with: + cache-prefix: turbo-cache- - name: Build all pkg run: yarn release-build - name: Build docs - run: yarn build:doc + run: yarn turbo build:doc --single-package - run: yarn install --immutable working-directory: ./website - run: yarn typecheck diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 326708ea2..fd9945f3e 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -19,26 +19,12 @@ jobs: node-version: 20 cache: yarn - run: yarn install --immutable - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v5 + - name: Cache for Turbo + uses: rharkor/caching-for-turbo@v2.3.5 with: - path: .turbo - key: ${{ runner.os }}-turbo-test:unit-${{ inputs.package }}-${{ github.sha }} - - name: Build dist cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build-${{ github.sha }} - - name: Build type cache - if: steps.turbo-cache.outputs.cache-hit != 'true' - uses: actions/cache@v5 - with: - path: .turbo - key: ${{ runner.os }}-turbo-build:type-${{ github.sha }} + cache-prefix: turbo-cache- - name: Unit Test - run: yarn test:unit --cache-dir=".turbo" --filter=./packages/${{ inputs.package }} + run: yarn test:unit --filter=./packages/${{ inputs.package }} timeout-minutes: 1 - name: Collect and upload the coverage report uses: codecov/codecov-action@v5 diff --git a/.mocharc.base.cjs b/.mocharc.base.cjs deleted file mode 100644 index 4d585c8b0..000000000 --- a/.mocharc.base.cjs +++ /dev/null @@ -1,9 +0,0 @@ -// This needs to be a different file as this is used for Deno and Bun - -module.exports = { - recursive: true, - timeout: 2000, - 'watch-extensions': 'ts', - 'watch-files': ['src', 'tests'], - parallel: false, -} diff --git a/.mocharc.cjs b/.mocharc.cjs index cc65d8d85..ae8736faa 100644 --- a/.mocharc.cjs +++ b/.mocharc.cjs @@ -1,6 +1,23 @@ -module.exports = { - ...require('./.mocharc.base.cjs'), - require: 'ts-node/register', - loader: 'ts-node/esm', - 'enable-source-maps': true, +// @ts-check + +// If we are running in Bun or Deno, they have native TypeScript support with .js imports, node requires .ts imports +const supportsTypescript = 'Bun' in globalThis || 'Deno' in globalThis + +/** @type {import("mocha").MochaInstanceOptions & Record} */ +const mochaConfig = { + timeout: 2000, + 'watch-extensions': 'ts', + 'watch-files': ['src', 'tests'], } + +if (!supportsTypescript) { + mochaConfig.require = ['ts-node/register'] + + // Node options + mochaConfig.loader = ['ts-node/esm'] + // Node will output a ExperimentalWarning about --loader (--experimental-loader) and a DeprecationWarning because ts-node uses fs.Stat + mochaConfig['no-warnings'] = true + mochaConfig['enable-source-maps'] = true +} + +module.exports = mochaConfig diff --git a/biome.jsonc b/biome.jsonc index b05d02e21..9798b6e42 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", "root": true, "formatter": { "enabled": true, diff --git a/docker-apps/rest-passthrough/package.json b/docker-apps/rest-passthrough/package.json index f3533f233..fd2bfd4c6 100644 --- a/docker-apps/rest-passthrough/package.json +++ b/docker-apps/rest-passthrough/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@favware/cliff-jumper": "^6.0.0", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "dotenv": "^17.2.3", "typescript": "^5.9.3" } diff --git a/docker-apps/rest-passthrough/yarn.lock b/docker-apps/rest-passthrough/yarn.lock index f97455f4c..069f0f232 100644 --- a/docker-apps/rest-passthrough/yarn.lock +++ b/docker-apps/rest-passthrough/yarn.lock @@ -321,12 +321,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^25.0.3": - version: 25.0.3 - resolution: "@types/node@npm:25.0.3" +"@types/node@npm:^25.0.6": + version: 25.0.6 + resolution: "@types/node@npm:25.0.6" dependencies: undici-types: "npm:~7.16.0" - checksum: 10c0/b7568f0d765d9469621615e2bb257c7fd1953d95e9acbdb58dffb6627a2c4150d405a4600aa1ad8a40182a94fe5f903cafd3c0a2f5132814debd0e3bfd61f835 + checksum: 10c0/4ee3b6b606bc28f6840c04d58769e1e75dd8085149908956c874c85f8f8c890990701bcbe783fc2d14017817861dc07fcf23de6fc2805fe491f84eae717b5705 languageName: node linkType: hard @@ -1133,7 +1133,7 @@ __metadata: "@fastify/helmet": "npm:^13.0.2" "@fastify/multipart": "npm:^9.0.3" "@favware/cliff-jumper": "npm:^6.0.0" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" dotenv: "npm:^17.2.3" fastify: "npm:^5.5.0" typescript: "npm:^5.9.3" diff --git a/examples/bigbot/yarn.lock b/examples/bigbot/yarn.lock index e1d0b3091..775e80e00 100644 --- a/examples/bigbot/yarn.lock +++ b/examples/bigbot/yarn.lock @@ -1102,12 +1102,12 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^3.0.1": - version: 3.0.1 - resolution: "mkdirp@npm:3.0.1" - bin: - mkdirp: dist/cjs/src/bin.js - checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec languageName: node linkType: hard @@ -1617,16 +1617,15 @@ __metadata: linkType: hard "tar@npm:^7.4.3": - version: 7.4.3 - resolution: "tar@npm:7.4.3" + version: 7.5.3 + resolution: "tar@npm:7.5.3" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" - minizlib: "npm:^3.0.1" - mkdirp: "npm:^3.0.1" + minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d + checksum: 10c0/e5e3237bca325fbb33282d92d9807f4c8d81abaf71bf2627efdf93bd5610c146460c78fc7e9767d4ab5ae3c0b18af8197314c964f8cbd23b30b25bf4d42d7cb4 languageName: node linkType: hard diff --git a/package.json b/package.json index 0c4d19461..f5f3b0a9f 100644 --- a/package.json +++ b/package.json @@ -10,27 +10,26 @@ "scripts": { "build": "turbo build", "build:type": "turbo build:type", + "release-build": "turbo build build:type", "build:doc": "typedoc --options ./typedoc.jsonc", "build:watch": "turbo watch build build:type", - "release-build": "turbo build build:type", "test:unit": "turbo test:unit", - "test:unit-coverage": "turbo test:unit-coverage", - "test:bun-unit": "turbo test:bun-unit", "test:deno-unit": "turbo test:deno-unit", + "test:bun-unit": "turbo test:bun-unit", "test:integration": "turbo test:integration", "test:e2e": "turbo test:e2e", - "test:type": "turbo build:type", "test:test-type": "turbo test:test-type", + "test:type": "turbo build:type", "check": "turbo check", "check:all": "biome check --write", "postinstall": "husky" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "husky": "^9.1.7", "lint-staged": "^16.2.7", - "turbo": "^2.7.2", - "typedoc": "^0.28.15", + "turbo": "^2.7.4", + "typedoc": "^0.28.16", "typedoc-plugin-markdown": "^4.9.0", "typescript": "^5.9.3" }, diff --git a/packages/bot/package.json b/packages/bot/package.json index cb31ff7fb..75465c254 100644 --- a/packages/bot/package.json +++ b/packages/bot/package.json @@ -17,14 +17,12 @@ "build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist", "release-build": "yarn build && yarn build:type", "check": "biome check --write", - "test:unit-coverage": "c8 mocha --no-warnings 'tests/unit/**/*.spec.ts'", - "test:unit": "c8 --r lcov mocha --no-warnings 'tests/unit/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js discordeno", - "test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/unit/**/*.spec.ts'", - "test:deno-unit": "deno -A ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/unit/**/*.spec.ts'", - "test:bun-unit": "bun ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/unit/**/*.spec.ts'", - "test:e2e": "c8 --r lcov mocha --no-warnings --jobs 1 --t 30000 'tests/e2e/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js rest", - "test:type": "tsc --noEmit", - "test:test-type": "tsc --project tests/tsconfig.json" + "test:unit": "c8 -r lcov mocha 'tests/unit/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js bot", + "test:deno-unit": "deno -A npm:mocha 'tests/unit/**/*.spec.ts'", + "test:bun-unit": "bun --bun mocha 'tests/unit/**/*.spec.ts'", + "test:e2e": "c8 -r lcov mocha -t 30000 'tests/e2e/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js bot", + "test:test-type": "tsc --project tests/tsconfig.json", + "test:type": "tsc --noEmit" }, "dependencies": { "@discordeno/gateway": "workspace:^", @@ -33,12 +31,12 @@ "@discordeno/utils": "workspace:^" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.7", + "@swc/core": "^1.15.8", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "@types/sinon": "^21.0.0", "c8": "^10.1.3", "chai": "^6.2.2", diff --git a/packages/bot/src/helpers.ts b/packages/bot/src/helpers.ts index 2eb5ef53f..ea937b36f 100644 --- a/packages/bot/src/helpers.ts +++ b/packages/bot/src/helpers.ts @@ -179,6 +179,9 @@ export function createBotHelpers { return await bot.rest.createInvite(channelId, options, reason) }, + getGuildRoleMemberCounts: async (guildId) => { + return await bot.rest.getGuildRoleMemberCounts(guildId) + }, createRole: async (guildId, options, reason) => { return bot.transformers.role(bot, snakelize(await bot.rest.createRole(guildId, options, reason)), { guildId }) }, @@ -871,6 +874,7 @@ export type BotHelpers Promise> createGuildTemplate: (guildId: BigString, options: CreateTemplate) => Promise> createInvite: (channelId: BigString, options?: CreateChannelInvite, reason?: string) => Promise> + getGuildRoleMemberCounts: (guildId: BigString) => Promise> createRole: (guildId: BigString, options: CreateGuildRole, reason?: string) => Promise> createScheduledEvent: ( guildId: BigString, diff --git a/packages/bot/src/transformers/automodRule.ts b/packages/bot/src/transformers/automodRule.ts index 5c295338b..a4ec14671 100644 --- a/packages/bot/src/transformers/automodRule.ts +++ b/packages/bot/src/transformers/automodRule.ts @@ -20,6 +20,7 @@ export function transformAutoModerationRule(bot: Bot, payload: DiscordAutoModera presets: payload.trigger_metadata.presets, allowList: payload.trigger_metadata.allow_list, mentionTotalLimit: payload.trigger_metadata.mention_total_limit, + mentionRaidProtectionEnabled: payload.trigger_metadata.mention_raid_protection_enabled, } : undefined, actions: payload.actions.map((action) => ({ diff --git a/packages/bot/src/transformers/role.ts b/packages/bot/src/transformers/role.ts index af559a7b9..18b4d04b3 100644 --- a/packages/bot/src/transformers/role.ts +++ b/packages/bot/src/transformers/role.ts @@ -55,6 +55,7 @@ export function transformRole(bot: Bot, payload: DiscordRole, extra?: { guildId? if (props.position) role.position = payload.position if (props.guildId && extra?.guildId) role.guildId = bot.transformers.snowflake(extra?.guildId) if (props.color && payload.color !== undefined) role.color = payload.color + if (props.colors && payload.colors) role.colors = bot.transformers.roleColors(bot, payload.colors) if (props.permissions && payload.permissions) role.permissions = new Permissions(payload.permissions) if (props.icon && payload.icon) role.icon = iconHashToBigInt(payload.icon) if (props.unicodeEmoji && payload.unicode_emoji) role.unicodeEmoji = payload.unicode_emoji diff --git a/packages/bot/src/transformers/types.ts b/packages/bot/src/transformers/types.ts index 165fa4b26..a48bc6eca 100644 --- a/packages/bot/src/transformers/types.ts +++ b/packages/bot/src/transformers/types.ts @@ -358,11 +358,57 @@ export interface AutoModerationRule { } export interface AutoModerationRuleTriggerMetadata { + /** + * Substrings which will be searched for in content. + * + * @remarks + * Only present with {@link AutoModerationTriggerTypes.Keyword} and {@link AutoModerationTriggerTypes.MemberProfile}. + * + * Can have up to 1000 elements in the array and each string can have up to 60 characters. + */ keywordFilter?: string[] + /** + * Regular expression patterns which will be matched against content. + * + * @remarks + * Only present with {@link AutoModerationTriggerTypes.Keyword} and {@link AutoModerationTriggerTypes.MemberProfile}. + * + * Only Rust flavored regex is currently supported. Can have up to 10 elements in the array and each string can have up to 260 characters. + */ + regexPatterns?: string[] + /** + * The Discord pre-defined wordsets which will be searched for in content. + * + * @remarks + * Only present with {@link AutoModerationTriggerTypes.KeywordPreset}. + */ presets?: DiscordAutoModerationRuleTriggerMetadataPresets[] + /** + * The substrings which should not trigger the rule. + * + * @remarks + * Only present with {@link AutoModerationTriggerTypes.Keyword}, {@link AutoModerationTriggerTypes.KeywordPreset} and {@link AutoModerationTriggerTypes.MemberProfile}. + * + * When used with {@link AutoModerationTriggerTypes.Keyword} and {@link AutoModerationTriggerTypes.MemberProfile}, there can be up to 100 elements in the array and each string can have up to 60 characters. + * When used with {@link AutoModerationTriggerTypes.KeywordPreset}, there can be up to 1000 elements in the array and each string can have up to 60 characters. + */ allowList?: string[] + /** + * Total number of unique role and user mentions allowed per message. + * + * @remarks + * Only present with {@link AutoModerationTriggerTypes.MentionSpam}. + * + * Maximum of 50 + */ mentionTotalLimit?: number - regexPatterns: string[] + /** + * Whether to automatically detect mention raids. + * + * @remarks + * Only present with {@link AutoModerationTriggerTypes.MentionSpam}. + */ + mentionRaidProtectionEnabled?: boolean } export interface AvatarDecorationData { @@ -901,7 +947,7 @@ export interface Interaction { /** The type of interaction */ type: InteractionTypes /** Guild that the interaction was sent from */ - guild: Guild + guild?: Partial /** The guild it was sent from */ guildId?: bigint /** The channel it was sent from */ diff --git a/packages/discordeno/package.json b/packages/discordeno/package.json index 11e99b859..8485f2480 100644 --- a/packages/discordeno/package.json +++ b/packages/discordeno/package.json @@ -17,13 +17,11 @@ "build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist", "release-build": "yarn build && yarn build:type", "check": "biome check --write", - "test:unit-coverage": "c8 mocha --no-warnings 'tests/**/*.spec.ts'", - "test:unit": "c8 --r lcov mocha --no-warnings 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js discordeno", - "test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/**/*.spec.ts'", - "test:deno-unit": "deno -A ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/**/*.spec.ts'", - "test:bun-unit": "bun ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/**/*.spec.ts'", - "test:type": "tsc --noEmit", - "test:test-type": "tsc --project tests/tsconfig.json" + "test:unit": "c8 -r lcov mocha 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js discordeno", + "test:deno-unit": "deno -A npm:mocha 'tests/**/*.spec.ts'", + "test:bun-unit": "bun --bun mocha 'tests/**/*.spec.ts'", + "test:test-type": "tsc --project tests/tsconfig.json", + "test:type": "tsc --noEmit" }, "dependencies": { "@discordeno/bot": "workspace:^", @@ -33,12 +31,12 @@ "@discordeno/utils": "workspace:^" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.7", + "@swc/core": "^1.15.8", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "c8": "^10.1.3", "chai": "^6.2.2", "mocha": "^11.7.5", diff --git a/packages/gateway/package.json b/packages/gateway/package.json index 126404a9c..d8c836f84 100644 --- a/packages/gateway/package.json +++ b/packages/gateway/package.json @@ -17,30 +17,28 @@ "build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist", "release-build": "yarn build && yarn build:type", "check": "biome check --write", - "test:unit-coverage": "c8 mocha --no-warnings 'tests/unit/**/*.spec.ts'", - "test:unit": "c8 --r lcov mocha --no-warnings 'tests/unit/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js gateway", - "test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/unit/**/*.spec.ts'", - "test:deno-unit": "deno -A ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/unit/**/*.spec.ts'", - "test:bun-unit": "bun ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/unit/**/*.spec.ts'", - "test:integration": "c8 --r lcov mocha --no-warnings --t 15000 'tests/integration/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js gateway", - "test:type": "tsc --noEmit", - "test:test-type": "tsc --project tests/tsconfig.json" + "test:unit": "c8 -r lcov mocha 'tests/unit/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js gateway", + "test:deno-unit": "deno -A npm:mocha 'tests/unit/**/*.spec.ts'", + "test:bun-unit": "bun --bun mocha 'tests/unit/**/*.spec.ts'", + "test:integration": "c8 -r lcov mocha -t 15000 'tests/integration/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js gateway", + "test:test-type": "tsc --project tests/tsconfig.json", + "test:type": "tsc --noEmit" }, "dependencies": { "@discordeno/types": "workspace:^", "@discordeno/utils": "workspace:^", - "ws": "^8.18.3" + "ws": "^8.19.0" }, "optionalDependencies": { "fzstd": "^0.1.1" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.7", + "@swc/core": "^1.15.8", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "@types/sinon": "^21.0.0", "@types/ws": "^8.18.1", "c8": "^10.1.3", diff --git a/packages/gateway/src/Shard.ts b/packages/gateway/src/Shard.ts index e09f2ca38..e5262b495 100644 --- a/packages/gateway/src/Shard.ts +++ b/packages/gateway/src/Shard.ts @@ -730,7 +730,6 @@ export class DiscordenoShard { break case 'READY': { const payload = packet.d as DiscordReady - this.events.ready?.(this) // Important for future resumes. this.resumeGatewayUrl = payload.resume_gateway_url @@ -739,6 +738,7 @@ export class DiscordenoShard { this.state = ShardState.Connected this.logger.debug(`[Shard] Shard #${this.id} received READY`) + this.events.ready?.(this) // Continue the requests which have been queued since the shard went offline. // Important when this is a re-identify diff --git a/packages/rest/package.json b/packages/rest/package.json index 621a89cf2..372bd76aa 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -17,27 +17,25 @@ "build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist", "release-build": "yarn build && yarn build:type", "check": "biome check --write", - "test:unit-coverage": "c8 mocha --no-warnings 'tests/unit/**/*.spec.ts'", - "test:unit": "c8 --r lcov mocha --no-warnings 'tests/unit/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js rest", - "test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/unit/**/*.spec.ts'", - "test:deno-unit": "deno -A ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/unit/**/*.spec.ts'", - "test:bun-unit": "bun ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/unit/**/*.spec.ts'", - "test:e2e": "c8 --r lcov mocha --exit --no-warnings --jobs 1 --t 60000 'tests/e2e/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js rest", - "test:type": "tsc --noEmit", - "test:test-type": "tsc --project tests/tsconfig.json" + "test:unit": "c8 -r lcov mocha 'tests/unit/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js rest", + "test:deno-unit": "deno -A npm:mocha 'tests/unit/**/*.spec.ts'", + "test:bun-unit": "bun --bun mocha 'tests/unit/**/*.spec.ts'", + "test:e2e": "c8 -r lcov mocha -t 60000 'tests/e2e/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js rest", + "test:test-type": "tsc --project tests/tsconfig.json", + "test:type": "tsc --noEmit" }, "dependencies": { "@discordeno/types": "workspace:^", "@discordeno/utils": "workspace:^" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.7", + "@swc/core": "^1.15.8", "@types/chai": "^5.2.3", "@types/chai-as-promised": "^8.0.2", "@types/mocha": "^10.0.10", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "@types/sinon": "^21.0.0", "c8": "^10.1.3", "chai": "^6.2.2", diff --git a/packages/rest/src/manager.ts b/packages/rest/src/manager.ts index aa7a8a1f3..026b008ba 100644 --- a/packages/rest/src/manager.ts +++ b/packages/rest/src/manager.ts @@ -819,6 +819,10 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage return await rest.post(rest.routes.channels.invites(channelId), { body, reason }) }, + async getGuildRoleMemberCounts(guildId) { + return await rest.get>(rest.routes.guilds.roles.memberCounts(guildId)) + }, + async createRole(guildId, body, reason) { return await rest.post(rest.routes.guilds.roles.all(guildId), { body, reason }) }, diff --git a/packages/rest/src/routes.ts b/packages/rest/src/routes.ts index e4c22633c..963b1ff96 100644 --- a/packages/rest/src/routes.ts +++ b/packages/rest/src/routes.ts @@ -2,29 +2,37 @@ import type { GetMessagesOptions, GetScheduledEventUsers } from '@discordeno/typ import { isGetMessagesAfter, isGetMessagesAround, isGetMessagesBefore, isGetMessagesLimit } from '@discordeno/utils' import type { RestRoutes } from './typings/routes.js' -export function createRoutes(): RestRoutes { +/** + * Creates the available discord API routes + * + * @param disableURIEncode Don't encode strings, except where required - Danger: disabling URI encoding may lead to path traversal if unsafe strings are used + * @returns The available discord API routes + */ +export function createRoutes(disableURIEncode: boolean = false): RestRoutes { + const encode: typeof encodeComponent = disableURIEncode ? (x) => x.toString() : encodeComponent + return { webhooks: { id: (webhookId) => { - return `/webhooks/${webhookId}` + return `/webhooks/${encode(webhookId)}` }, message: (webhookId, token, messageId, options) => { - let url = `/webhooks/${webhookId}/${token}/messages/${messageId}?` + let url = `/webhooks/${encode(webhookId)}/${encode(token)}/messages/${encode(messageId)}?` if (options) { - if (options.threadId) url += `thread_id=${options.threadId}` - if (options.withComponents) url += `&with_components=${options.withComponents}` + if (options.threadId) url += `thread_id=${encode(options.threadId)}` + if (options.withComponents) url += `&with_components=${encode(options.withComponents)}` } return url }, webhook: (webhookId, token, options) => { - let url = `/webhooks/${webhookId}/${token}?` + let url = `/webhooks/${encode(webhookId)}/${encode(token)}?` if (options) { - if (options?.wait !== undefined) url += `wait=${options.wait.toString()}` - if (options.threadId) url += `&thread_id=${options.threadId}` - if (options.withComponents) url += `&with_components=${options.withComponents}` + if (options?.wait !== undefined) url += `wait=${encode(options.wait)}` + if (options.threadId) url += `&thread_id=${encode(options.threadId)}` + if (options.withComponents) url += `&with_components=${encode(options.withComponents)}` } return url @@ -34,106 +42,104 @@ export function createRoutes(): RestRoutes { // Channel Endpoints channels: { bulk: (channelId) => { - return `/channels/${channelId}/messages/bulk-delete` + return `/channels/${encode(channelId)}/messages/bulk-delete` }, dm: () => { return '/users/@me/channels' }, dmRecipient: (channelId, userId) => { - return `/channels/${channelId}/recipients/${userId}` + return `/channels/${encode(channelId)}/recipients/${encode(userId)}` }, pin: (channelId, messageId) => { - return `/channels/${channelId}/pins/${messageId}` + return `/channels/${encode(channelId)}/pins/${encode(messageId)}` }, pins: (channelId) => { - return `/channels/${channelId}/pins` + return `/channels/${encode(channelId)}/pins` }, messagePins: (channelId, options) => { - let url = `/channels/${channelId}/messages/pins?` + let url = `/channels/${encode(channelId)}/messages/pins?` if (options) { - if (options.before) url += `before=${options.before}` - if (options.limit) url += `&limit=${options.limit}` + if (options.before) url += `before=${encode(options.before)}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url }, messagePin: (channelId, messageId) => { - return `/channels/${channelId}/messages/pins/${messageId}` + return `/channels/${encode(channelId)}/messages/pins/${encode(messageId)}` }, reactions: { bot: (channelId, messageId, emoji) => { - return `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}/@me` + return `/channels/${encode(channelId)}/messages/${encode(messageId)}/reactions/${encodeURIComponent(emoji)}/@me` }, user: (channelId, messageId, emoji, userId) => { - return `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}/${userId}` + return `/channels/${encode(channelId)}/messages/${encode(messageId)}/reactions/${encodeURIComponent(emoji)}/${encode(userId)}` }, all: (channelId, messageId) => { - return `/channels/${channelId}/messages/${messageId}/reactions` + return `/channels/${encode(channelId)}/messages/${encode(messageId)}/reactions` }, emoji: (channelId, messageId, emoji, options) => { - let url = `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}?` + let url = `/channels/${encode(channelId)}/messages/${encode(messageId)}/reactions/${encodeURIComponent(emoji)}?` if (options) { - if (options.type) url += `type=${options.type}` - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - if (options.after) url += `&after=${options.after}` - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - if (options.limit) url += `&limit=${options.limit}` + if (options.type) url += `type=${encode(options.type)}` + if (options.after) url += `&after=${encode(options.after)}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url }, message: (channelId, messageId, emoji, options) => { - let url = `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}?` + let url = `/channels/${encode(channelId)}/messages/${encode(messageId)}/reactions/${encodeURIComponent(emoji)}?` if (options) { - if (options.after) url += `after=${options.after}` - if (options.limit) url += `&limit=${options.limit}` + if (options.after) url += `after=${encode(options.after)}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url }, }, webhooks: (channelId) => { - return `/channels/${channelId}/webhooks` + return `/channels/${encode(channelId)}/webhooks` }, channel: (channelId) => { - return `/channels/${channelId}` + return `/channels/${encode(channelId)}` }, follow: (channelId) => { - return `/channels/${channelId}/followers` + return `/channels/${encode(channelId)}/followers` }, forum: (channelId) => { - return `/channels/${channelId}/threads` + return `/channels/${encode(channelId)}/threads` }, invites: (channelId) => { - return `/channels/${channelId}/invites` + return `/channels/${encode(channelId)}/invites` }, message: (channelId, messageId) => { - return `/channels/${channelId}/messages/${messageId}` + return `/channels/${encode(channelId)}/messages/${encode(messageId)}` }, messages: (channelId, options?: GetMessagesOptions) => { - let url = `/channels/${channelId}/messages?` + let url = `/channels/${encode(channelId)}/messages?` if (options) { if (isGetMessagesAfter(options) && options.after) { - url += `after=${options.after}` + url += `after=${encode(options.after)}` } if (isGetMessagesBefore(options) && options.before) { - url += `&before=${options.before}` + url += `&before=${encode(options.before)}` } if (isGetMessagesAround(options) && options.around) { - url += `&around=${options.around}` + url += `&around=${encode(options.around)}` } if (isGetMessagesLimit(options) && options.limit) { - url += `&limit=${options.limit}` + url += `&limit=${encode(options.limit)}` } } @@ -141,11 +147,11 @@ export function createRoutes(): RestRoutes { }, overwrite: (channelId, overwriteId) => { - return `/channels/${channelId}/permissions/${overwriteId}` + return `/channels/${encode(channelId)}/permissions/${encode(overwriteId)}` }, crosspost: (channelId, messageId) => { - return `/channels/${channelId}/messages/${messageId}/crosspost` + return `/channels/${encode(channelId)}/messages/${encode(messageId)}/crosspost` }, stages: () => { @@ -153,81 +159,84 @@ export function createRoutes(): RestRoutes { }, stage: (channelId) => { - return `/stage-instances/${channelId}` + return `/stage-instances/${encode(channelId)}` }, // Thread Endpoints threads: { message: (channelId, messageId) => { - return `/channels/${channelId}/messages/${messageId}/threads` + return `/channels/${encode(channelId)}/messages/${encode(messageId)}/threads` }, all: (channelId) => { - return `/channels/${channelId}/threads` + return `/channels/${encode(channelId)}/threads` }, active: (guildId) => { - return `/guilds/${guildId}/threads/active` + return `/guilds/${encode(guildId)}/threads/active` }, members: (channelId, options) => { - let url = `/channels/${channelId}/thread-members?` + let url = `/channels/${encode(channelId)}/thread-members?` if (options) { - if (options.withMember) url += `with_member=${options.withMember}` - if (options.limit) url += `&limit=${options.limit}` - if (options.after) url += `&after=${options.after}` + if (options.withMember) url += `with_member=${encode(options.withMember)}` + if (options.limit) url += `&limit=${encode(options.limit)}` + if (options.after) url += `&after=${encode(options.after)}` } return url }, me: (channelId) => { - return `/channels/${channelId}/thread-members/@me` + return `/channels/${encode(channelId)}/thread-members/@me` }, getUser(channelId, userId, options) { - let url = `/channels/${channelId}/thread-members/${userId}?` + let url = `/channels/${encode(channelId)}/thread-members/${encode(userId)}?` if (options) { - if (options.withMember) url += `with_member=${options.withMember}` + if (options.withMember) url += `with_member=${encode(options.withMember)}` } return url }, user: (channelId, userId) => { - return `/channels/${channelId}/thread-members/${userId}` + return `/channels/${encode(channelId)}/thread-members/${encode(userId)}` }, archived: (channelId) => { - return `/channels/${channelId}/threads/archived` + return `/channels/${encode(channelId)}/threads/archived` }, public: (channelId, options) => { - let url = `/channels/${channelId}/threads/archived/public?` + let url = `/channels/${encode(channelId)}/threads/archived/public?` if (options) { if (options.before) { - url += `before=${new Date(options.before).toISOString()}` + const iso = new Date(options.before).toISOString() + url += `before=${encode(iso)}` } - if (options.limit) url += `&limit=${options.limit}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url }, private: (channelId, options) => { - let url = `/channels/${channelId}/threads/archived/private?` + let url = `/channels/${encode(channelId)}/threads/archived/private?` if (options) { if (options.before) { - url += `before=${new Date(options.before).toISOString()}` + const iso = new Date(options.before).toISOString() + url += `before=${encode(iso)}` } - if (options.limit) url += `&limit=${options.limit}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url }, joined: (channelId, options) => { - let url = `/channels/${channelId}/users/@me/threads/archived/private?` + let url = `/channels/${encode(channelId)}/users/@me/threads/archived/private?` if (options) { if (options.before) { - url += `before=${new Date(options.before).toISOString()}` + const iso = new Date(options.before).toISOString() + url += `before=${encode(iso)}` } - if (options.limit) url += `&limit=${options.limit}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url @@ -235,22 +244,22 @@ export function createRoutes(): RestRoutes { }, typing: (channelId) => { - return `/channels/${channelId}/typing` + return `/channels/${encode(channelId)}/typing` }, polls: { votes: (channelId, messageId, answerId, options) => { - let url = `/channels/${channelId}/polls/${messageId}/answers/${answerId}?` + let url = `/channels/${encode(channelId)}/polls/${encode(messageId)}/answers/${encode(answerId)}?` if (options) { - if (options.after) url += `after=${options.after}` - if (options.limit) url += `&limit=${options.limit}` + if (options.after) url += `after=${encode(options.after)}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url }, expire: (channelId, messageId) => { - return `/channels/${channelId}/polls/${messageId}/expire` + return `/channels/${encode(channelId)}/polls/${encode(messageId)}/expire` }, }, }, @@ -264,167 +273,167 @@ export function createRoutes(): RestRoutes { let url = '/users/@me/guilds?' if (options) { - if (options.after) url += `after=${options.after}` - if (options.before) url += `&before=${options.before}` - if (options.limit) url += `&limit=${options.limit}` - if (options.withCounts) url += `&with_counts=${options.withCounts}` + if (options.after) url += `after=${encode(options.after)}` + if (options.before) url += `&before=${encode(options.before)}` + if (options.limit) url += `&limit=${encode(options.limit)}` + if (options.withCounts) url += `&with_counts=${encode(options.withCounts)}` } return url }, auditlogs: (guildId, options) => { - let url = `/guilds/${guildId}/audit-logs?` + let url = `/guilds/${encode(guildId)}/audit-logs?` if (options) { - if (options.actionType) url += `action_type=${options.actionType}` - if (options.before) url += `&before=${options.before}` - if (options.after) url += `&after=${options.after}` - if (options.limit) url += `&limit=${options.limit}` - if (options.userId) url += `&user_id=${options.userId}` + if (options.actionType) url += `action_type=${encode(options.actionType)}` + if (options.before) url += `&before=${encode(options.before)}` + if (options.after) url += `&after=${encode(options.after)}` + if (options.limit) url += `&limit=${encode(options.limit)}` + if (options.userId) url += `&user_id=${encode(options.userId)}` } return url }, automod: { rule: (guildId, ruleId) => { - return `/guilds/${guildId}/auto-moderation/rules/${ruleId}` + return `/guilds/${encode(guildId)}/auto-moderation/rules/${encode(ruleId)}` }, rules: (guildId) => { - return `/guilds/${guildId}/auto-moderation/rules` + return `/guilds/${encode(guildId)}/auto-moderation/rules` }, }, channels: (guildId) => { - return `/guilds/${guildId}/channels` + return `/guilds/${encode(guildId)}/channels` }, emoji: (guildId, emojiId) => { - return `/guilds/${guildId}/emojis/${emojiId}` + return `/guilds/${encode(guildId)}/emojis/${encode(emojiId)}` }, emojis: (guildId) => { - return `/guilds/${guildId}/emojis` + return `/guilds/${encode(guildId)}/emojis` }, events: { events: (guildId, withUserCount?: boolean) => { - let url = `/guilds/${guildId}/scheduled-events?` + let url = `/guilds/${encode(guildId)}/scheduled-events?` if (withUserCount !== undefined) { - url += `with_user_count=${withUserCount.toString()}` + url += `with_user_count=${encode(withUserCount)}` } return url }, event: (guildId, eventId, withUserCount?: boolean) => { - let url = `/guilds/${guildId}/scheduled-events/${eventId}` + let url = `/guilds/${encode(guildId)}/scheduled-events/${encode(eventId)}` if (withUserCount !== undefined) { - url += `with_user_count=${withUserCount.toString()}` + url += `with_user_count=${encode(withUserCount)}` } return url }, users: (guildId, eventId, options?: GetScheduledEventUsers) => { - let url = `/guilds/${guildId}/scheduled-events/${eventId}/users?` + let url = `/guilds/${encode(guildId)}/scheduled-events/${encode(eventId)}/users?` if (options) { - if (options.limit !== undefined) url += `limit=${options.limit}` + if (options.limit !== undefined) url += `limit=${encode(options.limit)}` if (options.withMember !== undefined) { - url += `&with_member=${options.withMember.toString()}` + url += `&with_member=${encode(options.withMember)}` } - if (options.after !== undefined) url += `&after=${options.after}` - if (options.before !== undefined) url += `&before=${options.before}` + if (options.after !== undefined) url += `&after=${encode(options.after)}` + if (options.before !== undefined) url += `&before=${encode(options.before)}` } return url }, }, guild(guildId, withCounts) { - let url = `/guilds/${guildId}?` + let url = `/guilds/${encode(guildId)}?` if (withCounts !== undefined) { - url += `with_counts=${withCounts.toString()}` + url += `with_counts=${encode(withCounts)}` } return url }, integration(guildId, integrationId) { - return `/guilds/${guildId}/integrations/${integrationId}` + return `/guilds/${encode(guildId)}/integrations/${encode(integrationId)}` }, integrations: (guildId) => { - return `/guilds/${guildId}/integrations?include_applications=true` + return `/guilds/${encode(guildId)}/integrations?include_applications=true` }, invite(inviteCode, options) { - let url = `/invites/${inviteCode}?` + let url = `/invites/${encode(inviteCode)}?` if (options) { if (options.withCounts !== undefined) { - url += `with_counts=${options.withCounts.toString()}` + url += `with_counts=${encode(options.withCounts)}` } if (options.scheduledEventId) { - url += `&guild_scheduled_event_id=${options.scheduledEventId}` + url += `&guild_scheduled_event_id=${encode(options.scheduledEventId)}` } } return url }, invites: (guildId) => { - return `/guilds/${guildId}/invites` + return `/guilds/${encode(guildId)}/invites` }, leave: (guildId) => { - return `/users/@me/guilds/${guildId}` + return `/users/@me/guilds/${encode(guildId)}` }, members: { ban: (guildId, userId) => { - return `/guilds/${guildId}/bans/${userId}` + return `/guilds/${encode(guildId)}/bans/${encode(userId)}` }, bans: (guildId, options) => { - let url = `/guilds/${guildId}/bans?` + let url = `/guilds/${encode(guildId)}/bans?` if (options) { - if (options.limit) url += `limit=${options.limit}` - if (options.after) url += `&after=${options.after}` - if (options.before) url += `&before=${options.before}` + if (options.limit) url += `limit=${encode(options.limit)}` + if (options.after) url += `&after=${encode(options.after)}` + if (options.before) url += `&before=${encode(options.before)}` } return url }, bulkBan: (guildId) => { - return `/guilds/${guildId}/bulk-ban` + return `/guilds/${encode(guildId)}/bulk-ban` }, bot: (guildId) => { - return `/guilds/${guildId}/members/@me` + return `/guilds/${encode(guildId)}/members/@me` }, member: (guildId, userId) => { - return `/guilds/${guildId}/members/${userId}` + return `/guilds/${encode(guildId)}/members/${encode(userId)}` }, currentMember: (guildId) => { - return `/users/@me/guilds/${guildId}/member` + return `/users/@me/guilds/${encode(guildId)}/member` }, members: (guildId, options) => { - let url = `/guilds/${guildId}/members?` + let url = `/guilds/${encode(guildId)}/members?` if (options !== undefined) { - if (options.limit) url += `limit=${options.limit}` - if (options.after) url += `&after=${options.after}` + if (options.limit) url += `limit=${encode(options.limit)}` + if (options.after) url += `&after=${encode(options.after)}` } return url }, search: (guildId, query, options) => { - let url = `/guilds/${guildId}/members/search?query=${encodeURIComponent(query)}` + let url = `/guilds/${encode(guildId)}/members/search?query=${encode(query)}` if (options) { - if (options.limit !== undefined) url += `&limit=${options.limit}` + if (options.limit !== undefined) url += `&limit=${encode(options.limit)}` } return url }, prune: (guildId, options) => { - let url = `/guilds/${guildId}/prune?` + let url = `/guilds/${encode(guildId)}/prune?` if (options) { - if (options.days) url += `days=${options.days}` + if (options.days) url += `days=${encode(options.days)}` if (Array.isArray(options.includeRoles)) { - url += `&include_roles=${options.includeRoles.join(',')}` + url += `&include_roles=${encode(options.includeRoles.join(','))}` } else if (options.includeRoles) { - url += `&include_roles=${options.includeRoles}` + url += `&include_roles=${encode(options.includeRoles)}` } } @@ -432,17 +441,17 @@ export function createRoutes(): RestRoutes { }, }, preview: (guildId) => { - return `/guilds/${guildId}/preview` + return `/guilds/${encode(guildId)}/preview` }, prune: (guildId, options) => { - let url = `/guilds/${guildId}/prune?` + let url = `/guilds/${encode(guildId)}/prune?` if (options) { - if (options.days) url += `days=${options.days}` + if (options.days) url += `days=${encode(options.days)}` if (Array.isArray(options.includeRoles)) { - url += `&include_roles=${options.includeRoles.join(',')}` + url += `&include_roles=${encode(options.includeRoles.join(','))}` } else if (options.includeRoles) { - url += `&include_roles=${options.includeRoles}` + url += `&include_roles=${encode(options.includeRoles)}` } } @@ -450,63 +459,66 @@ export function createRoutes(): RestRoutes { }, roles: { one: (guildId, roleId) => { - return `/guilds/${guildId}/roles/${roleId}` + return `/guilds/${encode(guildId)}/roles/${encode(roleId)}` }, all: (guildId) => { - return `/guilds/${guildId}/roles` + return `/guilds/${encode(guildId)}/roles` }, member: (guildId, memberId, roleId) => { - return `/guilds/${guildId}/members/${memberId}/roles/${roleId}` + return `/guilds/${encode(guildId)}/members/${encode(memberId)}/roles/${encode(roleId)}` + }, + memberCounts: (guildId) => { + return `/guilds/${guildId}/roles/member-counts` }, }, stickers: (guildId) => { - return `/guilds/${guildId}/stickers` + return `/guilds/${encode(guildId)}/stickers` }, sticker: (guildId, stickerId) => { - return `/guilds/${guildId}/stickers/${stickerId}` + return `/guilds/${encode(guildId)}/stickers/${encode(stickerId)}` }, voice: (guildId, userId) => { - return `/guilds/${guildId}/voice-states/${userId ?? '@me'}` + return `/guilds/${encode(guildId)}/voice-states/${encode(userId ?? '@me')}` }, templates: { code: (code) => { - return `/guilds/templates/${code}` + return `/guilds/templates/${encode(code)}` }, guild: (guildId, code) => { - return `/guilds/${guildId}/templates/${code}` + return `/guilds/${encode(guildId)}/templates/${encode(code)}` }, all: (guildId) => { - return `/guilds/${guildId}/templates` + return `/guilds/${encode(guildId)}/templates` }, }, vanity: (guildId) => { - return `/guilds/${guildId}/vanity-url` + return `/guilds/${encode(guildId)}/vanity-url` }, regions: (guildId) => { - return `/guilds/${guildId}/regions` + return `/guilds/${encode(guildId)}/regions` }, webhooks: (guildId) => { - return `/guilds/${guildId}/webhooks` + return `/guilds/${encode(guildId)}/webhooks` }, welcome: (guildId) => { - return `/guilds/${guildId}/welcome-screen` + return `/guilds/${encode(guildId)}/welcome-screen` }, widget: (guildId) => { - return `/guilds/${guildId}/widget` + return `/guilds/${encode(guildId)}/widget` }, widgetJson: (guildId) => { - return `/guilds/${guildId}/widget.json` + return `/guilds/${encode(guildId)}/widget.json` }, onboarding: (guildId) => { - return `/guilds/${guildId}/onboarding` + return `/guilds/${encode(guildId)}/onboarding` }, incidentActions: (guildId) => { - return `/guilds/${guildId}/incident-actions` + return `/guilds/${encode(guildId)}/incident-actions` }, }, sticker: (stickerId) => { - return `/stickers/${stickerId}` + return `/stickers/${encode(stickerId)}` }, regions: () => { @@ -518,10 +530,10 @@ export function createRoutes(): RestRoutes { commands: { // Application Endpoints commands: (applicationId, withLocalizations) => { - let url = `/applications/${applicationId}/commands?` + let url = `/applications/${encode(applicationId)}/commands?` if (withLocalizations !== undefined) { - url += `with_localizations=${withLocalizations.toString()}` + url += `with_localizations=${encode(withLocalizations)}` } return url @@ -529,30 +541,30 @@ export function createRoutes(): RestRoutes { guilds: { all(applicationId, guildId, withLocalizations) { - let url = `/applications/${applicationId}/guilds/${guildId}/commands?` + let url = `/applications/${encode(applicationId)}/guilds/${encode(guildId)}/commands?` if (withLocalizations !== undefined) { - url += `with_localizations=${withLocalizations.toString()}` + url += `with_localizations=${encode(withLocalizations)}` } return url }, one(applicationId, guildId, commandId) { - return `/applications/${applicationId}/guilds/${guildId}/commands/${commandId}` + return `/applications/${encode(applicationId)}/guilds/${encode(guildId)}/commands/${encode(commandId)}` }, }, permissions: (applicationId, guildId) => { - return `/applications/${applicationId}/guilds/${guildId}/commands/permissions` + return `/applications/${encode(applicationId)}/guilds/${encode(guildId)}/commands/permissions` }, permission: (applicationId, guildId, commandId) => { - return `/applications/${applicationId}/guilds/${guildId}/commands/${commandId}/permissions` + return `/applications/${encode(applicationId)}/guilds/${encode(guildId)}/commands/${encode(commandId)}/permissions` }, command: (applicationId, commandId, withLocalizations) => { - let url = `/applications/${applicationId}/commands/${commandId}?` + let url = `/applications/${encode(applicationId)}/commands/${encode(commandId)}?` if (withLocalizations !== undefined) { - url += `withLocalizations=${withLocalizations.toString()}` + url += `withLocalizations=${encode(withLocalizations)}` } return url @@ -562,13 +574,13 @@ export function createRoutes(): RestRoutes { responses: { // Interaction Endpoints callback: (interactionId, token, options) => { - return `/interactions/${interactionId}/${token}/callback?with_response=${!!options?.withResponse}` + return `/interactions/${encode(interactionId)}/${encode(token)}/callback?with_response=${encode(!!options?.withResponse)}` }, original: (interactionId, token) => { - return `/webhooks/${interactionId}/${token}/messages/@original` + return `/webhooks/${encode(interactionId)}/${encode(token)}/messages/@original` }, message: (applicationId, token, messageId) => { - return `/webhooks/${applicationId}/${token}/messages/${messageId}` + return `/webhooks/${encode(applicationId)}/${encode(token)}/messages/${encode(messageId)}` }, }, }, @@ -591,49 +603,49 @@ export function createRoutes(): RestRoutes { return '/users/@me/connections' }, roleConnections: (applicationId) => { - return `/users/@me/applications/${applicationId}/role-connection` + return `/users/@me/applications/${encode(applicationId)}/role-connection` }, }, monetization: { entitlements: (applicationId, options) => { - let url = `/applications/${applicationId}/entitlements?` + let url = `/applications/${encode(applicationId)}/entitlements?` if (options) { - if (options.after) url += `after=${options.after}` - if (options.before) url += `&before=${options.before}` - if (options.excludeEnded) url += `&exclude_ended=${options.excludeEnded}` - if (options.guildId) url += `&guild_id=${options.guildId}` - if (options.limit) url += `&limit=${options.limit}` - if (options.skuIds) url += `&sku_ids=${options.skuIds.join(',')}` - if (options.userId) url += `&user_id=${options.userId}` + if (options.after) url += `after=${encode(options.after)}` + if (options.before) url += `&before=${encode(options.before)}` + if (options.excludeEnded) url += `&exclude_ended=${encode(options.excludeEnded)}` + if (options.guildId) url += `&guild_id=${encode(options.guildId)}` + if (options.limit) url += `&limit=${encode(options.limit)}` + if (options.skuIds) url += `&sku_ids=${encode(options.skuIds.join(','))}` + if (options.userId) url += `&user_id=${encode(options.userId)}` } return url }, entitlement: (applicationId, entitlementId) => { - return `/applications/${applicationId}/entitlements/${entitlementId}` + return `/applications/${encode(applicationId)}/entitlements/${encode(entitlementId)}` }, consumeEntitlement: (applicationId, entitlementId) => { - return `/applications/${applicationId}/entitlements/${entitlementId}/consume` + return `/applications/${encode(applicationId)}/entitlements/${encode(entitlementId)}/consume` }, skus: (applicationId) => { - return `/applications/${applicationId}/skus` + return `/applications/${encode(applicationId)}/skus` }, subscription: (skuId, subscriptionId) => { - return `/skus/${skuId}/subscriptions/${subscriptionId}` + return `/skus/${encode(skuId)}/subscriptions/${encode(subscriptionId)}` }, subscriptions: (skuId, options) => { - let url = `/skus/${skuId}/subscriptions?` + let url = `/skus/${encode(skuId)}/subscriptions?` if (options) { - if (options.after) url += `after=${options.after}` - if (options.before) url += `&before=${options.before}` - if (options.userId) url += `&user_id=${options.userId}` - if (options.limit) url += `&limit=${options.limit}` + if (options.after) url += `after=${encode(options.after)}` + if (options.before) url += `&before=${encode(options.before)}` + if (options.userId) url += `&user_id=${encode(options.userId)}` + if (options.limit) url += `&limit=${encode(options.limit)}` } return url @@ -642,16 +654,16 @@ export function createRoutes(): RestRoutes { soundboard: { sendSound: (channelId) => { - return `/channels/${channelId}` + return `/channels/${encode(channelId)}` }, listDefault: () => { return `/soundboard-default-sounds` }, guildSounds: (guildId) => { - return `/guilds/${guildId}/soundboard-sounds` + return `/guilds/${encode(guildId)}/soundboard-sounds` }, guildSound: (guildId, soundId) => { - return `/guilds/${guildId}/soundboard-sounds/${soundId}` + return `/guilds/${encode(guildId)}/soundboard-sounds/${encode(soundId)}` }, }, @@ -661,37 +673,37 @@ export function createRoutes(): RestRoutes { }, lobby: (lobbyId) => { - return `/lobbies/${lobbyId}` + return `/lobbies/${encode(lobbyId)}` }, member: (lobbyId, userId) => { - return `/lobbies/${lobbyId}/members/${userId}` + return `/lobbies/${encode(lobbyId)}/members/${encode(userId)}` }, leave: (lobbyId) => { - return `/lobbies/${lobbyId}/members/@me` + return `/lobbies/${encode(lobbyId)}/members/@me` }, link: (lobbyId) => { - return `/lobbies/${lobbyId}/channel-linking` + return `/lobbies/${encode(lobbyId)}/channel-linking` }, }, applicationEmoji(applicationId, emojiId) { - return `/applications/${applicationId}/emojis/${emojiId}` + return `/applications/${encode(applicationId)}/emojis/${encode(emojiId)}` }, applicationEmojis(applicationId) { - return `/applications/${applicationId}/emojis` + return `/applications/${encode(applicationId)}/emojis` }, applicationRoleConnectionMetadata(applicationId) { - return `/applications/${applicationId}/role-connections/metadata` + return `/applications/${encode(applicationId)}/role-connections/metadata` }, // User endpoints user(userId) { - return `/users/${userId}` + return `/users/${encode(userId)}` }, application() { @@ -699,7 +711,7 @@ export function createRoutes(): RestRoutes { }, applicationActivityInstance(applicationId, instanceId) { - return `/applications/${applicationId}/activity-instances/${instanceId}` + return `/applications/${encode(applicationId)}/activity-instances/${encode(instanceId)}` }, currentUser() { @@ -711,7 +723,7 @@ export function createRoutes(): RestRoutes { }, stickerPack(stickerPackId) { - return `/sticker-packs/${stickerPackId}` + return `/sticker-packs/${encode(stickerPackId)}` }, stickerPacks() { @@ -719,3 +731,10 @@ export function createRoutes(): RestRoutes { }, } } + +function encodeComponent(uriComponent: string | number | bigint | boolean): string { + if (typeof uriComponent !== 'string') return uriComponent.toString() + if (/^\d+$/.test(uriComponent)) return uriComponent + + return encodeURIComponent(uriComponent) +} diff --git a/packages/rest/src/types.ts b/packages/rest/src/types.ts index 8e505f6c1..ac00d0946 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -558,6 +558,16 @@ export interface RestManager { * @see {@link https://discord.com/developers/docs/resources/channel#create-channel-invite} */ createInvite: (channelId: BigString, options?: CreateChannelInvite, reason?: string) => Promise> + /** + * Get guild role member counts + * + * Returns a map of role IDs to the number of members with the role. Does not include the \@everyone role. + * + * @param guildId - The ID of the guild to get role member counts for. + * + * @see {@link https://discord.com/developers/docs/resources/guild#get-guild-role-member-counts} + */ + getGuildRoleMemberCounts: (guildId: BigString) => Promise> /** * Creates a role in a guild. * diff --git a/packages/rest/src/typings/routes.ts b/packages/rest/src/typings/routes.ts index 59c4c08d3..e9ea0b6c7 100644 --- a/packages/rest/src/typings/routes.ts +++ b/packages/rest/src/typings/routes.ts @@ -215,6 +215,8 @@ export interface RestRoutes { all: (guildId: BigString) => string /** Route for handling a members roles in a guild. */ member: (guildId: BigString, memberId: BigString, roleId: BigString) => string + /** Route for the role member counts */ + memberCounts: (guildId: BigString) => string } /** Route for handling a specific guild sticker. */ stickers: (guildId: BigString) => string diff --git a/packages/rest/tests/e2e/automod.spec.ts b/packages/rest/tests/e2e/automod.spec.ts index 4ed4168b2..340c2c8b0 100644 --- a/packages/rest/tests/e2e/automod.spec.ts +++ b/packages/rest/tests/e2e/automod.spec.ts @@ -1,8 +1,8 @@ import { AutoModerationActionType, AutoModerationEventTypes, AutoModerationTriggerTypes } from '@discordeno/types' import { expect } from 'chai' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' -describe('Automod tests', async () => { +describe('Automod tests', () => { it('Create a MessageSend rule for Keyword with BlockMessage action.', async () => { const rule = await rest.createAutomodRule(e2eCache.guild.id, { name: 'test', @@ -17,6 +17,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -30,8 +31,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions).to.be.exist expect(fetchedRule.actions[0]).to.be.exist expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with Timeout action.', async () => { @@ -51,6 +50,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -65,8 +65,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0]).to.be.exist expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.Timeout) expect(fetchedRule.actions[0].metadata?.durationSeconds).to.equal(10) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with BlockMessage & Timeout action.', async () => { @@ -89,6 +87,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -103,8 +102,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage) expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.Timeout) expect(fetchedRule.actions[1].metadata?.durationSeconds).to.equal(10) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) describe('with a channel', () => { @@ -125,6 +122,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -139,8 +137,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0]).to.be.exist expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.SendAlertMessage) expect(fetchedRule.actions[0].metadata?.channelId).to.equal(e2eCache.channel.id) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with SendAlertMessage & Timeout action.', async () => { @@ -166,6 +162,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -182,8 +179,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0].metadata?.channelId).to.equal(e2eCache.channel.id) expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.Timeout) expect(fetchedRule.actions[1].metadata?.durationSeconds).to.equal(10) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) it('Create a MessageSend rule for Keyword with BlockMessage & SendAlertMessage & Timeout action.', async () => { @@ -212,6 +207,7 @@ describe('Automod tests', async () => { }, ], }) + toDispose.add(async () => await rest.deleteAutomodRule(e2eCache.guild.id, rule.id)) // Get the rule again to make sure it was created correctly const fetchedRule = await rest.getAutomodRule(e2eCache.guild.id, rule.id) @@ -232,8 +228,6 @@ describe('Automod tests', async () => { expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage) expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.SendAlertMessage) expect(fetchedRule.actions[2].type).to.equal(AutoModerationActionType.Timeout) - - await rest.deleteAutomodRule(e2eCache.guild.id, rule.id) }) }) }) diff --git a/packages/rest/tests/e2e/constants.ts b/packages/rest/tests/e2e/constants.ts index 910a65a2f..fb4a97e32 100644 --- a/packages/rest/tests/e2e/constants.ts +++ b/packages/rest/tests/e2e/constants.ts @@ -1,9 +1,9 @@ import dotenv from 'dotenv' -dotenv.config({ path: '../../.env' }) +dotenv.config({ path: '../../.env', quiet: true }) -if (!process.env.DISCORD_TOKEN) throw new Error('Token was not provided.') -export const token = process.env.DISCORD_TOKEN +export const token = process.env.DISCORD_TOKEN! +if (!token) throw new Error('Token was not provided.') export const E2E_TEST_GUILD_ID = process.env.E2E_TEST_GUILD_ID! if (!E2E_TEST_GUILD_ID) throw new Error('COMMUNITY guild id was not provided.') diff --git a/packages/rest/tests/e2e/emoji.spec.ts b/packages/rest/tests/e2e/emoji.spec.ts index 2810eb6e3..15ffb9822 100644 --- a/packages/rest/tests/e2e/emoji.spec.ts +++ b/packages/rest/tests/e2e/emoji.spec.ts @@ -3,7 +3,7 @@ import { urlToBase64 } from '@discordeno/utils' import { use as chaiUse, expect } from 'chai' import chaiAsPromised from 'chai-as-promised' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' chaiUse(chaiAsPromised) @@ -15,9 +15,10 @@ describe('Create and delete emojis', () => { roles: [], }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) + // Assertions - expect(emoji.id).to.be.exist - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + expect(emoji.id).to.exist }) // delete an emoji without a reason @@ -27,11 +28,15 @@ describe('Create and delete emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], }) + const cleanEmoji = async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + toDispose.add(cleanEmoji) // Assertions - expect(emoji.id).to.be.exist + expect(emoji.id).to.exist await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + // Remove from toDispose since we already deleted it + toDispose.delete(cleanEmoji) await expect(rest.getEmoji(e2eCache.guild.id, emoji.id!)).to.eventually.rejected }) @@ -43,11 +48,15 @@ describe('Create and delete emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], }) + const cleanEmoji = async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) + toDispose.add(cleanEmoji) // Assertions - expect(emoji.id).to.be.exist + expect(emoji.id).to.exist await rest.deleteEmoji(e2eCache.guild.id, emoji.id!, 'with a reason') + // Remove from toDispose since we already deleted it + toDispose.delete(cleanEmoji) await expect(rest.getEmoji(e2eCache.guild.id, emoji.id!)).to.eventually.rejected }) @@ -62,10 +71,8 @@ describe('Edit and get emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], })) as Camelize & { id: string } - }) - afterEach(async () => { - await rest.deleteEmoji(e2eCache.guild.id, emoji.id) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id)) }) // edit an emoji name @@ -83,9 +90,8 @@ describe('Edit and get emojis', () => { const role = await rest.createRole(e2eCache.guild.id, { name: 'dd-test-emoji', }) - after(async () => { - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) + await rest.editEmoji(e2eCache.guild.id, emoji.id, { roles: [role.id], }) @@ -108,12 +114,12 @@ describe('Edit and get emojis', () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/814955268123000832.png'), roles: [], }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, newEmoji.id!)) const exists = await rest.getEmojis(e2eCache.guild.id) + expect(exists.length).to.greaterThan(1) expect(exists.find((x) => x.id === newEmoji.id)).to.exist expect(exists.find((x) => x.id === emoji.id)).to.exist - - await rest.deleteEmoji(e2eCache.guild.id, newEmoji.id!) }) }) diff --git a/packages/rest/tests/e2e/guild.spec.ts b/packages/rest/tests/e2e/guild.spec.ts index 1bb3d2f0f..a82a34cfc 100644 --- a/packages/rest/tests/e2e/guild.spec.ts +++ b/packages/rest/tests/e2e/guild.spec.ts @@ -2,11 +2,11 @@ import { ChannelTypes } from '@discordeno/types' import { use as chaiUse, expect } from 'chai' import chaiAsPromised from 'chai-as-promised' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' chaiUse(chaiAsPromised) -describe('Manage Guilds', async () => { +describe('Manage Guilds', () => { it('Get a guild', async () => { const exists = await rest.getGuild(e2eCache.guildId) expect(exists).to.be.exist @@ -20,14 +20,10 @@ describe('Manage Guilds', async () => { name: 'e2e-afk-channel', type: ChannelTypes.GuildVoice, }) + toDispose.add(async () => await rest.deleteChannel(voiceChannel.id)) expect(voiceChannel.id).to.be.exist - after(async () => { - // Clean up the AFK channel created for testing - await rest.deleteChannel(voiceChannel.id) - }) - // Set the AFK channel const edited = await rest.editGuild(e2eCache.guild.id, { afkChannelId: voiceChannel.id, @@ -65,7 +61,6 @@ describe('Manage Guilds', async () => { expect(fetchedBan).to.be.exist expect(fetchedBan.user.id).to.equal('379643682984296448') - // Assertions expect(fetchedBans).to.be.exist expect(fetchedBans.length).to.greaterThanOrEqual(2) diff --git a/packages/rest/tests/e2e/message.spec.ts b/packages/rest/tests/e2e/message.spec.ts index 723438533..8f6b34915 100644 --- a/packages/rest/tests/e2e/message.spec.ts +++ b/packages/rest/tests/e2e/message.spec.ts @@ -1,14 +1,18 @@ import { processReactionString, urlToBase64 } from '@discordeno/utils' import { expect } from 'chai' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' describe('Send a message', () => { it('With content', async () => { const message = await rest.sendMessage(e2eCache.channel.id, { content: 'testing rate limit manager' }) + expect(message.content).to.be.equal('testing rate limit manager') + const edited = await rest.editMessage(message.channelId, message.id, { content: 'testing rate limit manager edited' }) + expect(message.content).to.be.not.equal(edited.content) + await rest.deleteMessage(message.channelId, message.id) }) @@ -16,40 +20,43 @@ describe('Send a message', () => { const image = await fetch('https://cdn.discordapp.com/avatars/270010330782892032/d031ea881688526d1ae235fd2843e53c.jpg?size=2048') .then(async (res) => await res.blob()) .catch(() => undefined) + expect(image).to.not.be.undefined - if (!image) throw new Error('Was not able to fetch the image.') - const message = await rest.sendMessage(e2eCache.channel.id, { files: [{ blob: image, name: 'gamer' }] }) - expect(message.attachments.length).to.be.greaterThan(0) + + const message = await rest.sendMessage(e2eCache.channel.id, { files: [{ blob: image!, name: 'gamer' }] }) const [attachment] = message.attachments + + expect(message.attachments.length).to.be.greaterThan(0) expect(attachment.filename).to.be.equal('gamer') }) it('With a file attachment', async () => { - const txtFile = new Blob(['hello world'], { type: 'text/plain' }) const fileMsg = await rest.sendMessage(e2eCache.channel.id, { content: '222', files: [ { name: 'application.txt', - blob: txtFile, + blob: new Blob(['hello world'], { type: 'text/plain' }), }, ], }) + expect(fileMsg.id).not.equals(undefined) expect(fileMsg.content).equals('222') expect(fileMsg.attachments.length).equals(1) expect(fileMsg.attachments.at(0)?.filename).equals('application.txt') expect(fileMsg.attachments.at(0)?.size).equals(11) - const txtFile2 = new Blob(['hello world edit'], { type: 'text/plain' }) + const edited = await rest.editMessage(e2eCache.channel.id, fileMsg.id, { content: '222 edit', files: [ { name: 'application_edit.txt', - blob: txtFile2, + blob: new Blob(['hello world edit'], { type: 'text/plain' }), }, ], }) + expect(edited.id).not.equals(undefined) expect(edited.content).equals('222 edit') expect(edited.attachments.length).equals(1) @@ -58,7 +65,7 @@ describe('Send a message', () => { }) }) -describe('Manage reactions', async () => { +describe('Manage reactions', () => { it('Add and delete a unicode reaction', async () => { const message = await rest.sendMessage(e2eCache.channel.id, { content: 'add reaction test' }) await rest.addReaction(message.channelId, message.id, '📙') @@ -79,11 +86,7 @@ describe('Manage reactions', async () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/785403373817823272.webp?size=96'), roles: [], }) - - after(async () => { - // Clean up the emoji created for testing - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) - }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) const emojiCode = `<:${emoji.name!}:${emoji.id!}>` @@ -109,11 +112,7 @@ describe('Manage reactions', async () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/785403373817823272.webp?size=96'), roles: [], }) - - after(async () => { - // Clean up the emoji created for testing - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) - }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) const emojiCode = `<:${emoji.name!}:${emoji.id!}>` @@ -136,11 +135,7 @@ describe('Manage reactions', async () => { image: await urlToBase64('https://cdn.discordapp.com/emojis/785403373817823272.webp?size=96'), roles: [], }) - - after(async () => { - // Clean up the emoji created for testing - await rest.deleteEmoji(e2eCache.guild.id, emoji.id!) - }) + toDispose.add(async () => await rest.deleteEmoji(e2eCache.guild.id, emoji.id!)) const emojiCode = `<:${emoji.name!}:${emoji.id!}>` @@ -197,10 +192,7 @@ describe('Rate limit manager testing', () => { // Create 10 channels and send a message to each const promises = Array.from({ length: 10 }, async (_, i) => { const channel = await rest.createChannel(e2eCache.guild.id, { name: `rate-limit-${i}` }) - - after(async () => { - await rest.deleteChannel(channel.id) - }) + toDispose.add(async () => await rest.deleteChannel(channel.id)) const messagePromises = Array.from({ length: 10 }, async (_, j) => { await rest.sendMessage(channel.id, { content: `testing rate limit manager ${j}` }) diff --git a/packages/rest/tests/e2e/misc.spec.ts b/packages/rest/tests/e2e/misc.spec.ts index 8505c207f..d0f3e8f92 100644 --- a/packages/rest/tests/e2e/misc.spec.ts +++ b/packages/rest/tests/e2e/misc.spec.ts @@ -1,13 +1,16 @@ +import { use as chaiUse, expect } from 'chai' +import chaiAsPromised from 'chai-as-promised' import { describe } from 'mocha' import { e2eCache, rest } from './utils.js' +chaiUse(chaiAsPromised) + describe('Typings', () => { it('Trigger Typing Indication', async () => { await rest.triggerTypingIndicator(e2eCache.channel.id) }) }) -/* TODO: Add this back when bot's name is changed (https://discord.com/channels/785384884197392384/785384884197392387/1142474846811459776) describe('Commands', () => { it('Upsert global commands', async () => { await rest.upsertGlobalApplicationCommands([ @@ -33,7 +36,6 @@ describe('Commands', () => { await rest.deleteGlobalApplicationCommand(created!.id) - expect(rest.getGlobalApplicationCommand(created!.id)).to.throw + await expect(rest.getGlobalApplicationCommand(created!.id)).to.eventually.be.rejected }) }) -*/ diff --git a/packages/rest/tests/e2e/role.spec.ts b/packages/rest/tests/e2e/role.spec.ts index 82de30b45..faa33d857 100644 --- a/packages/rest/tests/e2e/role.spec.ts +++ b/packages/rest/tests/e2e/role.spec.ts @@ -1,17 +1,13 @@ import { calculateBits } from '@discordeno/utils' import { expect } from 'chai' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' -describe('Role tests', async () => { +describe('Role tests', () => { // Create a role with a reason it('Create a role with a reason', async () => { const role = await rest.createRole(e2eCache.guild?.id, { name: `test role ${Date.now()}` }, 'test reason') - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) expect(role.id).to.exist }) @@ -21,11 +17,7 @@ describe('Role tests', async () => { const role = await rest.createRole(e2eCache.guild.id, { name: `test role ${Date.now()}`, }) - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) expect(role.id).to.exist }) @@ -47,11 +39,7 @@ describe('Role tests', async () => { const role = await rest.createRole(e2eCache.guild.id, { name: `test role ${Date.now()}`, }) - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) const edited = await rest.editRole(e2eCache.guild.id, role.id, { name: 'test role 4', @@ -82,11 +70,7 @@ describe('Role tests', async () => { const role = await rest.createRole(e2eCache.guild.id, { name: `test role ${Date.now()}`, }) - - after(async () => { - // Clean up the role created for testing - await rest.deleteRole(e2eCache.guild.id, role.id) - }) + toDispose.add(async () => await rest.deleteRole(e2eCache.guild.id, role.id)) // Assign the role to the user await rest.addRole(e2eCache.guild.id, rest.applicationId, role.id) diff --git a/packages/rest/tests/e2e/stickers.spec.ts b/packages/rest/tests/e2e/stickers.spec.ts index a36566577..6eeb3b3be 100644 --- a/packages/rest/tests/e2e/stickers.spec.ts +++ b/packages/rest/tests/e2e/stickers.spec.ts @@ -2,12 +2,12 @@ import { StickerFormatTypes } from '@discordeno/types' import { use as chaiUse, expect } from 'chai' import chaiAsPromised from 'chai-as-promised' import { describe, it } from 'mocha' -import { e2eCache, rest } from './utils.js' +import { e2eCache, rest, toDispose } from './utils.js' chaiUse(chaiAsPromised) // waiting for channel -describe('Sticker tests', async () => { +describe('Sticker tests', () => { it('Can get a sticker', async () => { const sticker = await rest.getSticker(749054660769218631n) expect(sticker.name).to.equal('Wave') @@ -23,6 +23,10 @@ describe('Sticker tests', async () => { name: 'ddlogo.png', }, }) + const cleanSticker = async () => await rest.deleteGuildSticker(e2eCache.guild.id, sticker.id) + + // We may remove this as we also test sticker deletion at the end of the test, so we need a var to reference the function + toDispose.add(cleanSticker) expect(sticker.name).to.equal('sticker name') expect(sticker.description).to.equal('sticker description') @@ -62,15 +66,15 @@ describe('Sticker tests', async () => { }, }) - after(async () => { - // Clean up the sticker created for testing - await rest.deleteGuildSticker(e2eCache.guild.id, sticker2.id) - }) + toDispose.add(async () => await rest.deleteGuildSticker(e2eCache.guild.id, sticker2.id)) const stickers = await rest.getGuildStickers(e2eCache.guild.id) expect(stickers.length).to.greaterThan(1) await rest.deleteGuildSticker(e2eCache.guild.id, sticker.id) + // Since we have already deleted the sticker, we can remove it from the toDispose set + toDispose.delete(cleanSticker) + await expect(rest.getGuildSticker(e2eCache.guild.id, sticker.id)).to.eventually.rejected }) }) diff --git a/packages/rest/tests/e2e/utils.ts b/packages/rest/tests/e2e/utils.ts index 82ca8b25b..f6487baca 100644 --- a/packages/rest/tests/e2e/utils.ts +++ b/packages/rest/tests/e2e/utils.ts @@ -3,7 +3,6 @@ import { createRestManager } from '../../src/manager.js' import { E2E_TEST_GUILD_ID, token } from './constants.js' // For debugging purposes // logger.setLevel(LogLevels.Debug) -// logger.setDepth(LogDepth.Full) export const rest = createRestManager({ token, @@ -29,3 +28,25 @@ export const e2eCache = { guild, channel, } + +// Some resources created during tests need to be disposed of afterwards, as they will persist otherwise (e.g., emojis, roles, automod rules) +export const toDispose = new Set<() => Promise>() + +afterEach(async () => { + let aggregateError: AggregateError | null = null + + for (const dispose of toDispose) { + try { + await dispose() + } catch (error) { + console.error('Error during cleanup:', error) + + aggregateError ??= new AggregateError([], 'Errors occurred during cleanup') + aggregateError.errors.push(error) + } + } + + toDispose.clear() + + if (aggregateError) throw aggregateError +}) diff --git a/packages/rest/tests/e2e/webhook.spec.ts b/packages/rest/tests/e2e/webhook.spec.ts index 3175af6ec..654452275 100644 --- a/packages/rest/tests/e2e/webhook.spec.ts +++ b/packages/rest/tests/e2e/webhook.spec.ts @@ -5,7 +5,7 @@ import { e2eCache, rest } from './utils.js' chaiUse(chaiAsPromised) -describe('Webhook helpers', async () => { +describe('Webhook helpers', () => { it('Manage webhooks', async () => { const webhook = await rest.createWebhook(e2eCache.channel.id, { name: 'idk', diff --git a/packages/types/package.json b/packages/types/package.json index 19896701e..0345bae7a 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -17,19 +17,18 @@ "build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist", "release-build": "yarn build && yarn build:type", "check": "biome check --write", - "test:unit-coverage": "c8 mocha --no-warnings 'tests/**/*.spec.ts'", - "test:unit": "c8 --r lcov mocha --no-warnings 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js utils", - "test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/**/*.spec.ts'", - "test:deno-unit": "deno -A ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/**/*.spec.ts'", - "test:bun-unit": "bun ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/**/*.spec.ts'", + "test:unit": "c8 -r lcov mocha 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js types", + "test:deno-unit": "deno -A npm:mocha 'tests/**/*.spec.ts'", + "test:bun-unit": "bun --bun mocha 'tests/**/*.spec.ts'", + "test:test-type": "tsc --project tests/tsconfig.json", "test:type": "tsc --noEmit" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.7", + "@swc/core": "^1.15.8", "@types/mocha": "^10.0.10", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "c8": "^10.1.3", "mocha": "^11.7.5", "ts-node": "^10.9.2", diff --git a/packages/types/src/discord/permissions.ts b/packages/types/src/discord/permissions.ts index 978f61dfa..bc6b8c8f0 100644 --- a/packages/types/src/discord/permissions.ts +++ b/packages/types/src/discord/permissions.ts @@ -102,6 +102,8 @@ export const BitwisePermissionFlags = { USE_EXTERNAL_APPS: 1n << 50n, /** Allows pinning and unpinning messages */ PIN_MESSAGES: 1n << 51n, + /** Allows bypassing slowmode restrictions */ + BYPASS_SLOWMODE: 1n << 52n, } as const /** https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags */ diff --git a/packages/types/src/discord/webhookEvents.ts b/packages/types/src/discord/webhookEvents.ts index c0860e2c6..718a82037 100644 --- a/packages/types/src/discord/webhookEvents.ts +++ b/packages/types/src/discord/webhookEvents.ts @@ -39,6 +39,8 @@ export interface DiscordEventWebhookEventBody { | DiscordEventWebhookApplicationAuthorizedBody | DiscordEventWebhookApplicationDeauthorizedBody | DiscordEventWebhookEntitlementCreateBody + | DiscordEventWebhookEntitlementUpdateBody + | DiscordEventWebhookEntitlementDeleteBody | DiscordEventWebhookLobbyMessageCreateBody | DiscordEventWebhookLobbyMessageUpdateBody | DiscordEventWebhookLobbyMessageDeleteBody @@ -55,6 +57,10 @@ export enum DiscordWebhookEventType { ApplicationDeauthorized = 'APPLICATION_DEAUTHORIZED', /** Entitlement was created */ EntitlementCreate = 'ENTITLEMENT_CREATE', + /** Entitlement was updated */ + EntitlementUpdate = 'ENTITLEMENT_UPDATE', + /** Entitlement was deleted */ + EntitlementDelete = 'ENTITLEMENT_DELETE', /** User was added to a Quest (currently unavailable) */ QuestUserEnrollment = 'QUEST_USER_ENROLLMENT', /** Sent when a message is created in a lobby */ @@ -92,6 +98,12 @@ export interface DiscordEventWebhookApplicationDeauthorizedBody { /** https://discord.com/developers/docs/events/webhook-events#entitlement-create-entitlement-create-structure */ export type DiscordEventWebhookEntitlementCreateBody = DiscordEntitlement +/** https://discord.com/developers/docs/events/webhook-events#entitlement-update-entitlement-update-structure */ +export type DiscordEventWebhookEntitlementUpdateBody = DiscordEntitlement + +/** https://discord.com/developers/docs/events/webhook-events#entitlement-delete-entitlement-delete-structure */ +export type DiscordEventWebhookEntitlementDeleteBody = DiscordEntitlement + /** https://discord.com/developers/docs/events/webhook-events#lobby-message-create-lobby-message-create-structure */ export type DiscordEventWebhookLobbyMessageCreateBody = DiscordSocialSDKLobbyMessage diff --git a/packages/utils/package.json b/packages/utils/package.json index 1f797b854..8133969a3 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -17,24 +17,22 @@ "build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist", "release-build": "yarn build && yarn build:type", "check": "biome check --write", - "test:unit-coverage": "c8 mocha --no-warnings 'tests/**/*.spec.ts'", - "test:unit": "c8 --r lcov mocha --no-warnings 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js utils", - "test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/**/*.spec.ts'", - "test:deno-unit": "deno -A ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/**/*.spec.ts'", - "test:bun-unit": "bun ../../node_modules/mocha/bin/mocha.js --config ../../.mocharc.base.cjs 'tests/**/*.spec.ts'", - "test:type": "tsc --noEmit", - "test:test-type": "tsc --project tests/tsconfig.json" + "test:unit": "c8 -r lcov mocha 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js utils", + "test:deno-unit": "deno -A npm:mocha 'tests/**/*.spec.ts'", + "test:bun-unit": "bun --bun mocha 'tests/**/*.spec.ts'", + "test:test-type": "tsc --project tests/tsconfig.json", + "test:type": "tsc --noEmit" }, "dependencies": { "@discordeno/types": "workspace:^" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.7", + "@swc/core": "^1.15.8", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", - "@types/node": "^25.0.3", + "@types/node": "^25.0.6", "@types/sinon": "^21.0.0", "c8": "^10.1.3", "chai": "^6.2.2", diff --git a/turbo.json b/turbo.json index 48e5c9456..a9c2c53b9 100644 --- a/turbo.json +++ b/turbo.json @@ -2,42 +2,61 @@ "$schema": "https://turbo.build/schema.json", "tasks": { "build": { - "outputs": ["dist/**/*.js"], - "inputs": ["src/**/*.ts"] + "inputs": ["src/**/*.ts"], + "outputs": ["dist/**/*.js"] }, "build:type": { "dependsOn": ["^build:type"], + "inputs": ["src/**/*.ts"], "outputs": ["dist/**/*.d.ts", "dist/**/*.d.ts.map", "dist/tsconfig.tsbuildinfo"] }, - "test:unit-coverage": { - "dependsOn": ["^build"], - "outputs": ["coverage/**"] - }, - "test:unit": { - "dependsOn": ["^build", "build"], - "outputs": ["coverage/**"] - }, - "test:integration": { - "dependsOn": ["^build"], - "outputs": ["coverage/**"] - }, - "test:test-type": { - "dependsOn": ["^build:type"] - }, - "test:deno-unit": { - "dependsOn": ["^build", "build"] - }, - "test:bun-unit": { - "dependsOn": ["^build", "build"] - }, - "test:e2e": { - "dependsOn": ["^build"], - "outputs": ["coverage/**"], - "env": ["DISCORD_TOKEN", "E2E_TEST_GUILD_ID"] - }, "check": { "dependsOn": ["build:type"], "cache": false + }, + "test:unit": { + "dependsOn": ["^build", "build"], + // We need to exclude e2e and integration test manually since when there aren't e2e or integration we don't have a unit folder + "inputs": ["src/**/*.ts", "tests/**/*.spec.ts", "!tests/e2e/**/*.spec.ts", "!tests/integration**/*.spec.ts"], + "outputs": ["coverage/**"] + }, + "test:deno-unit": { + "dependsOn": ["^build", "build"], + // We need to exclude e2e and integration test manually since when there aren't e2e or integration we don't have a unit folder + "inputs": ["src/**/*.ts", "tests/**/*.spec.ts", "!tests/e2e/**/*.spec.ts", "!tests/integration**/*.spec.ts"] + }, + "test:bun-unit": { + "dependsOn": ["^build", "build"], + // We need to exclude e2e and integration test manually since when there aren't e2e or integration we don't have a unit folder + "inputs": ["src/**/*.ts", "tests/**/*.spec.ts", "!tests/e2e/**/*.spec.ts", "!tests/integration**/*.spec.ts"] + }, + "test:integration": { + "dependsOn": ["^build"], + "inputs": ["src/**/*.ts", "tests/integration**/*.spec.ts"], + "outputs": ["coverage/**"] + }, + "test:e2e": { + "dependsOn": ["^build"], + "inputs": ["src/**/*.ts", "tests/e2e**/*.spec.ts"], + "outputs": ["coverage/**"], + "env": ["DISCORD_TOKEN", "E2E_TEST_GUILD_ID"] + }, + "test:test-type": { + "dependsOn": ["^build:type"], + "inputs": ["src/**/*.ts", "tests/**/*.ts"] + }, + "test:type": { + "dependsOn": ["^build:type"], + "inputs": ["src/**/*.ts"] + }, + "check": { + "dependsOn": ["build:type"], + "cache": false + }, + "build:doc": { + // NOTE: This task can only be run with "--singe-package" in the flags to turbo, as it is defined only in the root package.json + "inputs": ["packages/**/src/**/*.ts"], + "outputs": ["website/api_reference/generated/**/*"] } } } diff --git a/website/docs/bigbot/step-2-rest.md b/website/docs/bigbot/step-2-rest.md index 8b14a80ea..737d8f8f5 100644 --- a/website/docs/bigbot/step-2-rest.md +++ b/website/docs/bigbot/step-2-rest.md @@ -124,7 +124,7 @@ export const influxDB = : undefined export const Influx = influxDB?.getWriteApi(INFLUX_ORG, INFLUX_BUCKET) -let savingAnalyticsId: NodeJS.Interval | undefined = undefined +let saveAnalyticsId: NodeJS.Interval | undefined = undefined if (!saveAnalyticsId) { setInterval(() => { console.log(`[Influx - REST] Saving events...`) diff --git a/website/docs/bigbot/step-3-gateway.md b/website/docs/bigbot/step-3-gateway.md index 25907527e..f1692f972 100644 --- a/website/docs/bigbot/step-3-gateway.md +++ b/website/docs/bigbot/step-3-gateway.md @@ -426,7 +426,7 @@ export const influxDB = : undefined export const Influx = influxDB?.getWriteApi(INFLUX_ORG, INFLUX_BUCKET) -let savingAnalyticsId: NodeJS.Interval | undefined = undefined +let saveAnalyticsId: NodeJS.Interval | undefined = undefined if (!saveAnalyticsId) { setInterval(() => { console.log(`[Influx - Gateway] Saving events...`) diff --git a/website/package.json b/website/package.json index 898ded332..ece6c8a20 100644 --- a/website/package.json +++ b/website/package.json @@ -1,6 +1,7 @@ { "name": "website", "version": "0.0.0", + "packageManager": "yarn@4.12.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -30,11 +31,12 @@ "react-dom": "^19.2.3" }, "devDependencies": { - "@biomejs/biome": "2.3.10", + "@biomejs/biome": "2.3.11", "@docusaurus/module-type-aliases": "^3.9.2", "@docusaurus/tsconfig": "^3.9.2", "@docusaurus/types": "^3.9.2", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", + "turbo": "^2.7.4", "typescript": "5.9.3", "webpack": "5.104.1" }, diff --git a/website/turbo.json b/website/turbo.json new file mode 100644 index 000000000..8130fde5d --- /dev/null +++ b/website/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "outputs": ["build/**"], + "inputs": ["src/**/*", "static/**/*", "blog/**/*", "docs/**/*", "api_reference/**/*", "docusaurus.config.ts", "sidebars.ts"] + } + } +} diff --git a/website/yarn.lock b/website/yarn.lock index 6e23dc075..9e1374bf8 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -5361,18 +5361,18 @@ __metadata: languageName: node linkType: hard -"@biomejs/biome@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/biome@npm:2.3.10" +"@biomejs/biome@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/biome@npm:2.3.11" dependencies: - "@biomejs/cli-darwin-arm64": "npm:2.3.10" - "@biomejs/cli-darwin-x64": "npm:2.3.10" - "@biomejs/cli-linux-arm64": "npm:2.3.10" - "@biomejs/cli-linux-arm64-musl": "npm:2.3.10" - "@biomejs/cli-linux-x64": "npm:2.3.10" - "@biomejs/cli-linux-x64-musl": "npm:2.3.10" - "@biomejs/cli-win32-arm64": "npm:2.3.10" - "@biomejs/cli-win32-x64": "npm:2.3.10" + "@biomejs/cli-darwin-arm64": "npm:2.3.11" + "@biomejs/cli-darwin-x64": "npm:2.3.11" + "@biomejs/cli-linux-arm64": "npm:2.3.11" + "@biomejs/cli-linux-arm64-musl": "npm:2.3.11" + "@biomejs/cli-linux-x64": "npm:2.3.11" + "@biomejs/cli-linux-x64-musl": "npm:2.3.11" + "@biomejs/cli-win32-arm64": "npm:2.3.11" + "@biomejs/cli-win32-x64": "npm:2.3.11" dependenciesMeta: "@biomejs/cli-darwin-arm64": optional: true @@ -5392,62 +5392,62 @@ __metadata: optional: true bin: biome: bin/biome - checksum: 10c0/317bf8f8051f4030dc52855159bbd058db5cf2c9d5e7b212271f3d91842e0fc5bbc05495e47943676481325d4081cd41cf25dfa18a437d2b6e27f72dacfd41b4 + checksum: 10c0/b9764070c3d1583466a8861d37dc480c18103f7bb52115db0f265a38e6343d69792c9beea094e0b3db0905cb365b9a82ad2a0f3f05b7f04873a8f9b444263140 languageName: node linkType: hard -"@biomejs/cli-darwin-arm64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-darwin-arm64@npm:2.3.10" +"@biomejs/cli-darwin-arm64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-darwin-arm64@npm:2.3.11" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@biomejs/cli-darwin-x64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-darwin-x64@npm:2.3.10" +"@biomejs/cli-darwin-x64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-darwin-x64@npm:2.3.11" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@biomejs/cli-linux-arm64-musl@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-arm64-musl@npm:2.3.10" +"@biomejs/cli-linux-arm64-musl@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-arm64-musl@npm:2.3.11" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@biomejs/cli-linux-arm64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-arm64@npm:2.3.10" +"@biomejs/cli-linux-arm64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-arm64@npm:2.3.11" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@biomejs/cli-linux-x64-musl@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-x64-musl@npm:2.3.10" +"@biomejs/cli-linux-x64-musl@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-x64-musl@npm:2.3.11" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@biomejs/cli-linux-x64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-x64@npm:2.3.10" +"@biomejs/cli-linux-x64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-x64@npm:2.3.11" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@biomejs/cli-win32-arm64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-win32-arm64@npm:2.3.10" +"@biomejs/cli-win32-arm64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-win32-arm64@npm:2.3.11" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@biomejs/cli-win32-x64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-win32-x64@npm:2.3.10" +"@biomejs/cli-win32-x64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-win32-x64@npm:2.3.11" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -19935,6 +19935,77 @@ __metadata: languageName: node linkType: hard +"turbo-darwin-64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-darwin-64@npm:2.7.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"turbo-darwin-arm64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-darwin-arm64@npm:2.7.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"turbo-linux-64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-linux-64@npm:2.7.4" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"turbo-linux-arm64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-linux-arm64@npm:2.7.4" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"turbo-windows-64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-windows-64@npm:2.7.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"turbo-windows-arm64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-windows-arm64@npm:2.7.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"turbo@npm:^2.7.4": + version: 2.7.4 + resolution: "turbo@npm:2.7.4" + dependencies: + turbo-darwin-64: "npm:2.7.4" + turbo-darwin-arm64: "npm:2.7.4" + turbo-linux-64: "npm:2.7.4" + turbo-linux-arm64: "npm:2.7.4" + turbo-windows-64: "npm:2.7.4" + turbo-windows-arm64: "npm:2.7.4" + dependenciesMeta: + turbo-darwin-64: + optional: true + turbo-darwin-arm64: + optional: true + turbo-linux-64: + optional: true + turbo-linux-arm64: + optional: true + turbo-windows-64: + optional: true + turbo-windows-arm64: + optional: true + bin: + turbo: bin/turbo + checksum: 10c0/506a59df8d898833efb7d41b552a3a448a26fb2ebd5d545d37bf1e4854bdab0a0b847d8d82f962f499ba5340b7dc123abb5d4ceb9dae12b32ec6c46d1f5fe6fc + languageName: node + linkType: hard + "type-fest@npm:^0.21.3": version: 0.21.3 resolution: "type-fest@npm:0.21.3" @@ -19996,9 +20067,9 @@ __metadata: linkType: hard "undici@npm:^6.19.5": - version: 6.21.3 - resolution: "undici@npm:6.21.3" - checksum: 10c0/294da109853fad7a6ef5a172ad0ca3fb3f1f60cf34703d062a5ec967daf69ad8c03b52e6d536c5cba3bb65615769bf08e5b30798915cbccdddaca01045173dda + version: 6.23.0 + resolution: "undici@npm:6.23.0" + checksum: 10c0/d846b3fdfd05aa6081ba1eab5db6bbc21b283042c7a43722b86d1ee2bf749d7c990ceac0c809f9a07ffd88b1b0f4c0f548a8362c035088cb1997d63abdda499c languageName: node linkType: hard @@ -20786,7 +20857,7 @@ __metadata: version: 0.0.0-use.local resolution: "website@workspace:." dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@docusaurus/core": "npm:^3.9.2" "@docusaurus/faster": "npm:^3.9.2" "@docusaurus/module-type-aliases": "npm:^3.9.2" @@ -20798,11 +20869,12 @@ __metadata: "@docusaurus/types": "npm:^3.9.2" "@easyops-cn/docusaurus-search-local": "npm:^0.52.1" "@mdx-js/react": "npm:^3.1.1" - "@types/react": "npm:^19.2.7" + "@types/react": "npm:^19.2.8" "@xyflow/react": "npm:^12.10.0" prism-react-renderer: "npm:^2.4.1" react: "npm:^19.2.3" react-dom: "npm:^19.2.3" + turbo: "npm:^2.7.4" typescript: "npm:5.9.3" webpack: "npm:5.104.1" languageName: unknown diff --git a/yarn.lock b/yarn.lock index 71f85ad9a..b5be889b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,18 +12,18 @@ __metadata: languageName: node linkType: hard -"@biomejs/biome@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/biome@npm:2.3.10" +"@biomejs/biome@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/biome@npm:2.3.11" dependencies: - "@biomejs/cli-darwin-arm64": "npm:2.3.10" - "@biomejs/cli-darwin-x64": "npm:2.3.10" - "@biomejs/cli-linux-arm64": "npm:2.3.10" - "@biomejs/cli-linux-arm64-musl": "npm:2.3.10" - "@biomejs/cli-linux-x64": "npm:2.3.10" - "@biomejs/cli-linux-x64-musl": "npm:2.3.10" - "@biomejs/cli-win32-arm64": "npm:2.3.10" - "@biomejs/cli-win32-x64": "npm:2.3.10" + "@biomejs/cli-darwin-arm64": "npm:2.3.11" + "@biomejs/cli-darwin-x64": "npm:2.3.11" + "@biomejs/cli-linux-arm64": "npm:2.3.11" + "@biomejs/cli-linux-arm64-musl": "npm:2.3.11" + "@biomejs/cli-linux-x64": "npm:2.3.11" + "@biomejs/cli-linux-x64-musl": "npm:2.3.11" + "@biomejs/cli-win32-arm64": "npm:2.3.11" + "@biomejs/cli-win32-x64": "npm:2.3.11" dependenciesMeta: "@biomejs/cli-darwin-arm64": optional: true @@ -43,62 +43,62 @@ __metadata: optional: true bin: biome: bin/biome - checksum: 10c0/317bf8f8051f4030dc52855159bbd058db5cf2c9d5e7b212271f3d91842e0fc5bbc05495e47943676481325d4081cd41cf25dfa18a437d2b6e27f72dacfd41b4 + checksum: 10c0/b9764070c3d1583466a8861d37dc480c18103f7bb52115db0f265a38e6343d69792c9beea094e0b3db0905cb365b9a82ad2a0f3f05b7f04873a8f9b444263140 languageName: node linkType: hard -"@biomejs/cli-darwin-arm64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-darwin-arm64@npm:2.3.10" +"@biomejs/cli-darwin-arm64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-darwin-arm64@npm:2.3.11" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@biomejs/cli-darwin-x64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-darwin-x64@npm:2.3.10" +"@biomejs/cli-darwin-x64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-darwin-x64@npm:2.3.11" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@biomejs/cli-linux-arm64-musl@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-arm64-musl@npm:2.3.10" +"@biomejs/cli-linux-arm64-musl@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-arm64-musl@npm:2.3.11" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@biomejs/cli-linux-arm64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-arm64@npm:2.3.10" +"@biomejs/cli-linux-arm64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-arm64@npm:2.3.11" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@biomejs/cli-linux-x64-musl@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-x64-musl@npm:2.3.10" +"@biomejs/cli-linux-x64-musl@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-x64-musl@npm:2.3.11" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@biomejs/cli-linux-x64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-linux-x64@npm:2.3.10" +"@biomejs/cli-linux-x64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-linux-x64@npm:2.3.11" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@biomejs/cli-win32-arm64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-win32-arm64@npm:2.3.10" +"@biomejs/cli-win32-arm64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-win32-arm64@npm:2.3.11" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@biomejs/cli-win32-x64@npm:2.3.10": - version: 2.3.10 - resolution: "@biomejs/cli-win32-x64@npm:2.3.10" +"@biomejs/cli-win32-x64@npm:2.3.11": + version: 2.3.11 + resolution: "@biomejs/cli-win32-x64@npm:2.3.11" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -116,16 +116,16 @@ __metadata: version: 0.0.0-use.local resolution: "@discordeno/bot@workspace:packages/bot" dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@discordeno/gateway": "workspace:^" "@discordeno/rest": "workspace:^" "@discordeno/types": "workspace:^" "@discordeno/utils": "workspace:^" "@swc/cli": "npm:^0.7.9" - "@swc/core": "npm:^1.15.7" + "@swc/core": "npm:^1.15.8" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" "@types/sinon": "npm:^21.0.0" c8: "npm:^10.1.3" chai: "npm:^6.2.2" @@ -141,14 +141,14 @@ __metadata: version: 0.0.0-use.local resolution: "@discordeno/gateway@workspace:packages/gateway" dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@discordeno/types": "workspace:^" "@discordeno/utils": "workspace:^" "@swc/cli": "npm:^0.7.9" - "@swc/core": "npm:^1.15.7" + "@swc/core": "npm:^1.15.8" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" "@types/sinon": "npm:^21.0.0" "@types/ws": "npm:^8.18.1" c8: "npm:^10.1.3" @@ -159,7 +159,7 @@ __metadata: ts-node: "npm:^10.9.2" tsconfig: "npm:*" typescript: "npm:^5.9.3" - ws: "npm:^8.18.3" + ws: "npm:^8.19.0" dependenciesMeta: fzstd: optional: true @@ -170,15 +170,15 @@ __metadata: version: 0.0.0-use.local resolution: "@discordeno/rest@workspace:packages/rest" dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@discordeno/types": "workspace:^" "@discordeno/utils": "workspace:^" "@swc/cli": "npm:^0.7.9" - "@swc/core": "npm:^1.15.7" + "@swc/core": "npm:^1.15.8" "@types/chai": "npm:^5.2.3" "@types/chai-as-promised": "npm:^8.0.2" "@types/mocha": "npm:^10.0.10" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" "@types/sinon": "npm:^21.0.0" c8: "npm:^10.1.3" chai: "npm:^6.2.2" @@ -196,11 +196,11 @@ __metadata: version: 0.0.0-use.local resolution: "@discordeno/types@workspace:packages/types" dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@swc/cli": "npm:^0.7.9" - "@swc/core": "npm:^1.15.7" + "@swc/core": "npm:^1.15.8" "@types/mocha": "npm:^10.0.10" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" c8: "npm:^10.1.3" mocha: "npm:^11.7.5" ts-node: "npm:^10.9.2" @@ -213,13 +213,13 @@ __metadata: version: 0.0.0-use.local resolution: "@discordeno/utils@workspace:packages/utils" dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@discordeno/types": "workspace:^" "@swc/cli": "npm:^0.7.9" - "@swc/core": "npm:^1.15.7" + "@swc/core": "npm:^1.15.8" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" "@types/sinon": "npm:^21.0.0" c8: "npm:^10.1.3" chai: "npm:^6.2.2" @@ -589,90 +589,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-darwin-arm64@npm:1.15.7" +"@swc/core-darwin-arm64@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-darwin-arm64@npm:1.15.8" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-darwin-x64@npm:1.15.7" +"@swc/core-darwin-x64@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-darwin-x64@npm:1.15.8" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.15.7" +"@swc/core-linux-arm-gnueabihf@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.15.8" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-linux-arm64-gnu@npm:1.15.7" +"@swc/core-linux-arm64-gnu@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-arm64-gnu@npm:1.15.8" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-linux-arm64-musl@npm:1.15.7" +"@swc/core-linux-arm64-musl@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-arm64-musl@npm:1.15.8" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-linux-x64-gnu@npm:1.15.7" +"@swc/core-linux-x64-gnu@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-x64-gnu@npm:1.15.8" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-linux-x64-musl@npm:1.15.7" +"@swc/core-linux-x64-musl@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-x64-musl@npm:1.15.8" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-win32-arm64-msvc@npm:1.15.7" +"@swc/core-win32-arm64-msvc@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-win32-arm64-msvc@npm:1.15.8" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-win32-ia32-msvc@npm:1.15.7" +"@swc/core-win32-ia32-msvc@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-win32-ia32-msvc@npm:1.15.8" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.15.7": - version: 1.15.7 - resolution: "@swc/core-win32-x64-msvc@npm:1.15.7" +"@swc/core-win32-x64-msvc@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-win32-x64-msvc@npm:1.15.8" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:^1.15.7": - version: 1.15.7 - resolution: "@swc/core@npm:1.15.7" +"@swc/core@npm:^1.15.8": + version: 1.15.8 + resolution: "@swc/core@npm:1.15.8" dependencies: - "@swc/core-darwin-arm64": "npm:1.15.7" - "@swc/core-darwin-x64": "npm:1.15.7" - "@swc/core-linux-arm-gnueabihf": "npm:1.15.7" - "@swc/core-linux-arm64-gnu": "npm:1.15.7" - "@swc/core-linux-arm64-musl": "npm:1.15.7" - "@swc/core-linux-x64-gnu": "npm:1.15.7" - "@swc/core-linux-x64-musl": "npm:1.15.7" - "@swc/core-win32-arm64-msvc": "npm:1.15.7" - "@swc/core-win32-ia32-msvc": "npm:1.15.7" - "@swc/core-win32-x64-msvc": "npm:1.15.7" + "@swc/core-darwin-arm64": "npm:1.15.8" + "@swc/core-darwin-x64": "npm:1.15.8" + "@swc/core-linux-arm-gnueabihf": "npm:1.15.8" + "@swc/core-linux-arm64-gnu": "npm:1.15.8" + "@swc/core-linux-arm64-musl": "npm:1.15.8" + "@swc/core-linux-x64-gnu": "npm:1.15.8" + "@swc/core-linux-x64-musl": "npm:1.15.8" + "@swc/core-win32-arm64-msvc": "npm:1.15.8" + "@swc/core-win32-ia32-msvc": "npm:1.15.8" + "@swc/core-win32-x64-msvc": "npm:1.15.8" "@swc/counter": "npm:^0.1.3" "@swc/types": "npm:^0.1.25" peerDependencies: @@ -701,7 +701,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10c0/ae1d20db777bfe0404f2c840d8fa66da0664bca1f88ac8727cbe4c0cb04ee28505009b55caf345f1e790b96410f9bcf66d0178372e9c6505a9da76e7562a06bf + checksum: 10c0/929f334a224776fdb3c4a8aaba68f07666ff56fae7502a9459bc9666cb73d94e65f042ce8c4ef4e6746a8bb3f8255cbe8599bef6e3181269caf761c8e55513cf languageName: node linkType: hard @@ -835,12 +835,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^25.0.3": - version: 25.0.3 - resolution: "@types/node@npm:25.0.3" +"@types/node@npm:^25.0.6": + version: 25.0.6 + resolution: "@types/node@npm:25.0.6" dependencies: undici-types: "npm:~7.16.0" - checksum: 10c0/b7568f0d765d9469621615e2bb257c7fd1953d95e9acbdb58dffb6627a2c4150d405a4600aa1ad8a40182a94fe5f903cafd3c0a2f5132814debd0e3bfd61f835 + checksum: 10c0/4ee3b6b606bc28f6840c04d58769e1e75dd8085149908956c874c85f8f8c890990701bcbe783fc2d14017817861dc07fcf23de6fc2805fe491f84eae717b5705 languageName: node linkType: hard @@ -1442,11 +1442,11 @@ __metadata: version: 0.0.0-use.local resolution: "discordeno-monorepo@workspace:." dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" husky: "npm:^9.1.7" lint-staged: "npm:^16.2.7" - turbo: "npm:^2.7.2" - typedoc: "npm:^0.28.15" + turbo: "npm:^2.7.4" + typedoc: "npm:^0.28.16" typedoc-plugin-markdown: "npm:^4.9.0" typescript: "npm:^5.9.3" languageName: unknown @@ -1456,17 +1456,17 @@ __metadata: version: 0.0.0-use.local resolution: "discordeno@workspace:packages/discordeno" dependencies: - "@biomejs/biome": "npm:2.3.10" + "@biomejs/biome": "npm:2.3.11" "@discordeno/bot": "workspace:^" "@discordeno/gateway": "workspace:^" "@discordeno/rest": "workspace:^" "@discordeno/types": "workspace:^" "@discordeno/utils": "workspace:^" "@swc/cli": "npm:^0.7.9" - "@swc/core": "npm:^1.15.7" + "@swc/core": "npm:^1.15.8" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" - "@types/node": "npm:^25.0.3" + "@types/node": "npm:^25.0.6" c8: "npm:^10.1.3" chai: "npm:^6.2.2" mocha: "npm:^11.7.5" @@ -2917,58 +2917,58 @@ __metadata: languageName: unknown linkType: soft -"turbo-darwin-64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-darwin-64@npm:2.7.2" +"turbo-darwin-64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-darwin-64@npm:2.7.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-darwin-arm64@npm:2.7.2" +"turbo-darwin-arm64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-darwin-arm64@npm:2.7.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-linux-64@npm:2.7.2" +"turbo-linux-64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-linux-64@npm:2.7.4" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-linux-arm64@npm:2.7.2" +"turbo-linux-arm64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-linux-arm64@npm:2.7.4" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-windows-64@npm:2.7.2" +"turbo-windows-64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-windows-64@npm:2.7.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:2.7.2": - version: 2.7.2 - resolution: "turbo-windows-arm64@npm:2.7.2" +"turbo-windows-arm64@npm:2.7.4": + version: 2.7.4 + resolution: "turbo-windows-arm64@npm:2.7.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"turbo@npm:^2.7.2": - version: 2.7.2 - resolution: "turbo@npm:2.7.2" +"turbo@npm:^2.7.4": + version: 2.7.4 + resolution: "turbo@npm:2.7.4" dependencies: - turbo-darwin-64: "npm:2.7.2" - turbo-darwin-arm64: "npm:2.7.2" - turbo-linux-64: "npm:2.7.2" - turbo-linux-arm64: "npm:2.7.2" - turbo-windows-64: "npm:2.7.2" - turbo-windows-arm64: "npm:2.7.2" + turbo-darwin-64: "npm:2.7.4" + turbo-darwin-arm64: "npm:2.7.4" + turbo-linux-64: "npm:2.7.4" + turbo-linux-arm64: "npm:2.7.4" + turbo-windows-64: "npm:2.7.4" + turbo-windows-arm64: "npm:2.7.4" dependenciesMeta: turbo-darwin-64: optional: true @@ -2984,7 +2984,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 10c0/390937a32070ac52120af56705006e831de2edf8e22a2b0146006d07740cd413b3a36335339e7617b57cf74b008f4b15931cf56e1c7c0c03808f028ed937c4de + checksum: 10c0/506a59df8d898833efb7d41b552a3a448a26fb2ebd5d545d37bf1e4854bdab0a0b847d8d82f962f499ba5340b7dc123abb5d4ceb9dae12b32ec6c46d1f5fe6fc languageName: node linkType: hard @@ -3011,9 +3011,9 @@ __metadata: languageName: node linkType: hard -"typedoc@npm:^0.28.15": - version: 0.28.15 - resolution: "typedoc@npm:0.28.15" +"typedoc@npm:^0.28.16": + version: 0.28.16 + resolution: "typedoc@npm:0.28.16" dependencies: "@gerrit0/mini-shiki": "npm:^3.17.0" lunr: "npm:^2.3.9" @@ -3024,7 +3024,7 @@ __metadata: typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x bin: typedoc: bin/typedoc - checksum: 10c0/b5988ebebb367fed44f110bbd37baee85fe95fe10c8d5a511c33d787eb1e924e66ba54cb0763d63ed2c406adbd32fcb87bcbc3fd61b0bc8ab6f3a6c06f2de978 + checksum: 10c0/ae444913068088e88be6319a017a3a18f69cbd91dbb5b959fbdd0cf87d1a2a07f3a0d4ab29c957a83dd72808ff35bdd6ceec3ad1803fa412ddceffb78fa60ebb languageName: node linkType: hard @@ -3148,9 +3148,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.18.3": - version: 8.18.3 - resolution: "ws@npm:8.18.3" +"ws@npm:^8.19.0": + version: 8.19.0 + resolution: "ws@npm:8.19.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -3159,7 +3159,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10c0/eac918213de265ef7cb3d4ca348b891a51a520d839aa51cdb8ca93d4fa7ff9f6ccb339ccee89e4075324097f0a55157c89fa3f7147bde9d8d7e90335dc087b53 + checksum: 10c0/4741d9b9bc3f9c791880882414f96e36b8b254e34d4b503279d6400d9a4b87a033834856dbdd94ee4b637944df17ea8afc4bce0ff4a1560d2166be8855da5b04 languageName: node linkType: hard