Fix TopstepX login loop on bundled builds: signalr's dynamic require

@microsoft/signalr does an unconditional require('ws')/require('eventsource')
when constructing a connection; bundled (turbopack) builds throw 'dynamic
usage of require is not supported', which the login chain mislabelled as a
login failure and retried every 60s. Externalize the package and never let
hub setup fail the login — REST polling covers everything the hub does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-13 13:28:09 -05:00
co-authored by Claude Fable 5
parent 153b1273c3
commit 4edb771701
2 changed files with 18 additions and 2 deletions
+14 -1
View File
@@ -172,7 +172,14 @@ export class TopstepXClient implements PlatformClient {
if (this.syncInterval) { clearInterval(this.syncInterval); this.syncInterval = null; } if (this.syncInterval) { clearInterval(this.syncInterval); this.syncInterval = null; }
this.syncComplete = false; this.syncComplete = false;
await this.startHub(); // Hub failure must never read as a login failure: REST polling
// carries every feature (slower), and subscribeAll retries the
// hub on each sync tick.
try {
await this.startHub();
} catch (err) {
console.error('[topstepx] hub setup failed — running on REST polling only', err instanceof Error ? err.message : err);
}
await this.requestSync(); await this.requestSync();
this.syncInterval = setInterval(() => void this.requestSync(), 60_000); this.syncInterval = setInterval(() => void this.requestSync(), 60_000);
@@ -240,6 +247,12 @@ export class TopstepXClient implements PlatformClient {
} }
this.subscribedAccountIds.clear(); this.subscribedAccountIds.clear();
// signalr's Node path does an unconditional dynamic require('ws') /
// require('eventsource') when constructing the connection — bundled
// builds throw "dynamic usage of require is not supported" on it, so
// the package is listed in next.config serverExternalPackages and any
// residual failure here is caught by the caller (REST polling covers
// everything the hub does, just slower).
const hub = new signalR.HubConnectionBuilder() const hub = new signalR.HubConnectionBuilder()
.withUrl(USER_HUB, { .withUrl(USER_HUB, {
skipNegotiation: true, skipNegotiation: true,
+4 -1
View File
@@ -1,7 +1,10 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
serverExternalPackages: ['better-sqlite3'], // @microsoft/signalr does dynamic require('ws')/require('eventsource') for
// its Node transports — bundling it (turbopack production builds) throws
// "dynamic usage of require is not supported" the moment a hub is built.
serverExternalPackages: ['better-sqlite3', '@microsoft/signalr'],
}; };
export default nextConfig; export default nextConfig;