diff --git a/packages/rpc-subscriptions/README.md b/packages/rpc-subscriptions/README.md index 0a882fdff66..97e49ae19e4 100644 --- a/packages/rpc-subscriptions/README.md +++ b/packages/rpc-subscriptions/README.md @@ -14,4 +14,10 @@ # @solana/rpc-subscriptions -TODO +This package contains types that implement RPC subscriptions as required by the Solana RPC. Additionally, it incorporates some useful defaults that make working with subscriptions easier, more performant, and more reliable. It can be used standalone, but it is also exported as part of the Solana JavaScript SDK [`@solana/web3.js@rc`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/library). + +## Functions + +### `getRpcSubscriptionsChannelWithJSONSerialization(channel)` + +Given an `RpcSubscriptionsChannel`, will return a new channel that parses data published to the `'message'` channel as JSON, and JSON-stringifies messages sent via the `send(message)` method. diff --git a/packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts b/packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts new file mode 100644 index 00000000000..891cbb85dd5 --- /dev/null +++ b/packages/rpc-subscriptions/src/__tests__/rpc-subscriptions-json-test.ts @@ -0,0 +1,30 @@ +import { RpcSubscriptionsChannel } from '@solana/rpc-subscriptions-spec'; + +import { getRpcSubscriptionsChannelWithJSONSerialization } from '../rpc-subscriptions-json'; + +describe('getRpcSubscriptionsChannelWithJSONSerialization', () => { + let mockOn: jest.Mock; + let channel: RpcSubscriptionsChannel; + function receiveMessage(message: unknown) { + mockOn.mock.calls.filter(([type]) => type === 'message').forEach(([_, listener]) => listener(message)); + } + beforeEach(() => { + mockOn = jest.fn(); + channel = { + on: mockOn, + send: jest.fn(), + }; + }); + it('forwards JSON-serialized messages to the underlying channel', () => { + const channelWithJSONSerialization = getRpcSubscriptionsChannelWithJSONSerialization(channel); + channelWithJSONSerialization.send('hello'); + expect(channel.send).toHaveBeenCalledWith(JSON.stringify('hello')); + }); + it('deserializes messages received from the underlying channel as JSON', () => { + const channelWithJSONSerialization = getRpcSubscriptionsChannelWithJSONSerialization(channel); + const messageListener = jest.fn(); + channelWithJSONSerialization.on('message', messageListener); + receiveMessage(JSON.stringify('hello')); + expect(messageListener).toHaveBeenCalledWith('hello'); + }); +}); diff --git a/packages/rpc-subscriptions/src/rpc-subscriptions-json.ts b/packages/rpc-subscriptions/src/rpc-subscriptions-json.ts new file mode 100644 index 00000000000..2a3d1d7032b --- /dev/null +++ b/packages/rpc-subscriptions/src/rpc-subscriptions-json.ts @@ -0,0 +1,26 @@ +import { RpcSubscriptionsChannel } from '@solana/rpc-subscriptions-spec'; + +export function getRpcSubscriptionsChannelWithJSONSerialization( + channel: RpcSubscriptionsChannel, +): RpcSubscriptionsChannel { + return Object.freeze({ + ...channel, + on(type, listener, options) { + if (type !== 'message') { + return channel.on(type, listener, options); + } + return channel.on( + 'message', + function deserializingListener(message: string) { + const deserializedMessage = JSON.parse(message); + listener(deserializedMessage); + }, + options, + ); + }, + send(message) { + const serializedMessage = JSON.stringify(message); + return channel.send(serializedMessage); + }, + } as RpcSubscriptionsChannel); +}