@solana/rpc-subscriptions-spec 2.0.0-20241006045741
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/LICENSE +20 -0
- package/README.md +68 -0
- package/dist/index.browser.cjs +232 -0
- package/dist/index.browser.cjs.map +1 -0
- package/dist/index.browser.mjs +228 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.native.mjs +228 -0
- package/dist/index.native.mjs.map +1 -0
- package/dist/index.node.cjs +232 -0
- package/dist/index.node.cjs.map +1 -0
- package/dist/index.node.mjs +228 -0
- package/dist/index.node.mjs.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-api.d.ts +42 -0
- package/dist/types/rpc-subscriptions-api.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-channel.d.ts +15 -0
- package/dist/types/rpc-subscriptions-channel.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-pubsub-plan.d.ts +26 -0
- package/dist/types/rpc-subscriptions-pubsub-plan.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-request.d.ts +13 -0
- package/dist/types/rpc-subscriptions-request.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions-transport.d.ts +15 -0
- package/dist/types/rpc-subscriptions-transport.d.ts.map +1 -0
- package/dist/types/rpc-subscriptions.d.ts +18 -0
- package/dist/types/rpc-subscriptions.d.ts.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { SolanaError, SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_PLAN, getSolanaErrorFromJsonRpcError, SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID, SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED } from '@solana/errors';
|
|
2
|
+
import { createAsyncIterableFromDataPublisher, demultiplexDataPublisher } from '@solana/subscribable';
|
|
3
|
+
import { safeRace } from '@solana/promises';
|
|
4
|
+
import { createRpcMessage } from '@solana/rpc-spec-types';
|
|
5
|
+
|
|
6
|
+
// src/rpc-subscriptions.ts
|
|
7
|
+
function createSubscriptionRpc(rpcConfig) {
|
|
8
|
+
return new Proxy(rpcConfig.api, {
|
|
9
|
+
defineProperty() {
|
|
10
|
+
return false;
|
|
11
|
+
},
|
|
12
|
+
deleteProperty() {
|
|
13
|
+
return false;
|
|
14
|
+
},
|
|
15
|
+
get(target, p, receiver) {
|
|
16
|
+
return function(...rawParams) {
|
|
17
|
+
const notificationName = p.toString();
|
|
18
|
+
const createRpcSubscriptionPlan = Reflect.get(target, notificationName, receiver);
|
|
19
|
+
if (!createRpcSubscriptionPlan) {
|
|
20
|
+
throw new SolanaError(SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_PLAN, {
|
|
21
|
+
notificationName
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const subscriptionPlan = createRpcSubscriptionPlan(...rawParams);
|
|
25
|
+
return createPendingRpcSubscription(rpcConfig.transport, subscriptionPlan);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function createPendingRpcSubscription(transport, subscriptionsPlan) {
|
|
31
|
+
return {
|
|
32
|
+
async subscribe({ abortSignal }) {
|
|
33
|
+
const notificationsDataPublisher = await transport({
|
|
34
|
+
signal: abortSignal,
|
|
35
|
+
...subscriptionsPlan
|
|
36
|
+
});
|
|
37
|
+
return createAsyncIterableFromDataPublisher({
|
|
38
|
+
abortSignal,
|
|
39
|
+
dataChannelName: "notification",
|
|
40
|
+
dataPublisher: notificationsDataPublisher,
|
|
41
|
+
errorChannelName: "error"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/rpc-subscriptions-api.ts
|
|
48
|
+
var UNINITIALIZED = Symbol();
|
|
49
|
+
function createRpcSubscriptionsApi(config) {
|
|
50
|
+
return new Proxy({}, {
|
|
51
|
+
defineProperty() {
|
|
52
|
+
return false;
|
|
53
|
+
},
|
|
54
|
+
deleteProperty() {
|
|
55
|
+
return false;
|
|
56
|
+
},
|
|
57
|
+
get(...args) {
|
|
58
|
+
const [_, p] = args;
|
|
59
|
+
const notificationName = p.toString();
|
|
60
|
+
return function(...params) {
|
|
61
|
+
let _cachedSubscriptionHash = UNINITIALIZED;
|
|
62
|
+
return {
|
|
63
|
+
executeSubscriptionPlan(planConfig) {
|
|
64
|
+
return config.planExecutor({
|
|
65
|
+
...planConfig,
|
|
66
|
+
notificationName,
|
|
67
|
+
params
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
get subscriptionConfigurationHash() {
|
|
71
|
+
if (_cachedSubscriptionHash === UNINITIALIZED) {
|
|
72
|
+
_cachedSubscriptionHash = config?.getSubscriptionConfigurationHash?.({
|
|
73
|
+
notificationName,
|
|
74
|
+
params
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return _cachedSubscriptionHash;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
var subscriberCountBySubscriptionIdByChannel = /* @__PURE__ */ new WeakMap();
|
|
85
|
+
function decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) {
|
|
86
|
+
return augmentSubscriberCountAndReturnNewCount(-1, channel, subscriptionId);
|
|
87
|
+
}
|
|
88
|
+
function incrementSubscriberCount(channel, subscriptionId) {
|
|
89
|
+
augmentSubscriberCountAndReturnNewCount(1, channel, subscriptionId);
|
|
90
|
+
}
|
|
91
|
+
function augmentSubscriberCountAndReturnNewCount(amount, channel, subscriptionId) {
|
|
92
|
+
if (subscriptionId === void 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
let subscriberCountBySubscriptionId = subscriberCountBySubscriptionIdByChannel.get(channel);
|
|
96
|
+
if (!subscriberCountBySubscriptionId && amount > 0) {
|
|
97
|
+
subscriberCountBySubscriptionIdByChannel.set(
|
|
98
|
+
channel,
|
|
99
|
+
subscriberCountBySubscriptionId = { [subscriptionId]: 0 }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (subscriberCountBySubscriptionId[subscriptionId] !== void 0) {
|
|
103
|
+
return subscriberCountBySubscriptionId[subscriptionId] = amount + subscriberCountBySubscriptionId[subscriptionId];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
107
|
+
function getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(channel, responseTransformer) {
|
|
108
|
+
let publisherByResponseTransformer = cache.get(channel);
|
|
109
|
+
if (!publisherByResponseTransformer) {
|
|
110
|
+
cache.set(channel, publisherByResponseTransformer = /* @__PURE__ */ new WeakMap());
|
|
111
|
+
}
|
|
112
|
+
const responseTransformerKey = responseTransformer ?? channel;
|
|
113
|
+
let publisher = publisherByResponseTransformer.get(responseTransformerKey);
|
|
114
|
+
if (!publisher) {
|
|
115
|
+
publisherByResponseTransformer.set(
|
|
116
|
+
responseTransformerKey,
|
|
117
|
+
publisher = demultiplexDataPublisher(channel, "message", (rawMessage) => {
|
|
118
|
+
const message = rawMessage;
|
|
119
|
+
if (!("method" in message)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const transformedNotification = responseTransformer ? responseTransformer(message.params.result, message.method) : message.params.result;
|
|
123
|
+
return [`notification:${message.params.subscription}`, transformedNotification];
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return publisher;
|
|
128
|
+
}
|
|
129
|
+
async function executeRpcPubSubSubscriptionPlan({
|
|
130
|
+
channel,
|
|
131
|
+
responseTransformer,
|
|
132
|
+
signal,
|
|
133
|
+
subscribeMethodName,
|
|
134
|
+
subscribeParams,
|
|
135
|
+
unsubscribeMethodName
|
|
136
|
+
}) {
|
|
137
|
+
let subscriptionId;
|
|
138
|
+
channel.on(
|
|
139
|
+
"error",
|
|
140
|
+
() => {
|
|
141
|
+
subscriptionId = void 0;
|
|
142
|
+
subscriberCountBySubscriptionIdByChannel.delete(channel);
|
|
143
|
+
},
|
|
144
|
+
{ signal }
|
|
145
|
+
);
|
|
146
|
+
const abortPromise = new Promise((_, reject) => {
|
|
147
|
+
function handleAbort() {
|
|
148
|
+
if (decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) === 0) {
|
|
149
|
+
const unsubscribePayload = createRpcMessage(unsubscribeMethodName, [subscriptionId]);
|
|
150
|
+
subscriptionId = void 0;
|
|
151
|
+
channel.send(unsubscribePayload).catch(() => {
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
reject(this.reason);
|
|
155
|
+
}
|
|
156
|
+
if (signal.aborted) {
|
|
157
|
+
handleAbort.call(signal);
|
|
158
|
+
} else {
|
|
159
|
+
signal.addEventListener("abort", handleAbort);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
const subscribePayload = createRpcMessage(subscribeMethodName, subscribeParams);
|
|
163
|
+
await channel.send(subscribePayload);
|
|
164
|
+
const subscriptionIdPromise = new Promise((resolve, reject) => {
|
|
165
|
+
const abortController = new AbortController();
|
|
166
|
+
signal.addEventListener("abort", abortController.abort.bind(abortController));
|
|
167
|
+
const options = { signal: abortController.signal };
|
|
168
|
+
channel.on(
|
|
169
|
+
"error",
|
|
170
|
+
(err) => {
|
|
171
|
+
abortController.abort();
|
|
172
|
+
reject(err);
|
|
173
|
+
},
|
|
174
|
+
options
|
|
175
|
+
);
|
|
176
|
+
channel.on(
|
|
177
|
+
"message",
|
|
178
|
+
(message) => {
|
|
179
|
+
if (message && typeof message === "object" && "id" in message && message.id === subscribePayload.id) {
|
|
180
|
+
abortController.abort();
|
|
181
|
+
if ("error" in message) {
|
|
182
|
+
reject(getSolanaErrorFromJsonRpcError(message.error));
|
|
183
|
+
} else {
|
|
184
|
+
resolve(message.result);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
options
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
subscriptionId = await safeRace([abortPromise, subscriptionIdPromise]);
|
|
192
|
+
if (subscriptionId == null) {
|
|
193
|
+
throw new SolanaError(SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID);
|
|
194
|
+
}
|
|
195
|
+
incrementSubscriberCount(channel, subscriptionId);
|
|
196
|
+
const notificationPublisher = getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(
|
|
197
|
+
channel,
|
|
198
|
+
responseTransformer
|
|
199
|
+
);
|
|
200
|
+
const notificationKey = `notification:${subscriptionId}`;
|
|
201
|
+
return {
|
|
202
|
+
on(type, listener, options) {
|
|
203
|
+
switch (type) {
|
|
204
|
+
case "notification":
|
|
205
|
+
return notificationPublisher.on(
|
|
206
|
+
notificationKey,
|
|
207
|
+
listener,
|
|
208
|
+
options
|
|
209
|
+
);
|
|
210
|
+
case "error":
|
|
211
|
+
return channel.on(
|
|
212
|
+
"error",
|
|
213
|
+
listener,
|
|
214
|
+
options
|
|
215
|
+
);
|
|
216
|
+
default:
|
|
217
|
+
throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED, {
|
|
218
|
+
channelName: type,
|
|
219
|
+
supportedChannelNames: ["notification", "error"]
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export { createRpcSubscriptionsApi, createSubscriptionRpc, executeRpcPubSubSubscriptionPlan };
|
|
227
|
+
//# sourceMappingURL=index.native.mjs.map
|
|
228
|
+
//# sourceMappingURL=index.native.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rpc-subscriptions.ts","../src/rpc-subscriptions-api.ts","../src/rpc-subscriptions-pubsub-plan.ts"],"names":["SolanaError"],"mappings":"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","file":"index.native.mjs","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 } 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 getSubscriptionConfigurationHash?: (\n details: Readonly<{\n notificationName: string;\n params: unknown;\n }>,\n ) => string | undefined;\n planExecutor: RpcSubscriptionsPlanExecutor<ReturnType<TApiMethods[keyof TApiMethods]>>;\n}>;\n\ntype RpcSubscriptionsPlanExecutor<TNotification> = (\n config: Readonly<{\n channel: RpcSubscriptionsChannel<unknown, unknown>;\n notificationName: string;\n params?: unknown[];\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 executeSubscriptionPlan: (\n config: Readonly<{\n channel: RpcSubscriptionsChannel<unknown, unknown>;\n signal: AbortSignal;\n }>,\n ) => Promise<DataPublisher<RpcSubscriptionsTransportDataEvents<TNotification>>>;\n /**\n * This hash uniquely identifies the configuration of a subscription. It is typically used by\n * consumers of this API to deduplicate multiple subscriptions for the same notification.\n */\n subscriptionConfigurationHash: string | undefined;\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\nconst UNINITIALIZED = Symbol();\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 notificationName = 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 let _cachedSubscriptionHash: string | typeof UNINITIALIZED | undefined = UNINITIALIZED;\n return {\n executeSubscriptionPlan(planConfig) {\n return config.planExecutor({\n ...planConfig,\n notificationName,\n params,\n });\n },\n get subscriptionConfigurationHash() {\n if (_cachedSubscriptionHash === UNINITIALIZED) {\n _cachedSubscriptionHash = config?.getSubscriptionConfigurationHash?.({\n notificationName,\n params,\n });\n }\n return _cachedSubscriptionHash;\n },\n };\n };\n },\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, RpcResponseData } 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?: <T>(response: unknown, notificationName: string) => T;\n signal: AbortSignal;\n subscribeMethodName: string;\n subscribeParams?: unknown[];\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();\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 responseTransformer?: <T>(response: unknown, notificationName: string) => T,\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, message.method)\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 subscribeMethodName,\n subscribeParams,\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(unsubscribeMethodName, [subscriptionId]);\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(subscribeMethodName, subscribeParams);\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 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"]}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var errors = require('@solana/errors');
|
|
4
|
+
var subscribable = require('@solana/subscribable');
|
|
5
|
+
var promises = require('@solana/promises');
|
|
6
|
+
var rpcSpecTypes = require('@solana/rpc-spec-types');
|
|
7
|
+
|
|
8
|
+
// src/rpc-subscriptions.ts
|
|
9
|
+
function createSubscriptionRpc(rpcConfig) {
|
|
10
|
+
return new Proxy(rpcConfig.api, {
|
|
11
|
+
defineProperty() {
|
|
12
|
+
return false;
|
|
13
|
+
},
|
|
14
|
+
deleteProperty() {
|
|
15
|
+
return false;
|
|
16
|
+
},
|
|
17
|
+
get(target, p, receiver) {
|
|
18
|
+
return function(...rawParams) {
|
|
19
|
+
const notificationName = p.toString();
|
|
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, {
|
|
23
|
+
notificationName
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const subscriptionPlan = createRpcSubscriptionPlan(...rawParams);
|
|
27
|
+
return createPendingRpcSubscription(rpcConfig.transport, subscriptionPlan);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function createPendingRpcSubscription(transport, subscriptionsPlan) {
|
|
33
|
+
return {
|
|
34
|
+
async subscribe({ abortSignal }) {
|
|
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"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/rpc-subscriptions-api.ts
|
|
50
|
+
var UNINITIALIZED = Symbol();
|
|
51
|
+
function createRpcSubscriptionsApi(config) {
|
|
52
|
+
return new Proxy({}, {
|
|
53
|
+
defineProperty() {
|
|
54
|
+
return false;
|
|
55
|
+
},
|
|
56
|
+
deleteProperty() {
|
|
57
|
+
return false;
|
|
58
|
+
},
|
|
59
|
+
get(...args) {
|
|
60
|
+
const [_, p] = args;
|
|
61
|
+
const notificationName = p.toString();
|
|
62
|
+
return function(...params) {
|
|
63
|
+
let _cachedSubscriptionHash = UNINITIALIZED;
|
|
64
|
+
return {
|
|
65
|
+
executeSubscriptionPlan(planConfig) {
|
|
66
|
+
return config.planExecutor({
|
|
67
|
+
...planConfig,
|
|
68
|
+
notificationName,
|
|
69
|
+
params
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
get subscriptionConfigurationHash() {
|
|
73
|
+
if (_cachedSubscriptionHash === UNINITIALIZED) {
|
|
74
|
+
_cachedSubscriptionHash = config?.getSubscriptionConfigurationHash?.({
|
|
75
|
+
notificationName,
|
|
76
|
+
params
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return _cachedSubscriptionHash;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
var subscriberCountBySubscriptionIdByChannel = /* @__PURE__ */ new WeakMap();
|
|
87
|
+
function decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) {
|
|
88
|
+
return augmentSubscriberCountAndReturnNewCount(-1, channel, subscriptionId);
|
|
89
|
+
}
|
|
90
|
+
function incrementSubscriberCount(channel, subscriptionId) {
|
|
91
|
+
augmentSubscriberCountAndReturnNewCount(1, channel, subscriptionId);
|
|
92
|
+
}
|
|
93
|
+
function augmentSubscriberCountAndReturnNewCount(amount, channel, subscriptionId) {
|
|
94
|
+
if (subscriptionId === void 0) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let subscriberCountBySubscriptionId = subscriberCountBySubscriptionIdByChannel.get(channel);
|
|
98
|
+
if (!subscriberCountBySubscriptionId && amount > 0) {
|
|
99
|
+
subscriberCountBySubscriptionIdByChannel.set(
|
|
100
|
+
channel,
|
|
101
|
+
subscriberCountBySubscriptionId = { [subscriptionId]: 0 }
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (subscriberCountBySubscriptionId[subscriptionId] !== void 0) {
|
|
105
|
+
return subscriberCountBySubscriptionId[subscriptionId] = amount + subscriberCountBySubscriptionId[subscriptionId];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
109
|
+
function getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(channel, responseTransformer) {
|
|
110
|
+
let publisherByResponseTransformer = cache.get(channel);
|
|
111
|
+
if (!publisherByResponseTransformer) {
|
|
112
|
+
cache.set(channel, publisherByResponseTransformer = /* @__PURE__ */ new WeakMap());
|
|
113
|
+
}
|
|
114
|
+
const responseTransformerKey = responseTransformer ?? channel;
|
|
115
|
+
let publisher = publisherByResponseTransformer.get(responseTransformerKey);
|
|
116
|
+
if (!publisher) {
|
|
117
|
+
publisherByResponseTransformer.set(
|
|
118
|
+
responseTransformerKey,
|
|
119
|
+
publisher = subscribable.demultiplexDataPublisher(channel, "message", (rawMessage) => {
|
|
120
|
+
const message = rawMessage;
|
|
121
|
+
if (!("method" in message)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const transformedNotification = responseTransformer ? responseTransformer(message.params.result, message.method) : message.params.result;
|
|
125
|
+
return [`notification:${message.params.subscription}`, transformedNotification];
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return publisher;
|
|
130
|
+
}
|
|
131
|
+
async function executeRpcPubSubSubscriptionPlan({
|
|
132
|
+
channel,
|
|
133
|
+
responseTransformer,
|
|
134
|
+
signal,
|
|
135
|
+
subscribeMethodName,
|
|
136
|
+
subscribeParams,
|
|
137
|
+
unsubscribeMethodName
|
|
138
|
+
}) {
|
|
139
|
+
let subscriptionId;
|
|
140
|
+
channel.on(
|
|
141
|
+
"error",
|
|
142
|
+
() => {
|
|
143
|
+
subscriptionId = void 0;
|
|
144
|
+
subscriberCountBySubscriptionIdByChannel.delete(channel);
|
|
145
|
+
},
|
|
146
|
+
{ signal }
|
|
147
|
+
);
|
|
148
|
+
const abortPromise = new Promise((_, reject) => {
|
|
149
|
+
function handleAbort() {
|
|
150
|
+
if (decrementSubscriberCountAndReturnNewCount(channel, subscriptionId) === 0) {
|
|
151
|
+
const unsubscribePayload = rpcSpecTypes.createRpcMessage(unsubscribeMethodName, [subscriptionId]);
|
|
152
|
+
subscriptionId = void 0;
|
|
153
|
+
channel.send(unsubscribePayload).catch(() => {
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
reject(this.reason);
|
|
157
|
+
}
|
|
158
|
+
if (signal.aborted) {
|
|
159
|
+
handleAbort.call(signal);
|
|
160
|
+
} else {
|
|
161
|
+
signal.addEventListener("abort", handleAbort);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
const subscribePayload = rpcSpecTypes.createRpcMessage(subscribeMethodName, subscribeParams);
|
|
165
|
+
await channel.send(subscribePayload);
|
|
166
|
+
const subscriptionIdPromise = new Promise((resolve, reject) => {
|
|
167
|
+
const abortController = new AbortController();
|
|
168
|
+
signal.addEventListener("abort", abortController.abort.bind(abortController));
|
|
169
|
+
const options = { signal: abortController.signal };
|
|
170
|
+
channel.on(
|
|
171
|
+
"error",
|
|
172
|
+
(err) => {
|
|
173
|
+
abortController.abort();
|
|
174
|
+
reject(err);
|
|
175
|
+
},
|
|
176
|
+
options
|
|
177
|
+
);
|
|
178
|
+
channel.on(
|
|
179
|
+
"message",
|
|
180
|
+
(message) => {
|
|
181
|
+
if (message && typeof message === "object" && "id" in message && message.id === subscribePayload.id) {
|
|
182
|
+
abortController.abort();
|
|
183
|
+
if ("error" in message) {
|
|
184
|
+
reject(errors.getSolanaErrorFromJsonRpcError(message.error));
|
|
185
|
+
} else {
|
|
186
|
+
resolve(message.result);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
options
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
subscriptionId = await promises.safeRace([abortPromise, subscriptionIdPromise]);
|
|
194
|
+
if (subscriptionId == null) {
|
|
195
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID);
|
|
196
|
+
}
|
|
197
|
+
incrementSubscriberCount(channel, subscriptionId);
|
|
198
|
+
const notificationPublisher = getMemoizedDemultiplexedNotificationPublisherFromChannelAndResponseTransformer(
|
|
199
|
+
channel,
|
|
200
|
+
responseTransformer
|
|
201
|
+
);
|
|
202
|
+
const notificationKey = `notification:${subscriptionId}`;
|
|
203
|
+
return {
|
|
204
|
+
on(type, listener, options) {
|
|
205
|
+
switch (type) {
|
|
206
|
+
case "notification":
|
|
207
|
+
return notificationPublisher.on(
|
|
208
|
+
notificationKey,
|
|
209
|
+
listener,
|
|
210
|
+
options
|
|
211
|
+
);
|
|
212
|
+
case "error":
|
|
213
|
+
return channel.on(
|
|
214
|
+
"error",
|
|
215
|
+
listener,
|
|
216
|
+
options
|
|
217
|
+
);
|
|
218
|
+
default:
|
|
219
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__INVARIANT_VIOLATION__DATA_PUBLISHER_CHANNEL_UNIMPLEMENTED, {
|
|
220
|
+
channelName: type,
|
|
221
|
+
supportedChannelNames: ["notification", "error"]
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
exports.createRpcSubscriptionsApi = createRpcSubscriptionsApi;
|
|
229
|
+
exports.createSubscriptionRpc = createSubscriptionRpc;
|
|
230
|
+
exports.executeRpcPubSubSubscriptionPlan = executeRpcPubSubSubscriptionPlan;
|
|
231
|
+
//# sourceMappingURL=index.node.cjs.map
|
|
232
|
+
//# sourceMappingURL=index.node.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rpc-subscriptions.ts","../src/rpc-subscriptions-api.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.node.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 } 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 getSubscriptionConfigurationHash?: (\n details: Readonly<{\n notificationName: string;\n params: unknown;\n }>,\n ) => string | undefined;\n planExecutor: RpcSubscriptionsPlanExecutor<ReturnType<TApiMethods[keyof TApiMethods]>>;\n}>;\n\ntype RpcSubscriptionsPlanExecutor<TNotification> = (\n config: Readonly<{\n channel: RpcSubscriptionsChannel<unknown, unknown>;\n notificationName: string;\n params?: unknown[];\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 executeSubscriptionPlan: (\n config: Readonly<{\n channel: RpcSubscriptionsChannel<unknown, unknown>;\n signal: AbortSignal;\n }>,\n ) => Promise<DataPublisher<RpcSubscriptionsTransportDataEvents<TNotification>>>;\n /**\n * This hash uniquely identifies the configuration of a subscription. It is typically used by\n * consumers of this API to deduplicate multiple subscriptions for the same notification.\n */\n subscriptionConfigurationHash: string | undefined;\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\nconst UNINITIALIZED = Symbol();\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 notificationName = 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 let _cachedSubscriptionHash: string | typeof UNINITIALIZED | undefined = UNINITIALIZED;\n return {\n executeSubscriptionPlan(planConfig) {\n return config.planExecutor({\n ...planConfig,\n notificationName,\n params,\n });\n },\n get subscriptionConfigurationHash() {\n if (_cachedSubscriptionHash === UNINITIALIZED) {\n _cachedSubscriptionHash = config?.getSubscriptionConfigurationHash?.({\n notificationName,\n params,\n });\n }\n return _cachedSubscriptionHash;\n },\n };\n };\n },\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, RpcResponseData } 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?: <T>(response: unknown, notificationName: string) => T;\n signal: AbortSignal;\n subscribeMethodName: string;\n subscribeParams?: unknown[];\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();\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 responseTransformer?: <T>(response: unknown, notificationName: string) => T,\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, message.method)\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 subscribeMethodName,\n subscribeParams,\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(unsubscribeMethodName, [subscriptionId]);\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(subscribeMethodName, subscribeParams);\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 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"]}
|