summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-09-21 21:51:17 +0200
committerRory& <root@rory.gay>2025-09-29 18:38:06 +0200
commitbd9a75f91909f9a3a213bcf575a7722065e54e2b (patch)
tree2dc69379be8a4943e011ac40c6f79fcb9ffb9f41 /src
parentChannel: add helpers to calculate user permissions and determine channel visi... (diff)
downloadserver-ts-bd9a75f91909f9a3a213bcf575a7722065e54e2b.tar.xz
Move to some etension methods
Diffstat (limited to 'src')
-rw-r--r--src/bundle/start.ts1
-rw-r--r--src/util/entities/Channel.ts2
-rw-r--r--src/util/util/Array.ts31
-rw-r--r--src/util/util/Url.ts32
-rw-r--r--src/util/util/extensions/Array.ts58
-rw-r--r--src/util/util/extensions/Url.d.ts0
-rw-r--r--src/util/util/extensions/Url.ts60
-rw-r--r--src/util/util/extensions/index.ts2
-rw-r--r--src/util/util/index.ts4
9 files changed, 123 insertions, 67 deletions
diff --git a/src/bundle/start.ts b/src/bundle/start.ts

index c14487a98..5c2e28bd7 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 b79cc5d46..a83f4db46 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 082ac3071..000000000 --- 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 <https://www.gnu.org/licenses/>. -*/ - -// 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<T>(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 a23491261..000000000 --- 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 000000000..841803473 --- /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 <https://www.gnu.org/licenses/>. +*/ + +declare global { + interface Array<T> { + containsAll(target: T[]): boolean; + partition(filter: (elem: T) => boolean): [T[], T[]]; + single(filter: (elem: T) => boolean): T | null; + } +} + +export function containsAll<T>(arr: T[], target: T[]) { + return target.every((v) => arr.includes(v)); +} + +/* https://stackoverflow.com/a/50636286 */ +export function partition<T>(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<T>(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 <T>(this: T[], target: T[]) { + return containsAll(this, target); + }; +if (!Array.prototype.partition) + Array.prototype.partition = function <T>(this: T[], filter: (elem: T) => boolean) { + return partition(this, filter); + }; +if (!Array.prototype.single) + Array.prototype.single = function <T>(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 000000000..e69de29bb --- /dev/null +++ b/src/util/util/extensions/Url.d.ts
diff --git a/src/util/util/extensions/Url.ts b/src/util/util/extensions/Url.ts new file mode 100644
index 000000000..c0f07b374 --- /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 <https://www.gnu.org/licenses/>. +*/ + +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 000000000..46c26f4c2 --- /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 096db326a..05bdcdf81 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";