diff --git a/src/bundle/start.ts b/src/bundle/start.ts
index c14487a9..5c2e28bd 100644
--- a/src/bundle/start.ts
+++ b/src/bundle/start.ts
@@ -18,7 +18,6 @@
// process.env.MONGOMS_DEBUG = "true";
import moduleAlias from "module-alias";
-
moduleAlias(__dirname + "../../../package.json");
import "reflect-metadata";
diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts
index b79cc5d4..a83f4db4 100644
--- a/src/util/entities/Channel.ts
+++ b/src/util/entities/Channel.ts
@@ -566,7 +566,7 @@ export class Channel extends BaseClass {
let userPerms = new Permissions(new BitField(0).add(roles.map(r => r.permissions)));
// TODO: do we want to have an instance-wide opt out of this behavior? It would just be an extra if statement here
- if (userPerms.has(Permissions.FLAGS.ADMINISTRATOR)) return true;
+ if (userPerms.has(Permissions.FLAGS.ADMINISTRATOR)) return userPerms;
// apply channel overrides
if (this.permission_overwrites) {
diff --git a/src/util/util/Array.ts b/src/util/util/Array.ts
deleted file mode 100644
index 082ac307..00000000
--- a/src/util/util/Array.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
- Copyright (C) 2023 Spacebar and Spacebar Contributors
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-// TODO: remove this function.
-
-export function containsAll(arr: unknown[], target: unknown[]) {
- return target.every((v) => arr.includes(v));
-}
-
-/* https://stackoverflow.com/a/50636286 */
-export function partition(array: T[], filter: (elem: T) => boolean) {
- const pass: T[] = [],
- fail: T[] = [];
- array.forEach((e) => (filter(e) ? pass : fail).push(e));
- return [pass, fail];
-}
diff --git a/src/util/util/Url.ts b/src/util/util/Url.ts
deleted file mode 100644
index a2349126..00000000
--- a/src/util/util/Url.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Normalize a URL by:
- * - Removing trailing slashes (except root path)
- * - Sorting query params alphabetically
- * - Removing empty query strings
- * - Removing fragments
- */
-export function normalizeUrl(input: string): string {
- try {
- const u = new URL(input);
- // Remove fragment
- u.hash = "";
- // Normalize pathname - remove trailing slash except for root "/"
- if (u.pathname !== "/" && u.pathname.endsWith("/")) {
- u.pathname = u.pathname.slice(0, -1);
- }
- // Normalize query params: sort by key
- if (u.search) {
- const params = Array.from(u.searchParams.entries());
- params.sort(([a], [b]) => a.localeCompare(b));
- u.search = params.length
- ? "?" + params.map(([k, v]) => `${k}=${v}`).join("&")
- : "";
- } else {
- // Ensure no empty search string
- u.search = "";
- }
- return u.toString();
- } catch (e) {
- return input;
- }
-}
diff --git a/src/util/util/extensions/Array.ts b/src/util/util/extensions/Array.ts
new file mode 100644
index 00000000..84180347
--- /dev/null
+++ b/src/util/util/extensions/Array.ts
@@ -0,0 +1,58 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2025 Spacebar and Spacebar Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+declare global {
+ interface Array {
+ containsAll(target: T[]): boolean;
+ partition(filter: (elem: T) => boolean): [T[], T[]];
+ single(filter: (elem: T) => boolean): T | null;
+ }
+}
+
+export function containsAll(arr: T[], target: T[]) {
+ return target.every((v) => arr.includes(v));
+}
+
+/* https://stackoverflow.com/a/50636286 */
+export function partition(array: T[], filter: (elem: T) => boolean): [T[], T[]] {
+ const pass: T[] = [],
+ fail: T[] = [];
+ array.forEach((e) => (filter(e) ? pass : fail).push(e));
+ return [pass, fail];
+}
+
+export function single(array: T[], filter: (elem: T) => boolean): T | null {
+ const results = array.filter(filter);
+ if (results.length > 1) throw new Error("Array contains more than one matching element");
+ if (results.length === 0) return null;
+ return results[0];
+}
+
+// register extensions
+if (!Array.prototype.containsAll)
+ Array.prototype.containsAll = function (this: T[], target: T[]) {
+ return containsAll(this, target);
+ };
+if (!Array.prototype.partition)
+ Array.prototype.partition = function (this: T[], filter: (elem: T) => boolean) {
+ return partition(this, filter);
+ };
+if (!Array.prototype.single)
+ Array.prototype.single = function (this: T[], filter: (elem: T) => boolean) {
+ return single(this, filter);
+ };
diff --git a/src/util/util/extensions/Url.d.ts b/src/util/util/extensions/Url.d.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/src/util/util/extensions/Url.ts b/src/util/util/extensions/Url.ts
new file mode 100644
index 00000000..c0f07b37
--- /dev/null
+++ b/src/util/util/extensions/Url.ts
@@ -0,0 +1,60 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2025 Spacebar and Spacebar Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+declare module "url" {
+ interface URL {
+ normalize(): string;
+ }
+}
+
+/**
+ * Normalize a URL by:
+ * - Removing trailing slashes (except root path)
+ * - Sorting query params alphabetically
+ * - Removing empty query strings
+ * - Removing fragments
+ */
+export function normalizeUrl(input: string): string {
+ try {
+ const u = new URL(input);
+ // Remove fragment
+ u.hash = "";
+ // Normalize pathname - remove trailing slash except for root "/"
+ if (u.pathname !== "/" && u.pathname.endsWith("/")) {
+ u.pathname = u.pathname.slice(0, -1);
+ }
+ // Normalize query params: sort by key
+ if (u.search) {
+ const params = Array.from(u.searchParams.entries());
+ params.sort(([a], [b]) => a.localeCompare(b));
+ u.search = params.length ? "?" + params.map(([k, v]) => `${k}=${v}`).join("&") : "";
+ } else {
+ // Ensure no empty search string
+ u.search = "";
+ }
+ return u.toString();
+ } catch (e) {
+ return input;
+ }
+}
+
+// register extensions
+if (!URL.prototype.normalize)
+ URL.prototype.normalize = function () {
+ return normalizeUrl(this.toString());
+ };
diff --git a/src/util/util/extensions/index.ts b/src/util/util/extensions/index.ts
new file mode 100644
index 00000000..46c26f4c
--- /dev/null
+++ b/src/util/util/extensions/index.ts
@@ -0,0 +1,2 @@
+export * from "./Array";
+export * from "./Url";
\ No newline at end of file
diff --git a/src/util/util/index.ts b/src/util/util/index.ts
index 096db326..05bdcdf8 100644
--- a/src/util/util/index.ts
+++ b/src/util/util/index.ts
@@ -17,7 +17,7 @@
*/
export * from "./ApiError";
-export * from "./Array";
+export * from "./extensions/Array";
export * from "./BitField";
//export * from "./Categories";
export * from "./cdn";
@@ -44,8 +44,8 @@ export * from "./String";
export * from "./Token";
export * from "./TraverseDirectory";
export * from "./WebAuthn";
-export * from "./Url";
export * from "./Gifs";
export * from "./Application";
export * from "./NameValidation";
export * from "./HelperTypes";
+export * from "./extensions";
diff --git a/tsconfig.json b/tsconfig.json
index 5d408b24..f5cfbe04 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -55,8 +55,8 @@
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
- "declaration": false /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
- "declarationMap": false /* Create sourcemaps for d.ts files. */,
+ "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
+ "declarationMap": true /* Create sourcemaps for d.ts files. */,
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */