@solana/rpc-subscriptions-spec 2.0.0-rc.0 → 2.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -4
- package/dist/index.browser.cjs +199 -79
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.mjs +198 -81
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.native.mjs +198 -81
- package/dist/index.native.mjs.map +1 -1
- package/dist/index.node.cjs +199 -79
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +198 -81
- package/dist/index.node.mjs.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/rpc-subscriptions-api.d.ts +29 -9
- package/dist/types/rpc-subscriptions-api.d.ts.map +1 -1
- package/dist/types/rpc-subscriptions-channel.d.ts +17 -0
- package/dist/types/rpc-subscriptions-channel.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-pubsub-plan.d.ts +25 -0
- package/dist/types/rpc-subscriptions-pubsub-plan.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-request.d.ts +0 -6
- package/dist/types/rpc-subscriptions-request.d.ts.map +1 -1
- package/dist/types/rpc-subscriptions-transport.d.ts +10 -6
- package/dist/types/rpc-subscriptions-transport.d.ts.map +1 -1
- package/dist/types/rpc-subscriptions.d.ts +3 -3
- package/dist/types/rpc-subscriptions.d.ts.map +1 -1
- package/package.json +17 -4
package/README.md
CHANGED
|
@@ -6,12 +6,83 @@
|
|
|
6
6
|
|
|
7
7
|
[code-style-prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square
|
|
8
8
|
[code-style-prettier-url]: https://github.com/prettier/prettier
|
|
9
|
-
[npm-downloads-image]: https://img.shields.io/npm/dm/@solana/rpc-subscriptions-spec/
|
|
10
|
-
[npm-image]: https://img.shields.io/npm/v/@solana/rpc-subscriptions-spec/
|
|
11
|
-
[npm-url]: https://www.npmjs.com/package/@solana/rpc-subscriptions-spec/v/
|
|
9
|
+
[npm-downloads-image]: https://img.shields.io/npm/dm/@solana/rpc-subscriptions-spec/rc.svg?style=flat
|
|
10
|
+
[npm-image]: https://img.shields.io/npm/v/@solana/rpc-subscriptions-spec/rc.svg?style=flat
|
|
11
|
+
[npm-url]: https://www.npmjs.com/package/@solana/rpc-subscriptions-spec/v/rc
|
|
12
12
|
[semantic-release-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
|
13
13
|
[semantic-release-url]: https://github.com/semantic-release/semantic-release
|
|
14
14
|
|
|
15
15
|
# @solana/rpc-subscriptions-spec
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
This package contains types that describe the implementation of the JSON RPC Subscriptions API, as well as methods to create one. 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).
|
|
18
|
+
|
|
19
|
+
This API is designed to be used as follows:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
const rpcSubscriptions =
|
|
23
|
+
// Step 1 - Create an `RpcSubscriptions` instance. This may be stateful.
|
|
24
|
+
createSolanaRpcSubscriptions(mainnet('wss://api.mainnet-beta.solana.com'));
|
|
25
|
+
const response = await rpcSubscriptions
|
|
26
|
+
// Step 2 - Call supported methods on it to produce `PendingRpcSubscriptionsRequest` objects.
|
|
27
|
+
.slotNotifications({ commitment: 'confirmed' })
|
|
28
|
+
// Step 3 - Call the `subscribe()` method on those pending requests to trigger them.
|
|
29
|
+
.subscribe({ abortSignal: AbortSignal.timeout(10_000) });
|
|
30
|
+
// Step 4 - Iterate over the result.
|
|
31
|
+
try {
|
|
32
|
+
for await (const slotNotification of slotNotifications) {
|
|
33
|
+
console.log('Got a slot notification', slotNotification);
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error('The subscription closed unexpectedly', e);
|
|
37
|
+
} finally {
|
|
38
|
+
console.log('We have stopped listening for notifications');
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Types
|
|
43
|
+
|
|
44
|
+
### `RpcSubscriptionsChannel<TOutboundMessage, TInboundMessage>`
|
|
45
|
+
|
|
46
|
+
A channel is a `DataPublisher` that you can subscribe to events of type `RpcSubscriptionChannelEvents<TInboundMessage>`. Additionally, you can use it to send messages of type `TOutboundMessage` back to the remote end by calling the `send(message)` method.
|
|
47
|
+
|
|
48
|
+
### `RpcSubscriptionsChannelCreator<TOutboundMessage, TInboundMessage>`
|
|
49
|
+
|
|
50
|
+
A channel creator is a function that accepts an `AbortSignal`, returns a new `RpcSubscriptionsChannel`, and tears down the channel when the abort signal fires.
|
|
51
|
+
|
|
52
|
+
### `RpcSubscriptionChannelEvents<TInboundMessage>`
|
|
53
|
+
|
|
54
|
+
Subscription channels publish events on two channel names:
|
|
55
|
+
|
|
56
|
+
- `error`: Fires when the channel closes unexpectedly
|
|
57
|
+
- `message`: Fires on every message received from the remote end
|
|
58
|
+
|
|
59
|
+
## Functions
|
|
60
|
+
|
|
61
|
+
### `executeRpcPubSubSubscriptionPlan({ channel, responseTransformer, signal, subscribeRequest, unsubscribeMethodName })`
|
|
62
|
+
|
|
63
|
+
Given a channel, this function executes the particular subscription plan required by the Solana JSON RPC Subscriptions API.
|
|
64
|
+
|
|
65
|
+
1. Calls the `subscribeRequest` on the remote RPC
|
|
66
|
+
2. Waits for a response containing the subscription id
|
|
67
|
+
3. Returns a `DataPublisher` that publishes notifications related to that subscriptions id, filtering out all others
|
|
68
|
+
4. Calls the `unsubscribeMethodName` on the remote RPC when the abort signal is fired.
|
|
69
|
+
|
|
70
|
+
### `transformChannelInboundMessages(channel, transform)`
|
|
71
|
+
|
|
72
|
+
Given a channel with inbound messages of type `T` and a function of type `T => U`, returns a new channel with inbound messages of type `U`. Note that this only affects messages of type `"message"` and thus, does not affect incoming error messages.
|
|
73
|
+
|
|
74
|
+
For instance, it can be used to parse incoming JSON messages:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const transformedChannel = transformChannelInboundMessages(channel, JSON.parse);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `transformChannelOutboundMessages(channel, transform)`
|
|
81
|
+
|
|
82
|
+
Given a channel with outbound messages of type `T` and a function of type `U => T`, returns a new channel with outbound messages of type `U`.
|
|
83
|
+
|
|
84
|
+
For instance, it can be used to stringify JSON messages before sending them over the wire:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const transformedChannel = transformChannelOutboundMessages(channel, JSON.stringify);
|
|
88
|
+
```
|
package/dist/index.browser.cjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var errors = require('@solana/errors');
|
|
4
|
+
var subscribable = require('@solana/subscribable');
|
|
5
|
+
var promises = require('@solana/promises');
|
|
4
6
|
var rpcSpecTypes = require('@solana/rpc-spec-types');
|
|
5
7
|
|
|
6
8
|
// src/rpc-subscriptions.ts
|
|
7
9
|
function createSubscriptionRpc(rpcConfig) {
|
|
8
|
-
return makeProxy(rpcConfig);
|
|
9
|
-
}
|
|
10
|
-
function makeProxy(rpcConfig) {
|
|
11
10
|
return new Proxy(rpcConfig.api, {
|
|
12
11
|
defineProperty() {
|
|
13
12
|
return false;
|
|
@@ -18,82 +17,31 @@ function makeProxy(rpcConfig) {
|
|
|
18
17
|
get(target, p, receiver) {
|
|
19
18
|
return function(...rawParams) {
|
|
20
19
|
const notificationName = p.toString();
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
throw new errors.SolanaError(errors.
|
|
20
|
+
const createRpcSubscriptionPlan = Reflect.get(target, notificationName, receiver);
|
|
21
|
+
if (!createRpcSubscriptionPlan) {
|
|
22
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_PLAN, {
|
|
24
23
|
notificationName
|
|
25
24
|
});
|
|
26
25
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
subscribeMethodName: notificationName.replace(/Notifications$/, "Subscribe"),
|
|
30
|
-
unsubscribeMethodName: notificationName.replace(/Notifications$/, "Unsubscribe")
|
|
31
|
-
};
|
|
32
|
-
return createPendingRpcSubscription(rpcConfig, newRequest);
|
|
26
|
+
const subscriptionPlan = createRpcSubscriptionPlan(...rawParams);
|
|
27
|
+
return createPendingRpcSubscription(rpcConfig.transport, subscriptionPlan);
|
|
33
28
|
};
|
|
34
29
|
}
|
|
35
30
|
});
|
|
36
31
|
}
|
|
37
|
-
function
|
|
38
|
-
(async () => {
|
|
39
|
-
try {
|
|
40
|
-
for await (const _ of iterable) ;
|
|
41
|
-
} catch {
|
|
42
|
-
} finally {
|
|
43
|
-
cleanupFn();
|
|
44
|
-
}
|
|
45
|
-
})();
|
|
46
|
-
}
|
|
47
|
-
function createPendingRpcSubscription(rpcConfig, { params, subscribeMethodName, unsubscribeMethodName, responseTransformer }) {
|
|
32
|
+
function createPendingRpcSubscription(transport, subscriptionsPlan) {
|
|
48
33
|
return {
|
|
49
34
|
async subscribe({ abortSignal }) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
connectionAbortController.abort();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
abortSignal.addEventListener("abort", handleCleanup);
|
|
63
|
-
const connectionAbortController = new AbortController();
|
|
64
|
-
const subscribeMessage = rpcSpecTypes.createRpcMessage(subscribeMethodName, params);
|
|
65
|
-
const connection = await rpcConfig.transport({
|
|
66
|
-
payload: subscribeMessage,
|
|
67
|
-
signal: connectionAbortController.signal
|
|
35
|
+
const notificationsDataPublisher = await transport({
|
|
36
|
+
signal: abortSignal,
|
|
37
|
+
...subscriptionsPlan
|
|
38
|
+
});
|
|
39
|
+
return subscribable.createAsyncIterableFromDataPublisher({
|
|
40
|
+
abortSignal,
|
|
41
|
+
dataChannelName: "notification",
|
|
42
|
+
dataPublisher: notificationsDataPublisher,
|
|
43
|
+
errorChannelName: "error"
|
|
68
44
|
});
|
|
69
|
-
function handleConnectionCleanup() {
|
|
70
|
-
abortSignal.removeEventListener("abort", handleCleanup);
|
|
71
|
-
}
|
|
72
|
-
registerIterableCleanup(connection, handleConnectionCleanup);
|
|
73
|
-
for await (const message of connection) {
|
|
74
|
-
if ("id" in message && message.id === subscribeMessage.id) {
|
|
75
|
-
if ("error" in message) {
|
|
76
|
-
throw errors.getSolanaErrorFromJsonRpcError(message.error);
|
|
77
|
-
} else {
|
|
78
|
-
subscriptionId = message.result;
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (subscriptionId == null) {
|
|
84
|
-
throw new errors.SolanaError(errors.SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID);
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
async *[Symbol.asyncIterator]() {
|
|
88
|
-
for await (const message of connection) {
|
|
89
|
-
if (!("params" in message) || message.params.subscription !== subscriptionId) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
const notification = message.params;
|
|
93
|
-
yield responseTransformer ? responseTransformer(notification, subscribeMethodName) : notification;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
45
|
}
|
|
98
46
|
};
|
|
99
47
|
}
|
|
@@ -109,24 +57,196 @@ function createRpcSubscriptionsApi(config) {
|
|
|
109
57
|
},
|
|
110
58
|
get(...args) {
|
|
111
59
|
const [_, p] = args;
|
|
112
|
-
const
|
|
113
|
-
return function(...
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const subscribeMethodName = config?.subscribeNotificationNameTransformer ? config?.subscribeNotificationNameTransformer(notificationName) : notificationName;
|
|
117
|
-
const unsubscribeMethodName = config?.unsubscribeNotificationNameTransformer ? config?.unsubscribeNotificationNameTransformer(notificationName) : notificationName;
|
|
60
|
+
const methodName = p.toString();
|
|
61
|
+
return function(...params) {
|
|
62
|
+
const rawRequest = { methodName, params };
|
|
63
|
+
const request = config.requestTransformer ? config.requestTransformer(rawRequest) : rawRequest;
|
|
118
64
|
return {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
65
|
+
execute(planConfig) {
|
|
66
|
+
return config.planExecutor({ ...planConfig, request });
|
|
67
|
+
},
|
|
68
|
+
request
|
|
123
69
|
};
|
|
124
70
|
};
|
|
125
71
|
}
|
|
126
72
|
});
|
|
127
73
|
}
|
|
128
74
|
|
|
75
|
+
// src/rpc-subscriptions-channel.ts
|
|
76
|
+
function transformChannelInboundMessages(channel, transform) {
|
|
77
|
+
return Object.freeze({
|
|
78
|
+
...channel,
|
|
79
|
+
on(type, subscriber, options) {
|
|
80
|
+
if (type !== "message") {
|
|
81
|
+
return channel.on(
|
|
82
|
+
type,
|
|
83
|
+
subscriber,
|
|
84
|
+
options
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return channel.on(
|
|
88
|
+
"message",
|
|
89
|
+
(message) => subscriber(transform(message)),
|
|
90
|
+
options
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function transformChannelOutboundMessages(channel, transform) {
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
...channel,
|
|
98
|
+
send: (message) => channel.send(transform(message))
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
var subscriberCountBySubscriptionIdByChannel = /* @__PURE__ */ new WeakMap();
|
|
102
|
+
function decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) {
|
|
103
|
+
return augmentSubscriberCountAndReturnNewCount(-1, channel, subscriptionId);
|
|
104
|
+
}
|
|
105
|
+
function incrementSubscriberCount(channel, subscriptionId) {
|
|
106
|
+
augmentSubscriberCountAndReturnNewCount(1, channel, subscriptionId);
|
|
107
|
+
}
|
|
108
|
+
function augmentSubscriberCountAndReturnNewCount(amount, channel, subscriptionId) {
|
|
109
|
+
if (subscriptionId === void 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let subscriberCountBySubscriptionId = subscriberCountBySubscriptionIdByChannel.get(channel);
|
|
113
|
+
if (!subscriberCountBySubscriptionId && amount > 0) {
|
|
114
|
+
subscriberCountBySubscriptionIdByChannel.set(
|
|
115
|
+
channel,
|
|
116
|
+
subscriberCountBySubscriptionId = { [subscriptionId]: 0 }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
if (subscriberCountBySubscriptionId?.[subscriptionId] !== void 0) {
|
|
120
|
+
return subscriberCountBySubscriptionId[subscriptionId] = amount + subscriberCountBySubscriptionId[subscriptionId];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
124
|
+
function getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(channel, subscribeRequest, responseTransformer) {
|
|
125
|
+
let publisherByResponseTransformer = cache.get(channel);
|
|
126
|
+
if (!publisherByResponseTransformer) {
|
|
127
|
+
cache.set(channel, publisherByResponseTransformer = /* @__PURE__ */ new WeakMap());
|
|
128
|
+
}
|
|
129
|
+
const responseTransformerKey = responseTransformer ?? channel;
|
|
130
|
+
let publisher = publisherByResponseTransformer.get(responseTransformerKey);
|
|
131
|
+
if (!publisher) {
|
|
132
|
+
publisherByResponseTransformer.set(
|
|
133
|
+
responseTransformerKey,
|
|
134
|
+
publisher = subscribable.demultiplexDataPublisher(channel, "message", (rawMessage) => {
|
|
135
|
+
const message = rawMessage;
|
|
136
|
+
if (!("method" in message)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const transformedNotification = responseTransformer ? responseTransformer(message.params.result, subscribeRequest) : message.params.result;
|
|
140
|
+
return [`notification:${message.params.subscription}`, transformedNotification];
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return publisher;
|
|
145
|
+
}
|
|
146
|
+
async function executeRpcPubSubSubscriptionPlan({
|
|
147
|
+
channel,
|
|
148
|
+
responseTransformer,
|
|
149
|
+
signal,
|
|
150
|
+
subscribeRequest,
|
|
151
|
+
unsubscribeMethodName
|
|
152
|
+
}) {
|
|
153
|
+
let subscriptionId;
|
|
154
|
+
channel.on(
|
|
155
|
+
"error",
|
|
156
|
+
() => {
|
|
157
|
+
subscriptionId = void 0;
|
|
158
|
+
subscriberCountBySubscriptionIdByChannel.delete(channel);
|
|
159
|
+
},
|
|
160
|
+
{ signal }
|
|
161
|
+
);
|
|
162
|
+
const abortPromise = new Promise((_, reject) => {
|
|
163
|
+
function handleAbort() {
|
|
164
|
+
if (decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) === 0) {
|
|
165
|
+
const unsubscribePayload = rpcSpecTypes.createRpcMessage({
|
|
166
|
+
methodName: unsubscribeMethodName,
|
|
167
|
+
params: [subscriptionId]
|
|
168
|
+
});
|
|
169
|
+
subscriptionId = void 0;
|
|
170
|
+
channel.send(unsubscribePayload).catch(() => {
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
reject(this.reason);
|
|
174
|
+
}
|
|
175
|
+
if (signal.aborted) {
|
|
176
|
+
handleAbort.call(signal);
|
|
177
|
+
} else {
|
|
178
|
+
signal.addEventListener("abort", handleAbort);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
const subscribePayload = rpcSpecTypes.createRpcMessage(subscribeRequest);
|
|
182
|
+
await channel.send(subscribePayload);
|
|
183
|
+
const subscriptionIdPromise = new Promise((resolve, reject) => {
|
|
184
|
+
const abortController = new AbortController();
|
|
185
|
+
signal.addEventListener("abort", abortController.abort.bind(abortController));
|
|
186
|
+
const options = { signal: abortController.signal };
|
|
187
|
+
channel.on(
|
|
188
|
+
"error",
|
|
189
|
+
(err) => {
|
|
190
|
+
abortController.abort();
|
|
191
|
+
reject(err);
|
|
192
|
+
},
|
|
193
|
+
options
|
|
194
|
+
);
|
|
195
|
+
channel.on(
|
|
196
|
+
"message",
|
|
197
|
+
(message) => {
|
|
198
|
+
if (message && typeof message === "object" && "id" in message && message.id === subscribePayload.id) {
|
|
199
|
+
abortController.abort();
|
|
200
|
+
if ("error" in message) {
|
|
201
|
+
reject(errors.getSolanaErrorFromJsonRpcError(message.error));
|
|
202
|
+
} else {
|
|
203
|
+
resolve(message.result);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
options
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
subscriptionId = await promises.safeRace([abortPromise, subscriptionIdPromise]);
|
|
211
|
+
if (subscriptionId == null) {
|
|
212
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID);
|
|
213
|
+
}
|
|
214
|
+
incrementSubscriberCount(channel, subscriptionId);
|
|
215
|
+
const notificationPublisher = getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(
|
|
216
|
+
channel,
|
|
217
|
+
subscribeRequest,
|
|
218
|
+
responseTransformer
|
|
219
|
+
);
|
|
220
|
+
const notificationKey = `notification:${subscriptionId}`;
|
|
221
|
+
return {
|
|
222
|
+
on(type, listener, options) {
|
|
223
|
+
switch (type) {
|
|
224
|
+
case "notification":
|
|
225
|
+
return notificationPublisher.on(
|
|
226
|
+
notificationKey,
|
|
227
|
+
listener,
|
|
228
|
+
options
|
|
229
|
+
);
|
|
230
|
+
case "error":
|
|
231
|
+
return channel.on(
|
|
232
|
+
"error",
|
|
233
|
+
listener,
|
|
234
|
+
options
|
|
235
|
+
);
|
|
236
|
+
default:
|
|
237
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED, {
|
|
238
|
+
channelName: type,
|
|
239
|
+
supportedChannelNames: ["notification", "error"]
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
129
246
|
exports.createRpcSubscriptionsApi = createRpcSubscriptionsApi;
|
|
130
247
|
exports.createSubscriptionRpc = createSubscriptionRpc;
|
|
248
|
+
exports.executeRpcPubSubSubscriptionPlan = executeRpcPubSubSubscriptionPlan;
|
|
249
|
+
exports.transformChannelInboundMessages = transformChannelInboundMessages;
|
|
250
|
+
exports.transformChannelOutboundMessages = transformChannelOutboundMessages;
|
|
131
251
|
//# sourceMappingURL=index.browser.cjs.map
|
|
132
252
|
//# sourceMappingURL=index.browser.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/rpc-subscriptions.ts","../src/rpc-subscriptions-api.ts"],"names":["SolanaError","SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST","createRpcMessage","getSolanaErrorFromJsonRpcError","SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID"],"mappings":"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","file":"index.browser.cjs","sourcesContent":["import {\n SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST,\n SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID,\n SolanaError,\n} from '@solana/errors';\nimport { getSolanaErrorFromJsonRpcError } from '@solana/errors';\nimport {\n Callable,\n createRpcMessage,\n Flatten,\n OverloadImplementations,\n RpcResponse,\n UnionToIntersection,\n} from '@solana/rpc-spec-types';\n\nimport { RpcSubscriptionsApi } from './rpc-subscriptions-api';\nimport {\n PendingRpcSubscriptionsRequest,\n RpcSubscribeOptions,\n RpcSubscriptionsRequest,\n} from './rpc-subscriptions-request';\nimport { RpcSubscriptionsTransport } from './rpc-subscriptions-transport';\n\nexport type RpcSubscriptionsConfig<\n TRpcMethods,\n TRpcSubscriptionsTransport extends RpcSubscriptionsTransport,\n> = Readonly<{\n api: RpcSubscriptionsApi<TRpcMethods>;\n transport: TRpcSubscriptionsTransport;\n}>;\n\nexport type RpcSubscriptions<TRpcSubscriptionsMethods> = {\n [TMethodName in keyof TRpcSubscriptionsMethods]: PendingRpcSubscriptionsRequestBuilder<\n OverloadImplementations<TRpcSubscriptionsMethods, TMethodName>\n >;\n};\n\ntype PendingRpcSubscriptionsRequestBuilder<TSubscriptionMethodImplementations> = UnionToIntersection<\n Flatten<{\n [P in keyof TSubscriptionMethodImplementations]: PendingRpcSubscriptionsRequestReturnTypeMapper<\n TSubscriptionMethodImplementations[P]\n >;\n }>\n>;\n\ntype PendingRpcSubscriptionsRequestReturnTypeMapper<TSubscriptionMethodImplementation> =\n // Check that this property of the TRpcSubscriptionMethods interface is, in fact, a function.\n TSubscriptionMethodImplementation extends Callable\n ? (\n ...args: Parameters<TSubscriptionMethodImplementation>\n ) => PendingRpcSubscriptionsRequest<ReturnType<TSubscriptionMethodImplementation>>\n : never;\n\ntype RpcNotification<TNotification> = Readonly<{\n params: Readonly<{\n result: TNotification;\n subscription: number;\n }>;\n}>;\n\ntype RpcSubscriptionId = number;\n\nexport function createSubscriptionRpc<\n TRpcSubscriptionsApiMethods,\n TRpcSubscriptionsTransport extends RpcSubscriptionsTransport,\n>(\n rpcConfig: RpcSubscriptionsConfig<TRpcSubscriptionsApiMethods, TRpcSubscriptionsTransport>,\n): RpcSubscriptions<TRpcSubscriptionsApiMethods> {\n return makeProxy(rpcConfig);\n}\n\nfunction makeProxy<TRpcSubscriptionsApiMethods, TRpcSubscriptionsTransport extends RpcSubscriptionsTransport>(\n rpcConfig: RpcSubscriptionsConfig<TRpcSubscriptionsApiMethods, TRpcSubscriptionsTransport>,\n): RpcSubscriptions<TRpcSubscriptionsApiMethods> {\n return new Proxy(rpcConfig.api, {\n defineProperty() {\n return false;\n },\n deleteProperty() {\n return false;\n },\n get(target, p, receiver) {\n return function (...rawParams: unknown[]) {\n const notificationName = p.toString();\n const createRpcSubscription = Reflect.get(target, notificationName, receiver);\n if (p.toString().endsWith('Notifications') === false && !createRpcSubscription) {\n throw new SolanaError(SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST, {\n notificationName,\n });\n }\n const newRequest = createRpcSubscription\n ? createRpcSubscription(...rawParams)\n : {\n params: rawParams,\n subscribeMethodName: notificationName.replace(/Notifications$/, 'Subscribe'),\n unsubscribeMethodName: notificationName.replace(/Notifications$/, 'Unsubscribe'),\n };\n return createPendingRpcSubscription(rpcConfig, newRequest);\n };\n },\n }) as RpcSubscriptions<TRpcSubscriptionsApiMethods>;\n}\n\nfunction registerIterableCleanup(iterable: AsyncIterable<unknown>, cleanupFn: CallableFunction) {\n (async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n for await (const _ of iterable);\n } catch {\n /* empty */\n } finally {\n // Run the cleanup function.\n cleanupFn();\n }\n })();\n}\n\nfunction createPendingRpcSubscription<\n TRpcSubscriptionsApiMethods,\n TRpcSubscriptionsTransport extends RpcSubscriptionsTransport,\n TNotification,\n>(\n rpcConfig: RpcSubscriptionsConfig<TRpcSubscriptionsApiMethods, TRpcSubscriptionsTransport>,\n { params, subscribeMethodName, unsubscribeMethodName, responseTransformer }: RpcSubscriptionsRequest<TNotification>,\n): PendingRpcSubscriptionsRequest<TNotification> {\n return {\n async subscribe({ abortSignal }: RpcSubscribeOptions): Promise<AsyncIterable<TNotification>> {\n abortSignal.throwIfAborted();\n let subscriptionId: number | undefined;\n function handleCleanup() {\n if (subscriptionId !== undefined) {\n const payload = createRpcMessage(unsubscribeMethodName, [subscriptionId]);\n connection.send_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(payload).finally(() => {\n connectionAbortController.abort();\n });\n } else {\n connectionAbortController.abort();\n }\n }\n abortSignal.addEventListener('abort', handleCleanup);\n /**\n * STEP 1: Send the subscribe message.\n */\n const connectionAbortController = new AbortController();\n const subscribeMessage = createRpcMessage(subscribeMethodName, params);\n const connection = await rpcConfig.transport({\n payload: subscribeMessage,\n signal: connectionAbortController.signal,\n });\n function handleConnectionCleanup() {\n abortSignal.removeEventListener('abort', handleCleanup);\n }\n registerIterableCleanup(connection, handleConnectionCleanup);\n /**\n * STEP 2: Wait for the acknowledgement from the server with the subscription id.\n */\n for await (const message of connection as AsyncIterable<\n RpcNotification<unknown> | RpcResponse<RpcSubscriptionId>\n >) {\n if ('id' in message && message.id === subscribeMessage.id) {\n if ('error' in message) {\n throw getSolanaErrorFromJsonRpcError(message.error);\n } else {\n subscriptionId = message.result as RpcSubscriptionId;\n break;\n }\n }\n }\n if (subscriptionId == null) {\n throw new SolanaError(SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID);\n }\n /**\n * STEP 3: Return an iterable that yields notifications for this subscription id.\n */\n return {\n async *[Symbol.asyncIterator]() {\n for await (const message of connection as AsyncIterable<\n RpcNotification<unknown> | RpcResponse<RpcSubscriptionId>\n >) {\n if (!('params' in message) || message.params.subscription !== subscriptionId) {\n continue;\n }\n const notification = message.params as TNotification;\n yield responseTransformer\n ? responseTransformer(notification, subscribeMethodName)\n : notification;\n }\n },\n };\n },\n };\n}\n","import { Callable } from '@solana/rpc-spec-types';\n\nimport { RpcSubscriptionsRequest } from './rpc-subscriptions-request';\n\nexport type RpcSubscriptionsApiConfig = Readonly<{\n parametersTransformer?: <T extends unknown[]>(params: T, notificationName: string) => unknown[];\n responseTransformer?: <T>(response: unknown, notificationName: string) => T;\n subscribeNotificationNameTransformer?: (notificationName: string) => string;\n unsubscribeNotificationNameTransformer?: (notificationName: string) => string;\n}>;\n\nexport type RpcSubscriptionsApi<TRpcSubscriptionMethods> = {\n [MethodName in keyof TRpcSubscriptionMethods]: RpcSubscriptionsReturnTypeMapper<\n TRpcSubscriptionMethods[MethodName]\n >;\n};\n\ntype RpcSubscriptionsReturnTypeMapper<TRpcMethod> = TRpcMethod extends Callable\n ? (...rawParams: unknown[]) => RpcSubscriptionsRequest<ReturnType<TRpcMethod>>\n : never;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype RpcSubscriptionsApiMethod = (...args: any) => any;\nexport interface RpcSubscriptionsApiMethods {\n [methodName: string]: RpcSubscriptionsApiMethod;\n}\n\nexport function createRpcSubscriptionsApi<TRpcSubscriptionsApiMethods extends RpcSubscriptionsApiMethods>(\n config?: RpcSubscriptionsApiConfig,\n): RpcSubscriptionsApi<TRpcSubscriptionsApiMethods> {\n return new Proxy({} as RpcSubscriptionsApi<TRpcSubscriptionsApiMethods>, {\n defineProperty() {\n return false;\n },\n deleteProperty() {\n return false;\n },\n get<TNotificationName extends keyof RpcSubscriptionsApi<TRpcSubscriptionsApiMethods>>(\n ...args: Parameters<NonNullable<ProxyHandler<RpcSubscriptionsApi<TRpcSubscriptionsApiMethods>>['get']>>\n ) {\n const [_, p] = args;\n const notificationName = p.toString() as keyof TRpcSubscriptionsApiMethods as string;\n return function (\n ...rawParams: Parameters<\n TRpcSubscriptionsApiMethods[TNotificationName] extends CallableFunction\n ? TRpcSubscriptionsApiMethods[TNotificationName]\n : never\n >\n ): RpcSubscriptionsRequest<ReturnType<TRpcSubscriptionsApiMethods[TNotificationName]>> {\n const params = config?.parametersTransformer\n ? config?.parametersTransformer(rawParams, notificationName)\n : rawParams;\n const responseTransformer = config?.responseTransformer\n ? config?.responseTransformer<ReturnType<TRpcSubscriptionsApiMethods[TNotificationName]>>\n : (rawResponse: unknown) =>\n rawResponse as ReturnType<TRpcSubscriptionsApiMethods[TNotificationName]>;\n const subscribeMethodName = config?.subscribeNotificationNameTransformer\n ? config?.subscribeNotificationNameTransformer(notificationName)\n : notificationName;\n const unsubscribeMethodName = config?.unsubscribeNotificationNameTransformer\n ? config?.unsubscribeNotificationNameTransformer(notificationName)\n : notificationName;\n return {\n params,\n responseTransformer,\n subscribeMethodName,\n unsubscribeMethodName,\n };\n };\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/rpc-subscriptions.ts","../src/rpc-subscriptions-api.ts","../src/rpc-subscriptions-channel.ts","../src/rpc-subscriptions-pubsub-plan.ts"],"names":["SolanaError","SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_PLAN","createAsyncIterableFromDataPublisher","demultiplexDataPublisher","createRpcMessage","getSolanaErrorFromJsonRpcError","safeRace","SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID","SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED"],"mappings":"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","file":"index.browser.cjs","sourcesContent":["import { SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_PLAN, SolanaError } from '@solana/errors';\nimport { Callable, Flatten, OverloadImplementations, UnionToIntersection } from '@solana/rpc-spec-types';\nimport { createAsyncIterableFromDataPublisher } from '@solana/subscribable';\n\nimport { RpcSubscriptionsApi, RpcSubscriptionsPlan } from './rpc-subscriptions-api';\nimport { PendingRpcSubscriptionsRequest, RpcSubscribeOptions } from './rpc-subscriptions-request';\nimport { RpcSubscriptionsTransport } from './rpc-subscriptions-transport';\n\nexport type RpcSubscriptionsConfig<TRpcMethods> = Readonly<{\n api: RpcSubscriptionsApi<TRpcMethods>;\n transport: RpcSubscriptionsTransport;\n}>;\n\nexport type RpcSubscriptions<TRpcSubscriptionsMethods> = {\n [TMethodName in keyof TRpcSubscriptionsMethods]: PendingRpcSubscriptionsRequestBuilder<\n OverloadImplementations<TRpcSubscriptionsMethods, TMethodName>\n >;\n};\n\ntype PendingRpcSubscriptionsRequestBuilder<TSubscriptionMethodImplementations> = UnionToIntersection<\n Flatten<{\n [P in keyof TSubscriptionMethodImplementations]: PendingRpcSubscriptionsRequestReturnTypeMapper<\n TSubscriptionMethodImplementations[P]\n >;\n }>\n>;\n\ntype PendingRpcSubscriptionsRequestReturnTypeMapper<TSubscriptionMethodImplementation> =\n // Check that this property of the TRpcSubscriptionMethods interface is, in fact, a function.\n TSubscriptionMethodImplementation extends Callable\n ? (\n ...args: Parameters<TSubscriptionMethodImplementation>\n ) => PendingRpcSubscriptionsRequest<ReturnType<TSubscriptionMethodImplementation>>\n : never;\n\nexport function createSubscriptionRpc<TRpcSubscriptionsApiMethods>(\n rpcConfig: RpcSubscriptionsConfig<TRpcSubscriptionsApiMethods>,\n): RpcSubscriptions<TRpcSubscriptionsApiMethods> {\n return new Proxy(rpcConfig.api, {\n defineProperty() {\n return false;\n },\n deleteProperty() {\n return false;\n },\n get(target, p, receiver) {\n return function (...rawParams: unknown[]) {\n const notificationName = p.toString();\n const createRpcSubscriptionPlan = Reflect.get(target, notificationName, receiver);\n if (!createRpcSubscriptionPlan) {\n throw new SolanaError(SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_PLAN, {\n notificationName,\n });\n }\n const subscriptionPlan = createRpcSubscriptionPlan(...rawParams);\n return createPendingRpcSubscription(rpcConfig.transport, subscriptionPlan);\n };\n },\n }) as RpcSubscriptions<TRpcSubscriptionsApiMethods>;\n}\n\nfunction createPendingRpcSubscription<TNotification>(\n transport: RpcSubscriptionsTransport,\n subscriptionsPlan: RpcSubscriptionsPlan<TNotification>,\n): PendingRpcSubscriptionsRequest<TNotification> {\n return {\n async subscribe({ abortSignal }: RpcSubscribeOptions): Promise<AsyncIterable<TNotification>> {\n const notificationsDataPublisher = await transport({\n signal: abortSignal,\n ...subscriptionsPlan,\n });\n return createAsyncIterableFromDataPublisher<TNotification>({\n abortSignal,\n dataChannelName: 'notification',\n dataPublisher: notificationsDataPublisher,\n errorChannelName: 'error',\n });\n },\n };\n}\n","import { Callable, RpcRequest, RpcRequestTransformer } from '@solana/rpc-spec-types';\nimport { DataPublisher } from '@solana/subscribable';\n\nimport { RpcSubscriptionsChannel } from './rpc-subscriptions-channel';\nimport { RpcSubscriptionsTransportDataEvents } from './rpc-subscriptions-transport';\n\nexport type RpcSubscriptionsApiConfig<TApiMethods extends RpcSubscriptionsApiMethods> = Readonly<{\n planExecutor: RpcSubscriptionsPlanExecutor<ReturnType<TApiMethods[keyof TApiMethods]>>;\n requestTransformer?: RpcRequestTransformer;\n}>;\n\ntype RpcSubscriptionsPlanExecutor<TNotification> = (\n config: Readonly<{\n channel: RpcSubscriptionsChannel<unknown, unknown>;\n request: RpcRequest;\n signal: AbortSignal;\n }>,\n) => Promise<DataPublisher<RpcSubscriptionsTransportDataEvents<TNotification>>>;\n\nexport type RpcSubscriptionsPlan<TNotification> = Readonly<{\n /**\n * This method may be called with a newly-opened channel or a pre-established channel.\n */\n execute: (\n config: Readonly<{\n channel: RpcSubscriptionsChannel<unknown, unknown>;\n signal: AbortSignal;\n }>,\n ) => Promise<DataPublisher<RpcSubscriptionsTransportDataEvents<TNotification>>>;\n /**\n * This request is used to uniquely identify the subscription.\n * It typically comes from the method name and parameters of the subscription call,\n * after potentially being transformed by the RPC Subscriptions API.\n */\n request: RpcRequest;\n}>;\n\nexport type RpcSubscriptionsApi<TRpcSubscriptionMethods> = {\n [MethodName in keyof TRpcSubscriptionMethods]: RpcSubscriptionsReturnTypeMapper<\n TRpcSubscriptionMethods[MethodName]\n >;\n};\n\ntype RpcSubscriptionsReturnTypeMapper<TRpcMethod> = TRpcMethod extends Callable\n ? (...rawParams: unknown[]) => RpcSubscriptionsPlan<ReturnType<TRpcMethod>>\n : never;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype RpcSubscriptionsApiMethod = (...args: any) => any;\nexport interface RpcSubscriptionsApiMethods {\n [methodName: string]: RpcSubscriptionsApiMethod;\n}\n\nexport function createRpcSubscriptionsApi<TRpcSubscriptionsApiMethods extends RpcSubscriptionsApiMethods>(\n config: RpcSubscriptionsApiConfig<TRpcSubscriptionsApiMethods>,\n): RpcSubscriptionsApi<TRpcSubscriptionsApiMethods> {\n return new Proxy({} as RpcSubscriptionsApi<TRpcSubscriptionsApiMethods>, {\n defineProperty() {\n return false;\n },\n deleteProperty() {\n return false;\n },\n get<TNotificationName extends keyof RpcSubscriptionsApi<TRpcSubscriptionsApiMethods>>(\n ...args: Parameters<NonNullable<ProxyHandler<RpcSubscriptionsApi<TRpcSubscriptionsApiMethods>>['get']>>\n ) {\n const [_, p] = args;\n const methodName = p.toString() as keyof TRpcSubscriptionsApiMethods as string;\n return function (\n ...params: Parameters<\n TRpcSubscriptionsApiMethods[TNotificationName] extends CallableFunction\n ? TRpcSubscriptionsApiMethods[TNotificationName]\n : never\n >\n ): RpcSubscriptionsPlan<ReturnType<TRpcSubscriptionsApiMethods[TNotificationName]>> {\n const rawRequest = { methodName, params };\n const request = config.requestTransformer ? config.requestTransformer(rawRequest) : rawRequest;\n return {\n execute(planConfig) {\n return config.planExecutor({ ...planConfig, request });\n },\n request,\n };\n };\n },\n });\n}\n","import {\n SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED,\n SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CONNECTION_CLOSED,\n SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_FAILED_TO_CONNECT,\n SolanaError,\n} from '@solana/errors';\nimport { DataPublisher } from '@solana/subscribable';\n\ntype RpcSubscriptionsChannelSolanaErrorCode =\n | typeof SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED\n | typeof SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CONNECTION_CLOSED\n | typeof SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_FAILED_TO_CONNECT;\n\nexport type RpcSubscriptionChannelEvents<TInboundMessage> = {\n error: SolanaError<RpcSubscriptionsChannelSolanaErrorCode>;\n message: TInboundMessage;\n};\n\nexport interface RpcSubscriptionsChannel<TOutboundMessage, TInboundMessage>\n extends DataPublisher<RpcSubscriptionChannelEvents<TInboundMessage>> {\n send(message: TOutboundMessage): Promise<void>;\n}\n\nexport type RpcSubscriptionsChannelCreator<TOutboundMessage, TInboundMessage> = (\n config: Readonly<{\n abortSignal: AbortSignal;\n }>,\n) => Promise<RpcSubscriptionsChannel<TOutboundMessage, TInboundMessage>>;\n\nexport function transformChannelInboundMessages<TOutboundMessage, TNewInboundMessage, TInboundMessage>(\n channel: RpcSubscriptionsChannel<TOutboundMessage, TInboundMessage>,\n transform: (message: TInboundMessage) => TNewInboundMessage,\n): RpcSubscriptionsChannel<TOutboundMessage, TNewInboundMessage> {\n return Object.freeze<RpcSubscriptionsChannel<TOutboundMessage, TNewInboundMessage>>({\n ...channel,\n on(type, subscriber, options) {\n if (type !== 'message') {\n return channel.on(\n type,\n subscriber as (data: RpcSubscriptionChannelEvents<TInboundMessage>[typeof type]) => void,\n options,\n );\n }\n return channel.on(\n 'message',\n message => (subscriber as (data: TNewInboundMessage) => void)(transform(message)),\n options,\n );\n },\n });\n}\n\nexport function transformChannelOutboundMessages<TNewOutboundMessage, TOutboundMessage, TInboundMessage>(\n channel: RpcSubscriptionsChannel<TOutboundMessage, TInboundMessage>,\n transform: (message: TNewOutboundMessage) => TOutboundMessage,\n): RpcSubscriptionsChannel<TNewOutboundMessage, TInboundMessage> {\n return Object.freeze<RpcSubscriptionsChannel<TNewOutboundMessage, TInboundMessage>>({\n ...channel,\n send: message => channel.send(transform(message)),\n });\n}\n","import {\n getSolanaErrorFromJsonRpcError,\n SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED,\n SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID,\n SolanaError,\n} from '@solana/errors';\nimport { safeRace } from '@solana/promises';\nimport { createRpcMessage, RpcRequest, RpcResponseData, RpcResponseTransformer } from '@solana/rpc-spec-types';\nimport { DataPublisher } from '@solana/subscribable';\nimport { demultiplexDataPublisher } from '@solana/subscribable';\n\nimport { RpcSubscriptionChannelEvents } from './rpc-subscriptions-channel';\nimport { RpcSubscriptionsChannel } from './rpc-subscriptions-channel';\n\ntype Config<TNotification> = Readonly<{\n channel: RpcSubscriptionsChannel<unknown, RpcNotification<TNotification> | RpcResponseData<RpcSubscriptionId>>;\n responseTransformer?: RpcResponseTransformer;\n signal: AbortSignal;\n subscribeRequest: RpcRequest;\n unsubscribeMethodName: string;\n}>;\n\ntype RpcNotification<TNotification> = Readonly<{\n method: string;\n params: Readonly<{\n result: TNotification;\n subscription: number;\n }>;\n}>;\n\ntype RpcSubscriptionId = number;\n\ntype RpcSubscriptionNotificationEvents<TNotification> = Omit<RpcSubscriptionChannelEvents<TNotification>, 'message'> & {\n notification: TNotification;\n};\n\nconst subscriberCountBySubscriptionIdByChannel = new WeakMap<WeakKey, Record<number, number>>();\nfunction decrementSubscriberCountAndReturnNewCount(channel: WeakKey, subscriptionId?: number): number | undefined {\n return augmentSubscriberCountAndReturnNewCount(-1, channel, subscriptionId);\n}\nfunction incrementSubscriberCount(channel: WeakKey, subscriptionId?: number): void {\n augmentSubscriberCountAndReturnNewCount(1, channel, subscriptionId);\n}\nfunction augmentSubscriberCountAndReturnNewCount(\n amount: -1 | 1,\n channel: WeakKey,\n subscriptionId?: number,\n): number | undefined {\n if (subscriptionId === undefined) {\n return;\n }\n let subscriberCountBySubscriptionId = subscriberCountBySubscriptionIdByChannel.get(channel);\n if (!subscriberCountBySubscriptionId && amount > 0) {\n subscriberCountBySubscriptionIdByChannel.set(\n channel,\n (subscriberCountBySubscriptionId = { [subscriptionId]: 0 }),\n );\n }\n if (subscriberCountBySubscriptionId?.[subscriptionId] !== undefined) {\n return (subscriberCountBySubscriptionId[subscriptionId] =\n amount + subscriberCountBySubscriptionId[subscriptionId]);\n }\n}\n\nconst cache = new WeakMap();\nfunction getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer<TNotification>(\n channel: RpcSubscriptionsChannel<unknown, RpcNotification<TNotification>>,\n subscribeRequest: RpcRequest,\n responseTransformer?: RpcResponseTransformer,\n): DataPublisher<{\n [channelName: `notification:${number}`]: TNotification;\n}> {\n let publisherByResponseTransformer = cache.get(channel);\n if (!publisherByResponseTransformer) {\n cache.set(channel, (publisherByResponseTransformer = new WeakMap()));\n }\n const responseTransformerKey = responseTransformer ?? channel;\n let publisher = publisherByResponseTransformer.get(responseTransformerKey);\n if (!publisher) {\n publisherByResponseTransformer.set(\n responseTransformerKey,\n (publisher = demultiplexDataPublisher(channel, 'message', rawMessage => {\n const message = rawMessage as RpcNotification<unknown> | RpcResponseData<unknown>;\n if (!('method' in message)) {\n return;\n }\n const transformedNotification = responseTransformer\n ? responseTransformer(message.params.result, subscribeRequest)\n : message.params.result;\n return [`notification:${message.params.subscription}`, transformedNotification];\n })),\n );\n }\n return publisher;\n}\n\nexport async function executeRpcPubSubSubscriptionPlan<TNotification>({\n channel,\n responseTransformer,\n signal,\n subscribeRequest,\n unsubscribeMethodName,\n}: Config<TNotification>): Promise<DataPublisher<RpcSubscriptionNotificationEvents<TNotification>>> {\n let subscriptionId: number | undefined;\n channel.on(\n 'error',\n () => {\n // An error on the channel indicates that the subscriptions are dead.\n // There is no longer any sense hanging on to subscription ids.\n // Erasing it here will prevent the unsubscribe code from running.\n subscriptionId = undefined;\n subscriberCountBySubscriptionIdByChannel.delete(channel);\n },\n { signal },\n );\n /**\n * STEP 1\n * Create a promise that rejects if this subscription is aborted and sends\n * the unsubscribe message if the subscription is active at that time.\n */\n const abortPromise = new Promise<never>((_, reject) => {\n function handleAbort(this: AbortSignal) {\n /**\n * Because of https://github.com/solana-labs/solana/pull/18943, two subscriptions for\n * materially the same notification will be coalesced on the server. This means they\n * will be assigned the same subscription id, and will occupy one subscription slot. We\n * must be careful not to send the unsubscribe message until the last subscriber aborts.\n */\n if (decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) === 0) {\n const unsubscribePayload = createRpcMessage({\n methodName: unsubscribeMethodName,\n params: [subscriptionId],\n });\n subscriptionId = undefined;\n channel.send(unsubscribePayload).catch(() => {});\n }\n reject(this.reason);\n }\n if (signal.aborted) {\n handleAbort.call(signal);\n } else {\n signal.addEventListener('abort', handleAbort);\n }\n });\n /**\n * STEP 2\n * Send the subscription request.\n */\n const subscribePayload = createRpcMessage(subscribeRequest);\n await channel.send(subscribePayload);\n /**\n * STEP 3\n * Wait for the acknowledgement from the server with the subscription id.\n */\n const subscriptionIdPromise = new Promise<RpcSubscriptionId>((resolve, reject) => {\n const abortController = new AbortController();\n signal.addEventListener('abort', abortController.abort.bind(abortController));\n const options = { signal: abortController.signal } as const;\n channel.on(\n 'error',\n err => {\n abortController.abort();\n reject(err);\n },\n options,\n );\n channel.on(\n 'message',\n message => {\n if (message && typeof message === 'object' && 'id' in message && message.id === subscribePayload.id) {\n abortController.abort();\n if ('error' in message) {\n reject(getSolanaErrorFromJsonRpcError(message.error));\n } else {\n resolve(message.result);\n }\n }\n },\n options,\n );\n });\n subscriptionId = await safeRace([abortPromise, subscriptionIdPromise]);\n if (subscriptionId == null) {\n throw new SolanaError(SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID);\n }\n incrementSubscriberCount(channel, subscriptionId);\n /**\n * STEP 4\n * Filter out notifications unrelated to this subscription.\n */\n const notificationPublisher = getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(\n channel,\n subscribeRequest,\n responseTransformer,\n );\n const notificationKey = `notification:${subscriptionId}` as const;\n return {\n on(type, listener, options) {\n switch (type) {\n case 'notification':\n return notificationPublisher.on(\n notificationKey,\n listener as (data: RpcSubscriptionNotificationEvents<TNotification>['notification']) => void,\n options,\n );\n case 'error':\n return channel.on(\n 'error',\n listener as (data: RpcSubscriptionNotificationEvents<TNotification>['error']) => void,\n options,\n );\n default:\n throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED, {\n channelName: type,\n supportedChannelNames: ['notification', 'error'],\n });\n }\n },\n };\n}\n"]}
|