diff --git a/packages/gateway/src/manager.ts b/packages/gateway/src/manager.ts index 2d213ee07..a94a8a63f 100644 --- a/packages/gateway/src/manager.ts +++ b/packages/gateway/src/manager.ts @@ -11,7 +11,7 @@ import { GatewayOpcodes, type RequestGuildMembers, } from '@discordeno/types' -import { Collection, LeakyBucket, logger } from '@discordeno/utils' +import { Collection, LeakyBucket, jsonSafeReplacer, logger } from '@discordeno/utils' import Shard from './Shard.js' import { type ShardEvents, ShardSocketCloseCodes, type ShardSocketRequest, type TransportCompression, type UpdateVoiceState } from './types.js' @@ -442,7 +442,7 @@ export function createGatewayManager(options: CreateGatewayManagerOptions): Gate }, async editBotStatus(data) { - gateway.logger.debug(`[Gateway] editBotStatus data: ${JSON.stringify(data)}`) + gateway.logger.debug(`[Gateway] editBotStatus data: ${JSON.stringify(data, jsonSafeReplacer)}`) await Promise.all( [...gateway.shards.values()].map(async (shard) => { diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index 7b63c525a..a8211afac 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -14,3 +14,14 @@ export async function delay(ms: number): Promise { export function hasProperty(obj: T, prop: Y): obj is T & Record { return obj.hasOwnProperty(prop) } + +/** Convert `JSON.stringify`-unserializable record values for debugging purposes. */ +export function jsonSafeReplacer(_key: string, value: unknown): unknown { + switch (typeof value) { + case 'bigint': + // Bigints are unserializable by `JSON.stringify`. + return String(value) + default: // Any other unhandled type that isn't supposed to require conversion. + return value + } +} diff --git a/packages/utils/tests/utils.spec.ts b/packages/utils/tests/utils.spec.ts index c278f3321..fe388d2f9 100644 --- a/packages/utils/tests/utils.spec.ts +++ b/packages/utils/tests/utils.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' import { afterEach, beforeEach, describe, it } from 'mocha' import sinon from 'sinon' -import { delay, hasProperty } from '../src/utils.js' +import { delay, hasProperty, jsonSafeReplacer } from '../src/utils.js' describe('utils.ts', () => { let clock: sinon.SinonFakeTimers @@ -15,6 +15,15 @@ describe('utils.ts', () => { clock.restore() }) + describe('jsonSafe function', () => { + it('will convert records to `JSON.stringify`-serializable', () => { + // Example from issue#4196: https://github.com/discordeno/discordeno/issues/4196. + const value = { limit: 0, userIds: [0n, 0n, 0n] } + const expected = { limit: 0, userIds: ['0', '0', '0'] } + expect(JSON.stringify(value, jsonSafeReplacer)).equal(JSON.stringify(expected)) + }) + }) + describe('delay function', () => { it('will delay/sleep for given time', async () => { let delayEnded = false