refactor(ws)!: take /gateway/bot data in WebSocketManager#connect (#11602)

BREAKING CHANGE: `WebSocketManager` no longer accepts `fetchGatewayInformation`, pass `gatewayInformation` to `connect` instead.
This commit is contained in:
Denis-Adrian Cristea
2026-09-12 21:38:20 +01:00
committed by GitHub
parent a4116036be
commit c626815bff
10 changed files with 132 additions and 194 deletions
+3 -2
View File
@@ -55,7 +55,6 @@ const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
const gateway = new WebSocketManager({
token: process.env.DISCORD_TOKEN,
intents: GatewayIntentBits.GuildMessages | GatewayIntentBits.MessageContent,
fetchGatewayInformation: () => rest.get('/gateway/bot') as Promise<RESTGetAPIGatewayBotResult>,
});
// Create a client to emit relevant events.
@@ -75,7 +74,9 @@ client.on(GatewayDispatchEvents.InteractionCreate, async ({ data: interaction, a
client.once(GatewayDispatchEvents.Ready, () => console.log('Ready!'));
// Start the WebSocket connection.
gateway.connect();
await gateway.connect({
gatewayInformation: (await rest.get('/gateway/bot')) as RESTGetAPIGatewayBotResult,
});
```
## Independent REST API Usage
+2 -3
View File
@@ -206,7 +206,6 @@ class Client extends AsyncEventEmitter {
const wsOptions = {
...this.options.ws,
intents: this.options.intents.bitfield,
fetchGatewayInformation: () => this.rest.get(Routes.gatewayBot()),
// Explicitly nulled to always be set using `setToken` in `login`
token: null,
};
@@ -322,7 +321,7 @@ class Client extends AsyncEventEmitter {
this.ws.setToken(this.token);
try {
await this.ws.connect();
await this.ws.connect({ gatewayInformation: await this.rest.get(Routes.gatewayBot()) });
return this.token;
} catch (error) {
await this.destroy();
@@ -434,7 +433,7 @@ class Client extends AsyncEventEmitter {
* @private
*/
async _broadcast(packet) {
const shardIds = await this.ws.getShardIds();
const shardIds = this.ws.getShardIds();
return Promise.all(shardIds.map(shardId => this.ws.send(shardId, packet)));
}
@@ -137,7 +137,7 @@ class GuildManager extends CachedManager {
const innerData = await this.client.rest.get(Routes.guild(id), {
query: makeURLSearchParams({ with_counts: options.withCounts ?? true }),
});
innerData.shardId = ShardClientUtil.shardIdForGuildId(id, await this.client.ws.fetchShardCount());
innerData.shardId = ShardClientUtil.shardIdForGuildId(id, this.client.ws.getShardCount());
return this._add(innerData, options.cache);
}
@@ -165,7 +165,7 @@ class GuildManager extends CachedManager {
* console.log(soundboardSounds.get('123456789012345678'));
*/
async fetchSoundboardSounds({ guildIds, time = 10_000 }) {
const shardCount = await this.client.ws.getShardCount();
const shardCount = this.client.ws.getShardCount();
const shardIds = Map.groupBy(guildIds, guildId => ShardClientUtil.shardIdForGuildId(guildId, shardCount));
for (const [shardId, shardGuildIds] of shardIds) {
+6 -24
View File
@@ -46,16 +46,14 @@ The example uses [ES modules](https://nodejs.org/api/esm.html#enabling).
```ts
import { WebSocketManager, WebSocketShardEvents, CompressionMethod } from '@discordjs/ws';
import { REST } from '@discordjs/rest';
import type { RESTGetAPIGatewayBotResult } from 'discord-api-types/v10';
import { Routes, type RESTGetAPIGatewayBotResult } from 'discord-api-types/v10';
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
// This example will spawn Discord's recommended shard count, all under the current process.
const manager = new WebSocketManager({
token: process.env.DISCORD_TOKEN,
intents: 0, // for no intents
fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
// uncomment if you have zlib-sync installed and want to use compression
// compression: CompressionMethod.ZlibSync,
@@ -67,7 +65,10 @@ manager.on(WebSocketShardEvents.Dispatch, (event) => {
// Process gateway events here.
});
await manager.connect();
// The data from `/gateway/bot` is used as-is, so it's best fetched right before connecting.
await manager.connect({
gatewayInformation: (await rest.get(Routes.gatewayBot())) as RESTGetAPIGatewayBotResult,
});
```
### Specify shards
@@ -78,9 +79,6 @@ const manager = new WebSocketManager({
token: process.env.DISCORD_TOKEN,
intents: 0,
shardCount: 4,
fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
});
// The manager also supports being responsible for only a subset of your shards:
@@ -92,9 +90,6 @@ const manager = new WebSocketManager({
intents: 0,
shardCount: 8,
shardIds: [0, 2, 4, 6],
fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
});
// Alternatively, if your shards are consecutive, you can pass in a range
@@ -106,9 +101,6 @@ const manager = new WebSocketManager({
start: 0,
end: 4,
},
fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
});
```
@@ -118,16 +110,11 @@ You can also have the shards spawn in worker threads:
```ts
import { WebSocketManager, WorkerShardingStrategy } from '@discordjs/ws';
import { REST } from '@discordjs/rest';
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
const manager = new WebSocketManager({
token: process.env.DISCORD_TOKEN,
intents: 0,
shardCount: 6,
fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
// This will cause 3 workers to spawn, 2 shards per each
buildStrategy: (manager) => new WorkerShardingStrategy(manager, { shardsPerWorker: 2 }),
// Or maybe you want all your shards under a single worker
@@ -139,15 +126,10 @@ const manager = new WebSocketManager({
```ts
import { WebSocketManager, WorkerShardingStrategy } from '@discordjs/ws';
import { REST } from '@discordjs/rest';
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
const manager = new WebSocketManager({
token: process.env.DISCORD_TOKEN,
intents: 0,
fetchGatewayInformation() {
return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
},
buildStrategy: (manager) =>
new WorkerShardingStrategy(manager, {
shardsPerWorker: 2,
@@ -46,11 +46,17 @@ test('session info', async () => {
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
buildStrategy: () => ({
spawn: vi.fn(),
connect: vi.fn(),
destroy: vi.fn(),
send: vi.fn(),
fetchStatus: vi.fn(),
}),
});
await manager.connect({ gatewayInformation: mockGatewayInformation });
const strategy = new WorkerContextFetchingStrategy(await managerToFetchingStrategyOptions(manager));
strategy.updateSessionInfo(0, session);
@@ -142,9 +142,6 @@ test('spawn, connect, send a message, session info, and destroy', async () => {
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
shardIds: [0, 1],
retrieveSessionInfo: mockRetrieveSessionInfo,
updateSessionInfo: mockUpdateSessionInfo,
@@ -153,7 +150,7 @@ test('spawn, connect, send a message, session info, and destroy', async () => {
const managerEmitSpy = vi.spyOn(manager, 'emit');
await manager.connect();
await manager.connect({ gatewayInformation: mockGatewayInformation });
expect(mockConstructor).toHaveBeenCalledWith(
expect.stringContaining('defaultWorker.js'),
expect.objectContaining({ workerData: expect.objectContaining({ shardIds: [0, 1] }) }),
@@ -4,136 +4,113 @@ import { describe, expect, test, vi } from 'vitest';
import { WebSocketManager, type IShardingStrategy } from '../../src/index.js';
import { mockGatewayInformation } from '../gateway.mock.js';
vi.useFakeTimers();
class MockStrategy implements IShardingStrategy {
public spawn = vi.fn();
const NOW = vi.fn().mockReturnValue(Date.now());
global.Date.now = NOW;
public connect = vi.fn();
test('fetch gateway information', async () => {
const fetchGatewayInformation = vi.fn(async () => mockGatewayInformation);
public destroy = vi.fn();
public send = vi.fn();
public fetchStatus = vi.fn();
}
test('connect requires gateway information', async () => {
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
fetchGatewayInformation,
});
const initial = await manager.fetchGatewayInformation();
expect(initial).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).toHaveBeenCalledOnce();
// @ts-expect-error: Testing the runtime check for a missing gatewayInformation
await expect(manager.connect()).rejects.toThrow(TypeError);
});
fetchGatewayInformation.mockClear();
test('gateway information is not available before connecting', () => {
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
});
const cached = await manager.fetchGatewayInformation();
expect(cached).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).not.toHaveBeenCalled();
fetchGatewayInformation.mockClear();
const forced = await manager.fetchGatewayInformation(true);
expect(forced).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).toHaveBeenCalledOnce();
fetchGatewayInformation.mockClear();
NOW.mockReturnValue(Number.POSITIVE_INFINITY);
const cacheExpired = await manager.fetchGatewayInformation();
expect(cacheExpired).toEqual(mockGatewayInformation);
expect(fetchGatewayInformation).toHaveBeenCalledOnce();
expect(() => manager.getGatewayInformation()).toThrow(Error);
expect(() => manager.getShardCount()).toThrow(Error);
});
describe('get shard count', () => {
test('with shard count', async () => {
test('with no shard count or ids', async () => {
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
buildStrategy: () => new MockStrategy(),
});
await manager.connect({ gatewayInformation: mockGatewayInformation });
expect(manager.getShardCount()).toBe(mockGatewayInformation.shards);
});
test('with shard count', () => {
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardCount: 2,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
});
expect(await manager.getShardCount()).toBe(2);
expect(manager.getShardCount()).toBe(2);
});
test('with shard ids array', async () => {
test('with shard ids array', () => {
const shardIds = [5, 9];
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardIds,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
});
expect(await manager.getShardCount()).toBe(shardIds.at(-1)! + 1);
expect(manager.getShardCount()).toBe(shardIds.at(-1)! + 1);
});
test('with shard id range', async () => {
test('with shard id range', () => {
const shardIds = { start: 5, end: 9 };
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardIds,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
});
expect(await manager.getShardCount()).toBe(shardIds.end + 1);
expect(manager.getShardCount()).toBe(shardIds.end + 1);
});
});
test('update shard count', async () => {
const fetchGatewayInformation = vi.fn(async () => mockGatewayInformation);
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardCount: 2,
fetchGatewayInformation,
buildStrategy: () => new MockStrategy(),
});
expect(await manager.getShardCount()).toBe(2);
expect(fetchGatewayInformation).not.toHaveBeenCalled();
fetchGatewayInformation.mockClear();
expect(manager.getShardCount()).toBe(2);
await manager.updateShardCount(3);
expect(await manager.getShardCount()).toBe(3);
expect(fetchGatewayInformation).toHaveBeenCalled();
expect(manager.getShardCount()).toBe(3);
expect(manager.getShardIds()).toStrictEqual([0, 1, 2]);
});
test('it handles passing in both shardIds and shardCount', async () => {
test('it handles passing in both shardIds and shardCount', () => {
const shardIds = { start: 2, end: 3 };
const manager = new WebSocketManager({
token: 'A-Very-Fake-Token',
intents: 0,
shardIds,
shardCount: 4,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
});
expect(await manager.getShardCount()).toBe(4);
expect(await manager.getShardIds()).toStrictEqual([2, 3]);
expect(manager.getShardCount()).toBe(4);
expect(manager.getShardIds()).toStrictEqual([2, 3]);
});
test('strategies', async () => {
class MockStrategy implements IShardingStrategy {
public spawn = vi.fn();
public connect = vi.fn();
public destroy = vi.fn();
public send = vi.fn();
public fetchStatus = vi.fn();
}
const strategy = new MockStrategy();
const shardIds = [0, 1, 2];
@@ -142,19 +119,18 @@ test('strategies', async () => {
token: 'A-Very-Fake-Token',
intents: 0,
shardIds,
async fetchGatewayInformation() {
return mockGatewayInformation;
},
buildStrategy: () => strategy,
});
await manager.connect();
await manager.connect({ gatewayInformation: mockGatewayInformation });
expect(manager.getGatewayInformation()).toBe(mockGatewayInformation);
expect(strategy.spawn).toHaveBeenCalledWith(shardIds);
expect(strategy.connect).toHaveBeenCalled();
const destroyOptions = { reason: ':3' };
await manager.destroy(destroyOptions);
expect(strategy.destroy).toHaveBeenCalledWith(destroyOptions);
expect(() => manager.getGatewayInformation()).toThrow(Error);
const send: GatewaySendPayload = {
op: GatewayOpcodes.RequestGuildMembers,
@@ -51,7 +51,7 @@ export async function managerToFetchingStrategyOptions(manager: WebSocketManager
useIdentifyCompression: manager.options.useIdentifyCompression,
version: manager.options.version,
gatewayInformation: await manager.fetchGatewayInformation(),
shardCount: await manager.getShardCount(),
gatewayInformation: manager.getGatewayInformation(),
shardCount: manager.getShardCount(),
};
}
+3 -5
View File
@@ -37,10 +37,8 @@ export const CompressionParameterMap = {
* Default options used by the manager
*/
export const DefaultWebSocketManagerOptions = {
async buildIdentifyThrottler(manager: WebSocketManager) {
const info = await manager.fetchGatewayInformation();
return new SimpleIdentifyThrottler(info.session_start_limit.max_concurrency);
},
buildIdentifyThrottler: (manager: WebSocketManager) =>
new SimpleIdentifyThrottler(manager.getGatewayInformation().session_start_limit.max_concurrency),
buildStrategy: (manager) => new SimpleShardingStrategy(manager),
shardCount: null,
shardIds: null,
@@ -70,7 +68,7 @@ export const DefaultWebSocketManagerOptions = {
handshakeTimeout: 30_000,
helloTimeout: 60_000,
readyTimeout: 15_000,
} as const satisfies Omit<OptionalWebSocketManagerOptions, 'fetchGatewayInformation' | 'token'>;
} as const satisfies Omit<OptionalWebSocketManagerOptions, 'token'>;
export const ImportantGatewayOpcodes = new Set([
GatewayOpcodes.Heartbeat,
+56 -77
View File
@@ -2,7 +2,6 @@ import type { Collection } from '@discordjs/collection';
import { range, type Awaitable } from '@discordjs/util';
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
import type {
APIGatewayBotInfo,
GatewayIdentifyProperties,
GatewayPresenceUpdateData,
RESTGetAPIGatewayBotResult,
@@ -54,22 +53,6 @@ export interface SessionInfo {
* Required options for the WebSocketManager
*/
export interface RequiredWebSocketManagerOptions {
/**
* Function for retrieving the information returned by the `/gateway/bot` endpoint.
* We recommend using a REST client that respects Discord's rate limits, such as `@discordjs/rest`.
*
* @example
* ```ts
* const rest = new REST().setToken(process.env.DISCORD_TOKEN);
* const manager = new WebSocketManager({
* token: process.env.DISCORD_TOKEN,
* fetchGatewayInformation() {
* return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
* },
* });
* ```
*/
fetchGatewayInformation(): Awaitable<RESTGetAPIGatewayBotResult>;
/**
* The intents to request
*/
@@ -89,13 +72,9 @@ export interface OptionalWebSocketManagerOptions {
*
* @example
* ```ts
* const rest = new REST().setToken(process.env.DISCORD_TOKEN);
* const manager = new WebSocketManager({
* token: process.env.DISCORD_TOKEN,
* intents: 0, // for no intents
* fetchGatewayInformation() {
* return rest.get(Routes.gatewayBot()) as Promise<RESTGetAPIGatewayBotResult>;
* },
* buildStrategy: (manager) => new WorkerShardingStrategy(manager, { shardsPerWorker: 2 }),
* });
* ```
@@ -207,6 +186,26 @@ export interface WebSocketManagerOptions extends OptionalWebSocketManagerOptions
export interface CreateWebSocketManagerOptions
extends Partial<OptionalWebSocketManagerOptions>, RequiredWebSocketManagerOptions {}
/**
* Options for {@link WebSocketManager.connect}
*/
export interface WebSocketManagerConnectOptions {
/**
* Information retrieved from the `/gateway/bot` endpoint, used as-is.
* We recommend using a REST client that respects Discord's rate limits, such as `@discordjs/rest`,
* and fetching this information right before connecting, as the session start limits it reports go stale.
*
* @example
* ```ts
* const rest = new REST().setToken(process.env.DISCORD_TOKEN);
* await manager.connect({
* gatewayInformation: (await rest.get(Routes.gatewayBot())) as RESTGetAPIGatewayBotResult,
* });
* ```
*/
gatewayInformation: RESTGetAPIGatewayBotResult;
}
export interface ManagerShardEventsMap {
[WebSocketShardEvents.Closed]: [code: number, shardId: number];
[WebSocketShardEvents.Debug]: [message: string, shardId: number];
@@ -225,24 +224,13 @@ export interface ManagerShardEventsMap {
export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> implements AsyncDisposable {
#token: string | null = null;
#gatewayInformation: RESTGetAPIGatewayBotResult | null = null;
/**
* The options being used by this manager
*/
public readonly options: Omit<WebSocketManagerOptions, 'token'>;
/**
* Internal cache for a GET /gateway/bot result
*/
private gatewayInformation: {
data: APIGatewayBotInfo;
expiresAt: number;
} | null = null;
/**
* Internal cache for the shard ids
*/
private shardIds: number[] | null = null;
/**
* Strategy used to manage shards
*
@@ -266,10 +254,6 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
}
public constructor(options: CreateWebSocketManagerOptions) {
if (typeof options.fetchGatewayInformation !== 'function') {
throw new TypeError('fetchGatewayInformation is required');
}
super();
this.options = {
...DefaultWebSocketManagerOptions,
@@ -280,28 +264,19 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
}
/**
* Fetches the gateway information from Discord - or returns it from cache if available
*
* @param force - Whether to ignore the cache and force a fresh fetch
* The `/gateway/bot` information provided to {@link WebSocketManager.connect}.
* Throws if the method has not been invoked yet.
*/
public async fetchGatewayInformation(force = false) {
if (this.gatewayInformation) {
if (this.gatewayInformation.expiresAt <= Date.now()) {
this.gatewayInformation = null;
} else if (!force) {
return this.gatewayInformation.data;
}
public getGatewayInformation(): RESTGetAPIGatewayBotResult {
if (!this.#gatewayInformation) {
throw new Error('Gateway information has not been set. Invoke `connect()` first.');
}
const data = await this.options.fetchGatewayInformation();
// For single sharded bots session_start_limit.reset_after will be 0, use 5 seconds as a minimum expiration time
this.gatewayInformation = { data, expiresAt: Date.now() + (data.session_start_limit.reset_after || 5_000) };
return this.gatewayInformation.data;
return this.#gatewayInformation;
}
/**
* Updates your total shard count on-the-fly, spawning shards as needed
* Updates your total shard count on-the-fly, re-spawning all shards to the new amount
*
* @param shardCount - The new shard count to use
*/
@@ -309,7 +284,7 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
await this.strategy.destroy({ reason: 'User is adjusting their shards' });
this.options.shardCount = shardCount;
const shardIds = await this.getShardIds(true);
const shardIds = this.getShardIds();
await this.strategy.spawn(shardIds);
return this;
@@ -317,24 +292,26 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
/**
* Yields the total number of shards across for your bot, accounting for Discord recommendations
*
* @remarks
* Throws if {@link WebSocketManager.connect} has not been invoked yet.
*/
public async getShardCount(): Promise<number> {
public getShardCount(): number {
if (this.options.shardCount) {
return this.options.shardCount;
}
const shardIds = await this.getShardIds();
const shardIds = this.getShardIds();
return Math.max(...shardIds) + 1;
}
/**
* Yields the ids of the shards this manager should manage
*
* @remarks
* Throws if {@link WebSocketManager.connect} has not been invoked yet.
*/
public async getShardIds(force = false): Promise<number[]> {
if (this.shardIds && !force) {
return this.shardIds;
}
public getShardIds(): number[] {
let shardIds: number[];
if (this.options.shardIds) {
if (Array.isArray(this.options.shardIds)) {
@@ -344,30 +321,31 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
shardIds = [...range({ start, end: end + 1 })];
}
} else {
const data = await this.fetchGatewayInformation();
shardIds = [...range(this.options.shardCount ?? data.shards)];
shardIds = [...range(this.options.shardCount ?? this.getGatewayInformation().shards)];
}
this.shardIds = shardIds;
return shardIds;
}
public async connect() {
const shardCount = await this.getShardCount();
// Spawn shards and adjust internal state
await this.updateShardCount(shardCount);
public async connect(options: WebSocketManagerConnectOptions) {
if (!options?.gatewayInformation) {
throw new TypeError('gatewayInformation is required');
}
const shardIds = await this.getShardIds();
const data = await this.fetchGatewayInformation();
if (data.session_start_limit.remaining < shardIds.length) {
this.#gatewayInformation = options.gatewayInformation;
const shardIds = this.getShardIds();
if (options.gatewayInformation.session_start_limit.remaining < shardIds.length) {
this.#gatewayInformation = null;
throw new Error(
`Not enough sessions remaining to spawn ${shardIds.length} shards; only ${
data.session_start_limit.remaining
} remaining; resets at ${new Date(Date.now() + data.session_start_limit.reset_after).toISOString()}`,
options.gatewayInformation.session_start_limit.remaining
} remaining; resets at ${new Date(Date.now() + options.gatewayInformation.session_start_limit.reset_after).toISOString()}`,
);
}
// Spawn shards and adjust internal state
await this.updateShardCount(this.getShardCount());
await this.strategy.connect();
}
@@ -385,8 +363,9 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
* @remarks
* To keep the shard(s) resumable, use the {@link CloseCodes.Resuming} code.
*/
public destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>) {
return this.strategy.destroy(options);
public async destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>) {
await this.strategy.destroy(options);
this.#gatewayInformation = null;
}
public send(shardId: number, payload: GatewaySendPayload) {