diff --git a/deno/rest/v10/mod.ts b/deno/rest/v10/mod.ts index c5ee1e68..7a8f086a 100644 --- a/deno/rest/v10/mod.ts +++ b/deno/rest/v10/mod.ts @@ -1,4 +1,5 @@ import type { Snowflake } from '../../globals.ts'; +import { urlSafeCharacters } from '../../utils/internals.ts'; export * from '../common.ts'; export * from './application.ts'; @@ -1056,7 +1057,18 @@ export const Routes = { for (const [key, fn] of Object.entries(Routes)) { Routes[key as keyof typeof Routes] = (...args: (boolean | number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; @@ -1363,8 +1375,19 @@ export const CDNRoutes = { }; for (const [key, fn] of Object.entries(CDNRoutes)) { - CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; diff --git a/deno/rest/v6/mod.ts b/deno/rest/v6/mod.ts index 7b2af679..91a44eda 100644 --- a/deno/rest/v6/mod.ts +++ b/deno/rest/v6/mod.ts @@ -1,3 +1,5 @@ +import { urlSafeCharacters } from '../../utils/internals.ts'; + export * from '../common.ts'; export * from './auditLog.ts'; @@ -521,8 +523,19 @@ export const Routes = { }; for (const [key, fn] of Object.entries(Routes)) { - Routes[key] = (...args: string[]) => { - const escaped = args.map((arg) => encodeURIComponent(arg)); + Routes[key] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; diff --git a/deno/rest/v8/mod.ts b/deno/rest/v8/mod.ts index 2589416d..b18b6847 100644 --- a/deno/rest/v8/mod.ts +++ b/deno/rest/v8/mod.ts @@ -1,4 +1,5 @@ import type { Snowflake } from '../../globals.ts'; +import { urlSafeCharacters } from '../../utils/internals.ts'; export * from '../common.ts'; @@ -778,8 +779,19 @@ export const Routes = { }; for (const [key, fn] of Object.entries(Routes)) { - Routes[key] = (...args: string[]) => { - const escaped = args.map((arg) => encodeURIComponent(arg)); + Routes[key] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; diff --git a/deno/rest/v9/mod.ts b/deno/rest/v9/mod.ts index 2fce5f3f..44be90d1 100644 --- a/deno/rest/v9/mod.ts +++ b/deno/rest/v9/mod.ts @@ -1,4 +1,5 @@ import type { Snowflake } from '../../globals.ts'; +import { urlSafeCharacters } from '../../utils/internals.ts'; export * from '../common.ts'; export * from './application.ts'; @@ -1065,7 +1066,18 @@ export const Routes = { for (const [key, fn] of Object.entries(Routes)) { Routes[key as keyof typeof Routes] = (...args: (boolean | number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; @@ -1372,8 +1384,19 @@ export const CDNRoutes = { }; for (const [key, fn] of Object.entries(CDNRoutes)) { - CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; @@ -1398,8 +1421,8 @@ export type ApplicationCoverFormat = Exclude; export type AchievementIconFormat = Exclude; export type StickerPackBannerFormat = Exclude; -export type StorePageAssetFormat = Exclude; export type TeamIconFormat = Exclude; +export type StorePageAssetFormat = Exclude; export type StickerFormat = Extract; export type RoleIconFormat = Exclude; export type GuildScheduledEventCoverFormat = Exclude; @@ -1417,6 +1440,7 @@ export interface CDNQuery { export const RouteBases = { api: `https://discord.com/api/v${APIVersion}`, cdn: 'https://cdn.discordapp.com', + media: 'https://media.discordapp.net', invite: 'https://discord.gg', template: 'https://discord.new', gift: 'https://discord.gift', diff --git a/deno/utils/internals.ts b/deno/utils/internals.ts index 369b60a3..58a00ec2 100644 --- a/deno/utils/internals.ts +++ b/deno/utils/internals.ts @@ -39,3 +39,5 @@ export type DistributiveOmit> = T extends unknown ? { [P in keyof Omit_]: Omit_[P] } : never; type Omit_ = Omit>; + +export const urlSafeCharacters = /^[\d%A-Za-z-]+$/g; diff --git a/rest/v10/index.ts b/rest/v10/index.ts index 9d548638..ab10a509 100644 --- a/rest/v10/index.ts +++ b/rest/v10/index.ts @@ -1,4 +1,5 @@ import type { Snowflake } from '../../globals'; +import { urlSafeCharacters } from '../../utils/internals'; export * from '../common'; export * from './application'; @@ -1056,7 +1057,18 @@ export const Routes = { for (const [key, fn] of Object.entries(Routes)) { Routes[key as keyof typeof Routes] = (...args: (boolean | number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; @@ -1363,8 +1375,19 @@ export const CDNRoutes = { }; for (const [key, fn] of Object.entries(CDNRoutes)) { - CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; diff --git a/rest/v6/index.ts b/rest/v6/index.ts index 0d2391c5..2f193cef 100644 --- a/rest/v6/index.ts +++ b/rest/v6/index.ts @@ -1,3 +1,5 @@ +import { urlSafeCharacters } from '../../utils/internals'; + export * from '../common'; export * from './auditLog'; @@ -521,8 +523,19 @@ export const Routes = { }; for (const [key, fn] of Object.entries(Routes)) { - Routes[key] = (...args: string[]) => { - const escaped = args.map((arg) => encodeURIComponent(arg)); + Routes[key] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; diff --git a/rest/v8/index.ts b/rest/v8/index.ts index 8ff5a35b..529d6608 100644 --- a/rest/v8/index.ts +++ b/rest/v8/index.ts @@ -1,4 +1,5 @@ import type { Snowflake } from '../../globals'; +import { urlSafeCharacters } from '../../utils/internals'; export * from '../common'; @@ -778,8 +779,19 @@ export const Routes = { }; for (const [key, fn] of Object.entries(Routes)) { - Routes[key] = (...args: string[]) => { - const escaped = args.map((arg) => encodeURIComponent(arg)); + Routes[key] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; diff --git a/rest/v9/index.ts b/rest/v9/index.ts index c85d9382..9e314a96 100644 --- a/rest/v9/index.ts +++ b/rest/v9/index.ts @@ -1,4 +1,5 @@ import type { Snowflake } from '../../globals'; +import { urlSafeCharacters } from '../../utils/internals'; export * from '../common'; export * from './application'; @@ -1065,7 +1066,18 @@ export const Routes = { for (const [key, fn] of Object.entries(Routes)) { Routes[key as keyof typeof Routes] = (...args: (boolean | number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; @@ -1372,8 +1384,19 @@ export const CDNRoutes = { }; for (const [key, fn] of Object.entries(CDNRoutes)) { - CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (number | string | undefined)[]) => { - const escaped = args.map((arg) => arg && encodeURIComponent(arg)); + CDNRoutes[key as keyof typeof CDNRoutes] = (...args: (boolean | number | string | undefined)[]) => { + const escaped = args.map((arg) => { + if (arg) { + // Skip already "safe" urls + if (urlSafeCharacters.test(String(arg))) { + return arg; + } + + return encodeURIComponent(arg); + } + + return arg; + }); // eslint-disable-next-line no-useless-call return fn.call(null, ...escaped); }; @@ -1398,8 +1421,8 @@ export type ApplicationCoverFormat = Exclude; export type AchievementIconFormat = Exclude; export type StickerPackBannerFormat = Exclude; -export type StorePageAssetFormat = Exclude; export type TeamIconFormat = Exclude; +export type StorePageAssetFormat = Exclude; export type StickerFormat = Extract; export type RoleIconFormat = Exclude; export type GuildScheduledEventCoverFormat = Exclude; @@ -1417,6 +1440,7 @@ export interface CDNQuery { export const RouteBases = { api: `https://discord.com/api/v${APIVersion}`, cdn: 'https://cdn.discordapp.com', + media: 'https://media.discordapp.net', invite: 'https://discord.gg', template: 'https://discord.new', gift: 'https://discord.gift', diff --git a/utils/internals.ts b/utils/internals.ts index 369b60a3..58a00ec2 100644 --- a/utils/internals.ts +++ b/utils/internals.ts @@ -39,3 +39,5 @@ export type DistributiveOmit> = T extends unknown ? { [P in keyof Omit_]: Omit_[P] } : never; type Omit_ = Omit>; + +export const urlSafeCharacters = /^[\d%A-Za-z-]+$/g;