From b61585d5ae6e5600c84986d282362627ff47f160 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Tue, 21 Oct 2025 12:42:48 +0200 Subject: [PATCH 1/3] fix --- docs/vite-plugin.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/vite-plugin.md b/docs/vite-plugin.md index fca34660..b13b017e 100644 --- a/docs/vite-plugin.md +++ b/docs/vite-plugin.md @@ -60,7 +60,9 @@ export default { // port to run the event bus on port: 1234, // console log debug logs or not - debug: false + debug: false, + // enables the server event bus (defaults to true), you can disable it if you're running devtools in something like storybook or vitest + enabled: true }, }), // ... rest of your plugins here From f79a503c1979c9f631c475551a9ed49c56ea1612 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Fri, 24 Oct 2025 17:07:40 +0200 Subject: [PATCH 2/3] fix: fix issues with bigInt parsing --- packages/event-bus/src/client/client.ts | 10 +- packages/event-bus/src/index.ts | 1 + packages/event-bus/src/server/server.ts | 7 +- packages/event-bus/src/utils/json.test.ts | 142 ++++++++++++++++++++++ packages/event-bus/src/utils/json.ts | 33 +++++ 5 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 packages/event-bus/src/utils/json.test.ts create mode 100644 packages/event-bus/src/utils/json.ts diff --git a/packages/event-bus/src/client/client.ts b/packages/event-bus/src/client/client.ts index 55495786..bbd19098 100644 --- a/packages/event-bus/src/client/client.ts +++ b/packages/event-bus/src/client/client.ts @@ -1,3 +1,5 @@ +import { parseWithBigInt, stringifyWithBigInt } from '../utils/json' + interface TanStackDevtoolsEvent { type: TEventName payload: TPayload @@ -55,7 +57,7 @@ export class ClientEventBus { this.#connectToServerBus = connectToServerBus this.#eventTarget = this.getGlobalTarget() this.#broadcastChannel.onmessage = (e) => { - this.emitToClients(JSON.parse(e.data), true) + this.emitToClients(parseWithBigInt(e.data), true) } this.debugLog('Initializing client event bus') } @@ -74,14 +76,14 @@ export class ClientEventBus { // We only emit the events if they didn't come from the broadcast channel // otherwise it would infinitely send events between if (!fromBroadcastChannel) { - this.#broadcastChannel?.postMessage(JSON.stringify(event)) + this.#broadcastChannel?.postMessage(stringifyWithBigInt(event)) } this.debugLog('Emitting event to global client listeners', event) this.#eventTarget.dispatchEvent(globalEvent) } private emitToServer(event: TanStackDevtoolsEvent) { - const json = JSON.stringify(event) + const json = stringifyWithBigInt(event) // try to emit it to the event bus first if (this.#socket && this.#socket.readyState === WebSocket.OPEN) { this.debugLog('Emitting event to server via WS', event) @@ -185,7 +187,7 @@ export class ClientEventBus { private handleEventReceived(data: string) { try { - const event = JSON.parse(data) as TanStackDevtoolsEvent + const event = parseWithBigInt(data) as TanStackDevtoolsEvent this.emitToClients(event) } catch {} } diff --git a/packages/event-bus/src/index.ts b/packages/event-bus/src/index.ts index e69de29b..d6f440eb 100644 --- a/packages/event-bus/src/index.ts +++ b/packages/event-bus/src/index.ts @@ -0,0 +1 @@ +export { parseWithBigInt, stringifyWithBigInt } from './utils/json' diff --git a/packages/event-bus/src/server/server.ts b/packages/event-bus/src/server/server.ts index 21afa8d3..748e9470 100644 --- a/packages/event-bus/src/server/server.ts +++ b/packages/event-bus/src/server/server.ts @@ -1,5 +1,6 @@ import http from 'node:http' import { WebSocket, WebSocketServer } from 'ws' +import { parseWithBigInt, stringifyWithBigInt } from '../utils/json' // Shared types export interface TanStackDevtoolsEvent< @@ -76,7 +77,7 @@ export class ServerEventBus { private emitEventToClients(event: TanStackDevtoolsEvent) { this.debugLog('Emitting event to clients', event) - const json = JSON.stringify(event) + const json = stringifyWithBigInt(event) for (const client of this.#clients) { if (client.readyState === WebSocket.OPEN) { @@ -117,7 +118,7 @@ export class ServerEventBus { req.on('data', (chunk) => (body += chunk)) req.on('end', () => { try { - const msg = JSON.parse(body) + const msg = parseWithBigInt(body) this.debugLog('Received event from client', msg) this.emitToServer(msg) } catch {} @@ -155,7 +156,7 @@ export class ServerEventBus { }) ws.on('message', (msg) => { this.debugLog('Received message from WebSocket client', msg.toString()) - const data = JSON.parse(msg.toString()) + const data = parseWithBigInt(msg.toString()) this.emitToServer(data) }) }) diff --git a/packages/event-bus/src/utils/json.test.ts b/packages/event-bus/src/utils/json.test.ts new file mode 100644 index 00000000..a30d27c4 --- /dev/null +++ b/packages/event-bus/src/utils/json.test.ts @@ -0,0 +1,142 @@ +import { describe, expect, it } from 'vitest' +import { parseWithBigInt, stringifyWithBigInt } from './json' + +describe('json utils', () => { + describe('stringifyWithBigInt', () => { + it('should handle regular JSON data', () => { + const data = { name: 'test', count: 42, nested: { value: true } } + const result = stringifyWithBigInt(data) + expect(result).toBe(JSON.stringify(data)) + }) + + it('should convert BigInt to object with marker', () => { + const data = { id: BigInt(9007199254740991) } + const result = stringifyWithBigInt(data) + const parsed = JSON.parse(result) + expect(parsed.id).toEqual({ + __type: 'bigint', + value: '9007199254740991', + }) + }) + + it('should handle nested BigInt values', () => { + const data = { + user: { id: BigInt(123), balance: BigInt(456789) }, + timestamp: BigInt(1234567890), + } + const result = stringifyWithBigInt(data) + const parsed = JSON.parse(result) + expect(parsed.user.id).toEqual({ __type: 'bigint', value: '123' }) + expect(parsed.user.balance).toEqual({ + __type: 'bigint', + value: '456789', + }) + expect(parsed.timestamp).toEqual({ + __type: 'bigint', + value: '1234567890', + }) + }) + + it('should handle arrays with BigInt', () => { + const data = [BigInt(1), BigInt(2), BigInt(3)] + const result = stringifyWithBigInt(data) + const parsed = JSON.parse(result) + expect(parsed).toEqual([ + { __type: 'bigint', value: '1' }, + { __type: 'bigint', value: '2' }, + { __type: 'bigint', value: '3' }, + ]) + }) + + it('should handle mixed types with BigInt', () => { + const data = { + string: 'text', + number: 42, + bigint: BigInt(999), + boolean: true, + null: null, + array: [1, BigInt(2), 'three'], + } + const result = stringifyWithBigInt(data) + const parsed = JSON.parse(result) + expect(parsed.bigint).toEqual({ __type: 'bigint', value: '999' }) + expect(parsed.array[1]).toEqual({ __type: 'bigint', value: '2' }) + }) + }) + + describe('parseWithBigInt', () => { + it('should handle regular JSON data', () => { + const json = JSON.stringify({ name: 'test', count: 42 }) + const result = parseWithBigInt(json) + expect(result).toEqual({ name: 'test', count: 42 }) + }) + + it('should restore BigInt from marker object', () => { + const json = JSON.stringify({ id: { __type: 'bigint', value: '123' } }) + const result = parseWithBigInt(json) + expect(result.id).toBe(BigInt(123)) + expect(typeof result.id).toBe('bigint') + }) + + it('should handle nested BigInt restoration', () => { + const json = JSON.stringify({ + user: { + id: { __type: 'bigint', value: '9007199254740991' }, + balance: { __type: 'bigint', value: '456789' }, + }, + timestamp: { __type: 'bigint', value: '1234567890' }, + }) + const result = parseWithBigInt(json) + expect(result.user.id).toBe(BigInt(9007199254740991)) + expect(result.user.balance).toBe(BigInt(456789)) + expect(result.timestamp).toBe(BigInt(1234567890)) + }) + + it('should handle arrays with BigInt restoration', () => { + const json = JSON.stringify([ + { __type: 'bigint', value: '1' }, + { __type: 'bigint', value: '2' }, + { __type: 'bigint', value: '3' }, + ]) + const result = parseWithBigInt(json) + expect(result).toEqual([BigInt(1), BigInt(2), BigInt(3)]) + }) + + it('should not convert objects that look like BigInt markers but are not', () => { + const json = JSON.stringify({ + fake: { __type: 'bigint', value: 123 }, // value is not a string + real: { __type: 'bigint', value: '456' }, + }) + const result = parseWithBigInt(json) + expect(result.fake).toEqual({ __type: 'bigint', value: 123 }) + expect(result.real).toBe(BigInt(456)) + }) + }) + + describe('round-trip', () => { + it('should correctly round-trip BigInt values', () => { + const original = { + id: BigInt(9007199254740991), + nested: { + value: BigInt(123456789), + }, + array: [BigInt(1), BigInt(2)], + } + const stringified = stringifyWithBigInt(original) + const parsed = parseWithBigInt(stringified) + expect(parsed.id).toBe(original.id) + expect(parsed.nested.value).toBe(original.nested.value) + expect(parsed.array[0]).toBe(original.array[0]) + expect(parsed.array[1]).toBe(original.array[1]) + }) + + it('should handle very large BigInt values', () => { + const original = { + huge: BigInt('123456789012345678901234567890'), + } + const stringified = stringifyWithBigInt(original) + const parsed = parseWithBigInt(stringified) + expect(parsed.huge).toBe(original.huge) + }) + }) +}) diff --git a/packages/event-bus/src/utils/json.ts b/packages/event-bus/src/utils/json.ts new file mode 100644 index 00000000..1870f2e1 --- /dev/null +++ b/packages/event-bus/src/utils/json.ts @@ -0,0 +1,33 @@ +/** + * Safely stringify data that may contain BigInt values + * BigInt values are converted to objects with a special marker + */ +export function stringifyWithBigInt(data: any): string { + return JSON.stringify(data, (_key, value) => { + if (typeof value === 'bigint') { + return { + __type: 'bigint', + value: value.toString(), + } + } + return value + }) +} + +/** + * Parse JSON and restore BigInt values + * Objects with __type: 'bigint' are converted back to BigInt + */ +export function parseWithBigInt(json: string): any { + return JSON.parse(json, (_key, value) => { + if ( + value && + typeof value === 'object' && + value.__type === 'bigint' && + typeof value.value === 'string' + ) { + return BigInt(value.value) + } + return value + }) +} From 92220899eee47696ae4daeac5cc91ef8cc750cd5 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Fri, 24 Oct 2025 17:26:00 +0200 Subject: [PATCH 3/3] changeset --- .changeset/three-queens-stay.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/three-queens-stay.md diff --git a/.changeset/three-queens-stay.md b/.changeset/three-queens-stay.md new file mode 100644 index 00000000..8bcbfa44 --- /dev/null +++ b/.changeset/three-queens-stay.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools-event-bus': patch +--- + +fixed an issue where bigInt was not parsed properly