From 2a68d9b5570e2a9086cc12916890f84b2f761587 Mon Sep 17 00:00:00 2001 From: Oh64 Date: Wed, 1 Jul 2026 22:19:45 +0200 Subject: [PATCH 1/2] update links --- CONTRIBUTING.MD | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD index fb702a2089..935d4a2ab1 100644 --- a/CONTRIBUTING.MD +++ b/CONTRIBUTING.MD @@ -3,8 +3,8 @@ Welcome to Spacebar, if you've made it here, I'm guessing you're looking to cont A good place to start would be over at the issues tab, though https://docs.spacebar.chat/contributing/server/missingroute/ may also be a good indicator for things that are remaining. -If you find yourself working on something, or want to discuss particular issues, feel free to drop by our developers guild ( - +If you find yourself working on something, or want to discuss particular issues, feel free to drop by our developers guild ( + ) Keep in mind that we do, under no circumstances, accept AI-generated, AI-assisted, or otherwise machine-generated contributions, for legal reasons. diff --git a/README.md b/README.md index 357cfd3bfe..14fab4719c 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ - - + + @@ -48,4 +48,4 @@ And with documentation on how to set up your own server [here](https://docs.spac You _should_ be able to use any client designed for Discord.com to connect to a Spacebar instance. However, some incompatibilities still exist between Spacebar and Discord. For this reason, not every client will connect. We recommend using [Fermo](https://fermo.sovr.top/login?instance=spacebar.chat) as a solid starting point on your adventure in the SpaceBar! -You can explore other clients with the [Spacebar Explorer](https://spacebar-explorer.sovr.top/clients). +You can explore other clients with the [Spacebar Explorer](https://sbar.top/clients). From 46d9ab4991d18b9ece58beb0b317e32e8403bc15 Mon Sep 17 00:00:00 2001 From: Oh64 Date: Wed, 1 Jul 2026 23:28:28 +0200 Subject: [PATCH 2/2] encode circular object --- src/gateway/util/Send.ts | 9 ++++----- src/util/util/JSON.ts | 25 +++++++++++++++++++++++++ src/webrtc/util/Send.ts | 8 ++++---- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/gateway/util/Send.ts b/src/gateway/util/Send.ts index f857a40033..4d6185943a 100644 --- a/src/gateway/util/Send.ts +++ b/src/gateway/util/Send.ts @@ -20,7 +20,7 @@ import { Payload, WebSocket } from "@spacebar/gateway"; import fs from "node:fs/promises"; import path from "node:path"; -import { JSONReplacer } from "@spacebar/util"; +import { JSONReplacer, cleanCircularRefs } from "@spacebar/util"; import * as erlpack from "harmony-erlpack"; // don't care @@ -54,10 +54,9 @@ export async function Send(socket: WebSocket, data: Payload) { // Erlpack doesn't like Date objects, encodes them as {} data = recurseJsonReplace(data); buffer = Buffer.from(erlpack.pack(data)); - } - // TODO: encode circular object - else if (socket.encoding === "json") buffer = JSON.stringify(data, JSONReplacer); - else return; + } else if (socket.encoding === "json") { + buffer = JSON.stringify(cleanCircularRefs(data), JSONReplacer); + } else return; // TODO: compression if (socket.compress === "zlib-stream") { diff --git a/src/util/util/JSON.ts b/src/util/util/JSON.ts index e716f7d4a4..e2e15ddd95 100644 --- a/src/util/util/JSON.ts +++ b/src/util/util/JSON.ts @@ -36,3 +36,28 @@ export function JSONReplacer(this: { [key: string]: unknown }, key: string, valu return value; } + +/** + * Walk an object tree and null out circular references in-place, + * while preserving shared (non-circular) references. + */ +export function cleanCircularRefs(data: T): T { + const ancestors = new Set(); + function walk(val: unknown): unknown { + if (val === null || typeof val !== "object") return val; + if (ancestors.has(val)) return null; + ancestors.add(val); + if (Array.isArray(val)) { + for (let i = 0; i < val.length; i++) { + val[i] = walk(val[i]) as (typeof val)[number]; + } + } else { + for (const key of Object.keys(val as Record)) { + (val as Record)[key] = walk((val as Record)[key]); + } + } + ancestors.delete(val); + return val; + } + return walk(data) as T; +} diff --git a/src/webrtc/util/Send.ts b/src/webrtc/util/Send.ts index 1d8aa61e4f..b2278aa642 100644 --- a/src/webrtc/util/Send.ts +++ b/src/webrtc/util/Send.ts @@ -1,4 +1,4 @@ -import { JSONReplacer } from "@spacebar/util"; +import { JSONReplacer, cleanCircularRefs } from "@spacebar/util"; import { VoicePayload } from "./Constants"; import { WebRtcWebSocket } from "./WebRtcWebSocket"; @@ -7,9 +7,9 @@ export function Send(socket: WebRtcWebSocket, data: VoicePayload) { let buffer: Buffer | string; - // TODO: encode circular object - if (socket.encoding === "json") buffer = JSON.stringify(data, JSONReplacer); - else return; + if (socket.encoding === "json") { + buffer = JSON.stringify(cleanCircularRefs(data), JSONReplacer); + } else return; return new Promise((res, rej) => { if (socket.readyState !== 1) {