@pezkuwi/rpc-provider 16.5.17 → 16.5.18

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.
Files changed (210) hide show
  1. package/build/LICENSE +201 -0
  2. package/build/README.md +68 -0
  3. package/build/bizinikiwi-connect/Health.js +259 -0
  4. package/build/bizinikiwi-connect/index.js +319 -0
  5. package/build/bundle.js +5 -0
  6. package/build/cjs/bizinikiwi-connect/Health.d.ts +7 -0
  7. package/build/cjs/bizinikiwi-connect/Health.js +264 -0
  8. package/build/cjs/bizinikiwi-connect/index.d.ts +22 -0
  9. package/build/cjs/bizinikiwi-connect/index.js +323 -0
  10. package/build/cjs/bizinikiwi-connect/types.d.ts +12 -0
  11. package/build/cjs/bizinikiwi-connect/types.js +2 -0
  12. package/build/cjs/bundle.d.ts +5 -0
  13. package/build/cjs/bundle.js +14 -0
  14. package/build/cjs/coder/error.js +53 -0
  15. package/build/cjs/coder/index.js +63 -0
  16. package/build/cjs/defaults.js +8 -0
  17. package/build/cjs/http/index.js +196 -0
  18. package/build/cjs/http/types.js +2 -0
  19. package/build/cjs/index.js +5 -0
  20. package/build/cjs/lru.js +150 -0
  21. package/build/cjs/mock/index.js +196 -0
  22. package/build/cjs/mock/mockHttp.js +17 -0
  23. package/build/cjs/mock/mockWs.js +47 -0
  24. package/build/cjs/mock/types.js +2 -0
  25. package/build/cjs/packageDetect.d.ts +1 -0
  26. package/build/cjs/packageInfo.js +4 -0
  27. package/build/cjs/types.js +2 -0
  28. package/build/cjs/ws/errors.js +41 -0
  29. package/build/cjs/ws/index.js +529 -0
  30. package/build/coder/error.d.ts +29 -0
  31. package/build/coder/error.js +50 -0
  32. package/build/coder/index.d.ts +8 -0
  33. package/build/coder/index.js +58 -0
  34. package/build/defaults.d.ts +5 -0
  35. package/build/defaults.js +6 -0
  36. package/build/http/index.d.ts +81 -0
  37. package/build/http/index.js +191 -0
  38. package/build/http/types.d.ts +7 -0
  39. package/build/http/types.js +1 -0
  40. package/build/index.d.ts +2 -0
  41. package/build/index.js +2 -0
  42. package/build/lru.d.ts +15 -0
  43. package/build/lru.js +146 -0
  44. package/build/mock/index.d.ts +35 -0
  45. package/build/mock/index.js +191 -0
  46. package/build/mock/mockHttp.d.ts +9 -0
  47. package/build/mock/mockHttp.js +12 -0
  48. package/build/mock/mockWs.d.ts +26 -0
  49. package/build/mock/mockWs.js +43 -0
  50. package/build/mock/types.d.ts +23 -0
  51. package/build/mock/types.js +1 -0
  52. package/build/package.json +344 -0
  53. package/build/packageDetect.d.ts +1 -0
  54. package/build/packageDetect.js +4 -0
  55. package/build/packageInfo.d.ts +6 -0
  56. package/build/packageInfo.js +1 -0
  57. package/build/types.d.ts +85 -0
  58. package/build/types.js +1 -0
  59. package/build/ws/errors.d.ts +1 -0
  60. package/build/ws/errors.js +38 -0
  61. package/build/ws/index.d.ts +121 -0
  62. package/build/ws/index.js +524 -0
  63. package/build-deno/README.md +66 -0
  64. package/build-deno/bizinikiwi-connect/Health.ts +323 -0
  65. package/build-deno/bizinikiwi-connect/index.ts +417 -0
  66. package/build-deno/bizinikiwi-connect/types.ts +14 -0
  67. package/build-deno/bundle.ts +6 -0
  68. package/build-deno/coder/error.ts +64 -0
  69. package/build-deno/coder/index.ts +86 -0
  70. package/build-deno/defaults.ts +8 -0
  71. package/build-deno/http/index.ts +236 -0
  72. package/build-deno/http/types.ts +9 -0
  73. package/build-deno/index.ts +4 -0
  74. package/build-deno/lru.ts +189 -0
  75. package/build-deno/mock/index.ts +257 -0
  76. package/build-deno/mock/mockHttp.ts +33 -0
  77. package/build-deno/mock/mockWs.ts +87 -0
  78. package/build-deno/mock/types.ts +34 -0
  79. package/build-deno/mod.ts +2 -0
  80. package/build-deno/packageDetect.ts +8 -0
  81. package/build-deno/packageInfo.ts +3 -0
  82. package/build-deno/types.ts +99 -0
  83. package/build-deno/ws/errors.ts +38 -0
  84. package/build-deno/ws/index.ts +650 -0
  85. package/build-tsc-cjs/packageDetect.js +6 -0
  86. package/{cjs → build-tsc-cjs}/packageInfo.js +1 -1
  87. package/{packageInfo.js → build-tsc-esm/packageInfo.js} +1 -1
  88. package/package.json +16 -16
  89. package/src/bizinikiwi-connect/Health.ts +325 -0
  90. package/src/bizinikiwi-connect/index.spec.ts +675 -0
  91. package/src/bizinikiwi-connect/index.ts +427 -0
  92. package/src/bizinikiwi-connect/types.ts +16 -0
  93. package/src/bundle.ts +8 -0
  94. package/src/coder/decodeResponse.spec.ts +70 -0
  95. package/src/coder/encodeJson.spec.ts +20 -0
  96. package/src/coder/encodeObject.spec.ts +25 -0
  97. package/src/coder/error.spec.ts +111 -0
  98. package/src/coder/error.ts +66 -0
  99. package/src/coder/index.ts +88 -0
  100. package/src/defaults.ts +10 -0
  101. package/src/http/index.spec.ts +72 -0
  102. package/src/http/index.ts +238 -0
  103. package/src/http/send.spec.ts +61 -0
  104. package/src/http/types.ts +11 -0
  105. package/src/index.ts +6 -0
  106. package/src/lru.spec.ts +74 -0
  107. package/src/lru.ts +197 -0
  108. package/src/mock/index.ts +259 -0
  109. package/src/mock/mockHttp.ts +35 -0
  110. package/src/mock/mockWs.ts +92 -0
  111. package/src/mock/on.spec.ts +43 -0
  112. package/src/mock/send.spec.ts +38 -0
  113. package/src/mock/subscribe.spec.ts +81 -0
  114. package/src/mock/types.ts +36 -0
  115. package/src/mock/unsubscribe.spec.ts +57 -0
  116. package/src/mod.ts +4 -0
  117. package/src/packageDetect.ts +12 -0
  118. package/src/packageInfo.ts +6 -0
  119. package/src/types.ts +101 -0
  120. package/src/ws/connect.spec.ts +167 -0
  121. package/src/ws/errors.ts +41 -0
  122. package/src/ws/index.spec.ts +97 -0
  123. package/src/ws/index.ts +652 -0
  124. package/src/ws/send.spec.ts +126 -0
  125. package/src/ws/state.spec.ts +20 -0
  126. package/src/ws/subscribe.spec.ts +68 -0
  127. package/src/ws/unsubscribe.spec.ts +100 -0
  128. package/tsconfig.build.json +17 -0
  129. package/tsconfig.build.tsbuildinfo +1 -0
  130. package/tsconfig.spec.json +18 -0
  131. package/tsconfig.spec.tsbuildinfo +1 -0
  132. /package/{cjs → build}/bizinikiwi-connect/Health.d.ts +0 -0
  133. /package/{cjs → build}/bizinikiwi-connect/index.d.ts +0 -0
  134. /package/{cjs → build}/bizinikiwi-connect/types.d.ts +0 -0
  135. /package/{packageDetect.d.ts → build/bizinikiwi-connect/types.js} +0 -0
  136. /package/{cjs → build}/bundle.d.ts +0 -0
  137. /package/{coder → build/cjs/coder}/error.d.ts +0 -0
  138. /package/{coder → build/cjs/coder}/index.d.ts +0 -0
  139. /package/{defaults.d.ts → build/cjs/defaults.d.ts} +0 -0
  140. /package/{http → build/cjs/http}/index.d.ts +0 -0
  141. /package/{http → build/cjs/http}/types.d.ts +0 -0
  142. /package/{index.d.ts → build/cjs/index.d.ts} +0 -0
  143. /package/{lru.d.ts → build/cjs/lru.d.ts} +0 -0
  144. /package/{mock → build/cjs/mock}/index.d.ts +0 -0
  145. /package/{mock → build/cjs/mock}/mockHttp.d.ts +0 -0
  146. /package/{mock → build/cjs/mock}/mockWs.d.ts +0 -0
  147. /package/{mock → build/cjs/mock}/types.d.ts +0 -0
  148. /package/{cjs → build/cjs}/package.json +0 -0
  149. /package/{cjs → build/cjs}/packageDetect.js +0 -0
  150. /package/{packageInfo.d.ts → build/cjs/packageInfo.d.ts} +0 -0
  151. /package/{types.d.ts → build/cjs/types.d.ts} +0 -0
  152. /package/{ws → build/cjs/ws}/errors.d.ts +0 -0
  153. /package/{ws → build/cjs/ws}/index.d.ts +0 -0
  154. /package/{bizinikiwi-connect → build-tsc/bizinikiwi-connect}/Health.d.ts +0 -0
  155. /package/{bizinikiwi-connect → build-tsc/bizinikiwi-connect}/index.d.ts +0 -0
  156. /package/{bizinikiwi-connect → build-tsc/bizinikiwi-connect}/types.d.ts +0 -0
  157. /package/{bundle.d.ts → build-tsc/bundle.d.ts} +0 -0
  158. /package/{cjs → build-tsc}/coder/error.d.ts +0 -0
  159. /package/{cjs → build-tsc}/coder/index.d.ts +0 -0
  160. /package/{cjs → build-tsc}/defaults.d.ts +0 -0
  161. /package/{cjs → build-tsc}/http/index.d.ts +0 -0
  162. /package/{cjs → build-tsc}/http/types.d.ts +0 -0
  163. /package/{cjs → build-tsc}/index.d.ts +0 -0
  164. /package/{cjs → build-tsc}/lru.d.ts +0 -0
  165. /package/{cjs → build-tsc}/mock/index.d.ts +0 -0
  166. /package/{cjs → build-tsc}/mock/mockHttp.d.ts +0 -0
  167. /package/{cjs → build-tsc}/mock/mockWs.d.ts +0 -0
  168. /package/{cjs → build-tsc}/mock/types.d.ts +0 -0
  169. /package/{cjs → build-tsc}/packageDetect.d.ts +0 -0
  170. /package/{cjs → build-tsc}/packageInfo.d.ts +0 -0
  171. /package/{cjs → build-tsc}/types.d.ts +0 -0
  172. /package/{cjs → build-tsc}/ws/errors.d.ts +0 -0
  173. /package/{cjs → build-tsc}/ws/index.d.ts +0 -0
  174. /package/{cjs → build-tsc-cjs}/bizinikiwi-connect/Health.js +0 -0
  175. /package/{cjs → build-tsc-cjs}/bizinikiwi-connect/index.js +0 -0
  176. /package/{cjs → build-tsc-cjs}/bizinikiwi-connect/types.js +0 -0
  177. /package/{cjs → build-tsc-cjs}/bundle.js +0 -0
  178. /package/{cjs → build-tsc-cjs}/coder/error.js +0 -0
  179. /package/{cjs → build-tsc-cjs}/coder/index.js +0 -0
  180. /package/{cjs → build-tsc-cjs}/defaults.js +0 -0
  181. /package/{cjs → build-tsc-cjs}/http/index.js +0 -0
  182. /package/{cjs → build-tsc-cjs}/http/types.js +0 -0
  183. /package/{cjs → build-tsc-cjs}/index.js +0 -0
  184. /package/{cjs → build-tsc-cjs}/lru.js +0 -0
  185. /package/{cjs → build-tsc-cjs}/mock/index.js +0 -0
  186. /package/{cjs → build-tsc-cjs}/mock/mockHttp.js +0 -0
  187. /package/{cjs → build-tsc-cjs}/mock/mockWs.js +0 -0
  188. /package/{cjs → build-tsc-cjs}/mock/types.js +0 -0
  189. /package/{cjs → build-tsc-cjs}/types.js +0 -0
  190. /package/{cjs → build-tsc-cjs}/ws/errors.js +0 -0
  191. /package/{cjs → build-tsc-cjs}/ws/index.js +0 -0
  192. /package/{bizinikiwi-connect → build-tsc-esm/bizinikiwi-connect}/Health.js +0 -0
  193. /package/{bizinikiwi-connect → build-tsc-esm/bizinikiwi-connect}/index.js +0 -0
  194. /package/{bizinikiwi-connect → build-tsc-esm/bizinikiwi-connect}/types.js +0 -0
  195. /package/{bundle.js → build-tsc-esm/bundle.js} +0 -0
  196. /package/{coder → build-tsc-esm/coder}/error.js +0 -0
  197. /package/{coder → build-tsc-esm/coder}/index.js +0 -0
  198. /package/{defaults.js → build-tsc-esm/defaults.js} +0 -0
  199. /package/{http → build-tsc-esm/http}/index.js +0 -0
  200. /package/{http → build-tsc-esm/http}/types.js +0 -0
  201. /package/{index.js → build-tsc-esm/index.js} +0 -0
  202. /package/{lru.js → build-tsc-esm/lru.js} +0 -0
  203. /package/{mock → build-tsc-esm/mock}/index.js +0 -0
  204. /package/{mock → build-tsc-esm/mock}/mockHttp.js +0 -0
  205. /package/{mock → build-tsc-esm/mock}/mockWs.js +0 -0
  206. /package/{mock → build-tsc-esm/mock}/types.js +0 -0
  207. /package/{packageDetect.js → build-tsc-esm/packageDetect.js} +0 -0
  208. /package/{types.js → build-tsc-esm/types.js} +0 -0
  209. /package/{ws → build-tsc-esm/ws}/errors.js +0 -0
  210. /package/{ws → build-tsc-esm/ws}/index.js +0 -0
@@ -0,0 +1,417 @@
1
+
2
+ import type * as ScType from 'https://esm.sh/@bizinikiwi/connect@2.1.9';
3
+ import type { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback, ProviderInterfaceEmitCb, ProviderInterfaceEmitted } from '../types.ts';
4
+
5
+ import { EventEmitter } from 'https://esm.sh/eventemitter3@5.0.1';
6
+
7
+ import { isError, isFunction, isObject, logger, noop, objectSpread } from 'https://deno.land/x/pezkuwi/util/mod.ts';
8
+
9
+ import { RpcCoder } from '../coder/index.ts';
10
+ import { healthChecker } from './Health.ts';
11
+
12
+ type ResponseCallback = (response: string | Error) => void;
13
+
14
+ interface BizinikiwiConnect {
15
+ WellKnownChain: typeof ScType['WellKnownChain'];
16
+ createScClient: typeof ScType['createScClient'];
17
+ }
18
+
19
+ const l = logger('api-bizinikiwi-connect');
20
+
21
+ const subscriptionUnsubscriptionMethods = new Map<string, string>([
22
+ ['author_submitAndWatchExtrinsic', 'author_unwatchExtrinsic'],
23
+ ['chain_subscribeAllHeads', 'chain_unsubscribeAllHeads'],
24
+ ['chain_subscribeFinalizedHeads', 'chain_unsubscribeFinalizedHeads'],
25
+ ['chain_subscribeFinalisedHeads', 'chain_subscribeFinalisedHeads'],
26
+ ['chain_subscribeNewHeads', 'chain_unsubscribeNewHeads'],
27
+ ['chain_subscribeNewHead', 'chain_unsubscribeNewHead'],
28
+ ['chain_subscribeRuntimeVersion', 'chain_unsubscribeRuntimeVersion'],
29
+ ['subscribe_newHead', 'unsubscribe_newHead'],
30
+ ['state_subscribeRuntimeVersion', 'state_unsubscribeRuntimeVersion'],
31
+ ['state_subscribeStorage', 'state_unsubscribeStorage']
32
+ ]);
33
+
34
+ const scClients = new WeakMap<ScProvider, ScType.ScClient>();
35
+
36
+ interface ActiveSubs {
37
+ type: string,
38
+ method: string,
39
+ params: any[],
40
+ callback: ProviderInterfaceCallback
41
+ }
42
+
43
+ export class ScProvider implements ProviderInterface {
44
+ readonly #Sc: BizinikiwiConnect;
45
+ readonly #coder: RpcCoder = new RpcCoder();
46
+ readonly #spec: string | ScType.WellKnownChain;
47
+ readonly #sharedSandbox?: ScProvider | undefined;
48
+ readonly #subscriptions = new Map<string, [ResponseCallback, { unsubscribeMethod: string; id: string | number }]>();
49
+ readonly #resubscribeMethods = new Map<string, ActiveSubs>();
50
+ readonly #requests = new Map<number, ResponseCallback>();
51
+ readonly #wellKnownChains: Set<ScType.WellKnownChain>;
52
+ readonly #eventemitter: EventEmitter = new EventEmitter();
53
+
54
+ #chain: Promise<ScType.Chain> | null = null;
55
+ #isChainReady = false;
56
+
57
+ public constructor (Sc: BizinikiwiConnect, spec: string | ScType.WellKnownChain, sharedSandbox?: ScProvider) {
58
+ if (!isObject(Sc) || !isObject(Sc.WellKnownChain) || !isFunction(Sc.createScClient)) {
59
+ throw new Error('Expected an @bizinikiwi/connect interface as first parameter to ScProvider');
60
+ }
61
+
62
+ this.#Sc = Sc;
63
+ this.#spec = spec;
64
+ this.#sharedSandbox = sharedSandbox;
65
+ this.#wellKnownChains = new Set(Object.values(Sc.WellKnownChain));
66
+ }
67
+
68
+ public get hasSubscriptions (): boolean {
69
+ // Indicates that subscriptions are supported
70
+ return !!true;
71
+ }
72
+
73
+ public get isClonable (): boolean {
74
+ return !!false;
75
+ }
76
+
77
+ public get isConnected (): boolean {
78
+ return !!this.#chain && this.#isChainReady;
79
+ }
80
+
81
+ public clone (): ProviderInterface {
82
+ throw new Error('clone() is not supported.');
83
+ }
84
+
85
+ // Config details can be found in @bizinikiwi/connect repo following the link:
86
+ // https://github.com/pezkuwichain/bizinikiwi-connect/blob/main/packages/connect/src/connector/index.ts
87
+ async connect (config?: ScType.Config, checkerFactory = healthChecker): Promise<void> {
88
+ if (this.isConnected) {
89
+ throw new Error('Already connected!');
90
+ }
91
+
92
+ // it could happen that after emitting `disconnected` due to the fact that
93
+ // smoldot is syncing, the consumer tries to reconnect after a certain amount
94
+ // of time... In which case we want to make sure that we don't create a new
95
+ // chain.
96
+ if (this.#chain) {
97
+ await this.#chain;
98
+
99
+ return;
100
+ }
101
+
102
+ if (this.#sharedSandbox && !this.#sharedSandbox.isConnected) {
103
+ await this.#sharedSandbox.connect();
104
+ }
105
+
106
+ const client = this.#sharedSandbox
107
+ ? scClients.get(this.#sharedSandbox)
108
+ : this.#Sc.createScClient(config);
109
+
110
+ if (!client) {
111
+ throw new Error('Unknown ScProvider!');
112
+ }
113
+
114
+ scClients.set(this, client);
115
+
116
+ const hc = checkerFactory();
117
+
118
+ const onResponse = (res: string): void => {
119
+ const hcRes = hc.responsePassThrough(res);
120
+
121
+ if (!hcRes) {
122
+ return;
123
+ }
124
+
125
+ const response = JSON.parse(hcRes) as JsonRpcResponse<string>;
126
+ let decodedResponse: string | Error;
127
+
128
+ try {
129
+ decodedResponse = this.#coder.decodeResponse(response);
130
+ } catch (e) {
131
+ decodedResponse = e as Error;
132
+ }
133
+
134
+ // It's not a subscription message, but rather a standar RPC response
135
+ if (response.params?.subscription === undefined || !response.method) {
136
+ return this.#requests.get(response.id)?.(decodedResponse);
137
+ }
138
+
139
+ // We are dealing with a subscription message
140
+ const subscriptionId = `${response.method}::${response.params.subscription}`;
141
+
142
+ const callback = this.#subscriptions.get(subscriptionId)?.[0];
143
+
144
+ callback?.(decodedResponse);
145
+ };
146
+
147
+ const addChain = this.#sharedSandbox
148
+ ? (async (...args) => {
149
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
150
+ const source = this.#sharedSandbox!;
151
+
152
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
153
+ return (await source.#chain)!.addChain(...args);
154
+ }) as ScType.AddChain
155
+ : this.#wellKnownChains.has(this.#spec as ScType.WellKnownChain)
156
+ ? client.addWellKnownChain
157
+ : client.addChain;
158
+
159
+ this.#chain = addChain(this.#spec as ScType.WellKnownChain).then((chain) => {
160
+ hc.setSendJsonRpc(chain.sendJsonRpc);
161
+
162
+ // Start async response processing loop
163
+ // This replaces the callback-based API from older @substrate/connect versions
164
+ (async () => {
165
+ try {
166
+ for await (const res of chain.jsonRpcResponses) {
167
+ onResponse(res);
168
+ }
169
+ } catch {
170
+ // Chain was removed or connection closed - this is expected
171
+ }
172
+ })();
173
+
174
+ this.#isChainReady = false;
175
+
176
+ const cleanup = () => {
177
+ // If there are any callbacks left, we have to reject/error them.
178
+ // Otherwise, that would cause a memory leak.
179
+ const disconnectionError = new Error('Disconnected');
180
+
181
+ this.#requests.forEach((cb) => cb(disconnectionError));
182
+ this.#subscriptions.forEach(([cb]) => cb(disconnectionError));
183
+ this.#subscriptions.clear();
184
+ };
185
+
186
+ const staleSubscriptions: {
187
+ unsubscribeMethod: string
188
+ id: number | string
189
+ }[] = [];
190
+
191
+ const killStaleSubscriptions = () => {
192
+ if (staleSubscriptions.length === 0) {
193
+ return;
194
+ }
195
+
196
+ const stale = staleSubscriptions.pop();
197
+
198
+ if (!stale) {
199
+ throw new Error('Unable to get stale subscription');
200
+ }
201
+
202
+ const { id, unsubscribeMethod } = stale;
203
+
204
+ Promise
205
+ .race([
206
+ this.send(unsubscribeMethod, [id]).catch(noop),
207
+ new Promise((resolve) => setTimeout(resolve, 500))
208
+ ])
209
+ .then(killStaleSubscriptions)
210
+ .catch(noop);
211
+ };
212
+
213
+ hc.start((health) => {
214
+ const isReady =
215
+ !health.isSyncing && (health.peers > 0 || !health.shouldHavePeers);
216
+
217
+ // if it's the same as before, then nothing has changed and we are done
218
+ if (this.#isChainReady === isReady) {
219
+ return;
220
+ }
221
+
222
+ this.#isChainReady = isReady;
223
+
224
+ if (!isReady) {
225
+ // If we've reached this point, that means that the chain used to be "ready"
226
+ // and now we are about to emit `disconnected`.
227
+ //
228
+ // This will cause the PezkuwiJs API think that the connection is
229
+ // actually dead. In reality the smoldot chain is not dead, of course.
230
+ // However, we have to cleanup all the existing callbacks because when
231
+ // the smoldot chain stops syncing, then we will emit `connected` and
232
+ // the PezkuwiJs API will try to re-create the previous
233
+ // subscriptions and requests. Although, now is not a good moment
234
+ // to be sending unsubscription messages to the smoldot chain, we
235
+ // should wait until is no longer syncing to send the unsubscription
236
+ // messages from the stale subscriptions of the previous connection.
237
+ //
238
+ // That's why -before we perform the cleanup of `this.#subscriptions`-
239
+ // we keep the necessary information that we will need later on to
240
+ // kill the stale subscriptions.
241
+ [...this.#subscriptions.values()].forEach((s) => {
242
+ staleSubscriptions.push(s[1]);
243
+ });
244
+ cleanup();
245
+
246
+ this.#eventemitter.emit('disconnected');
247
+ } else {
248
+ killStaleSubscriptions();
249
+
250
+ this.#eventemitter.emit('connected');
251
+
252
+ if (this.#resubscribeMethods.size) {
253
+ this.#resubscribe();
254
+ }
255
+ }
256
+ });
257
+
258
+ return objectSpread({}, chain, {
259
+ remove: () => {
260
+ hc.stop();
261
+ chain.remove();
262
+ cleanup();
263
+ },
264
+ sendJsonRpc: hc.sendJsonRpc.bind(hc)
265
+ });
266
+ });
267
+
268
+ try {
269
+ await this.#chain;
270
+ } catch (e) {
271
+ this.#chain = null;
272
+ this.#eventemitter.emit('error', e);
273
+ throw e;
274
+ }
275
+ }
276
+
277
+ #resubscribe = (): void => {
278
+ const promises: any[] = [];
279
+
280
+ this.#resubscribeMethods.forEach((subDetails: ActiveSubs): void => {
281
+ // only re-create subscriptions which are not in author (only area where
282
+ // transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic'
283
+ // are not included (and will not be re-broadcast)
284
+ if (subDetails.type.startsWith('author_')) {
285
+ return;
286
+ }
287
+
288
+ try {
289
+ const promise = new Promise<void>((resolve) => {
290
+ this.subscribe(subDetails.type, subDetails.method, subDetails.params, subDetails.callback).catch((error) => console.log(error));
291
+ resolve();
292
+ });
293
+
294
+ promises.push(promise);
295
+ } catch (error) {
296
+ l.error(error);
297
+ }
298
+ });
299
+
300
+ Promise.all(promises).catch((err) => l.log(err));
301
+ };
302
+
303
+ async disconnect (): Promise<void> {
304
+ if (!this.#chain) {
305
+ return;
306
+ }
307
+
308
+ const chain = await this.#chain;
309
+
310
+ this.#chain = null;
311
+ this.#isChainReady = false;
312
+
313
+ try {
314
+ chain.remove();
315
+ } catch (_) {}
316
+
317
+ this.#eventemitter.emit('disconnected');
318
+ }
319
+
320
+ public on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void {
321
+ // It's possible. Although, quite unlikely, that by the time that pezkuwi
322
+ // subscribes to the `connected` event, the Provider is already connected.
323
+ // In that case, we must emit to let the consumer know that we are connected.
324
+ if (type === 'connected' && this.isConnected) {
325
+ sub();
326
+ }
327
+
328
+ this.#eventemitter.on(type, sub);
329
+
330
+ return (): void => {
331
+ this.#eventemitter.removeListener(type, sub);
332
+ };
333
+ }
334
+
335
+ public async send<T = any> (method: string, params: unknown[]): Promise<T> {
336
+ if (!this.isConnected || !this.#chain) {
337
+ throw new Error('Provider is not connected');
338
+ }
339
+
340
+ const chain = await this.#chain;
341
+ const [id, json] = this.#coder.encodeJson(method, params);
342
+
343
+ const result = new Promise<T>((resolve, reject): void => {
344
+ this.#requests.set(id, (response) => {
345
+ (isError(response) ? reject : resolve)(response as unknown as T);
346
+ });
347
+
348
+ try {
349
+ chain.sendJsonRpc(json);
350
+ } catch (e) {
351
+ this.#chain = null;
352
+
353
+ try {
354
+ chain.remove();
355
+ } catch (_) {}
356
+
357
+ this.#eventemitter.emit('error', e);
358
+ }
359
+ });
360
+
361
+ try {
362
+ return await result;
363
+ } finally {
364
+ // let's ensure that once the Promise is resolved/rejected, then we remove
365
+ // remove its entry from the internal #requests
366
+ this.#requests.delete(id);
367
+ }
368
+ }
369
+
370
+ public async subscribe (type: string, method: string, params: any[], callback: ProviderInterfaceCallback): Promise<number | string> {
371
+ if (!subscriptionUnsubscriptionMethods.has(method)) {
372
+ throw new Error(`Unsupported subscribe method: ${method}`);
373
+ }
374
+
375
+ const id = await this.send<number | string>(method, params);
376
+ const subscriptionId = `${type}::${id}`;
377
+
378
+ const cb = (response: Error | string) => {
379
+ if (response instanceof Error) {
380
+ callback(response, undefined);
381
+ } else {
382
+ callback(null, response);
383
+ }
384
+ };
385
+
386
+ const unsubscribeMethod = subscriptionUnsubscriptionMethods.get(method);
387
+
388
+ if (!unsubscribeMethod) {
389
+ throw new Error('Invalid unsubscribe method found');
390
+ }
391
+
392
+ this.#resubscribeMethods.set(subscriptionId, { callback, method, params, type });
393
+
394
+ this.#subscriptions.set(subscriptionId, [cb, { id, unsubscribeMethod }]);
395
+
396
+ return id;
397
+ }
398
+
399
+ public unsubscribe (type: string, method: string, id: number | string): Promise<boolean> {
400
+ if (!this.isConnected) {
401
+ throw new Error('Provider is not connected');
402
+ }
403
+
404
+ const subscriptionId = `${type}::${id}`;
405
+
406
+ if (!this.#subscriptions.has(subscriptionId)) {
407
+ return Promise.reject(
408
+ new Error(`Unable to find active subscription=${subscriptionId}`)
409
+ );
410
+ }
411
+
412
+ this.#resubscribeMethods.delete(subscriptionId);
413
+ this.#subscriptions.delete(subscriptionId);
414
+
415
+ return this.send(method, [id]);
416
+ }
417
+ }
@@ -0,0 +1,14 @@
1
+
2
+ export interface SmoldotHealth {
3
+ isSyncing: boolean
4
+ peers: number
5
+ shouldHavePeers: boolean
6
+ }
7
+
8
+ export interface HealthChecker {
9
+ setSendJsonRpc(sendRequest: (request: string) => void): void
10
+ start(healthCallback: (health: SmoldotHealth) => void): void
11
+ stop(): void
12
+ sendJsonRpc(request: string): void
13
+ responsePassThrough(response: string): string | null
14
+ }
@@ -0,0 +1,6 @@
1
+
2
+ export { ScProvider } from './bizinikiwi-connect/index.ts';
3
+ export { HttpProvider } from './http/index.ts';
4
+ export { DEFAULT_CAPACITY, LRUCache } from './lru.ts';
5
+ export { packageInfo } from './packageInfo.ts';
6
+ export { WsProvider } from './ws/index.ts';
@@ -0,0 +1,64 @@
1
+
2
+ import type { RpcErrorInterface } from '../types.ts';
3
+
4
+ import { isFunction } from 'https://deno.land/x/pezkuwi/util/mod.ts';
5
+
6
+ const UNKNOWN = -99999;
7
+
8
+ function extend<Data, K extends keyof RpcError<Data>> (that: RpcError<Data>, name: K, value: RpcError<Data>[K]): void {
9
+ Object.defineProperty(that, name, {
10
+ configurable: true,
11
+ enumerable: false,
12
+ value
13
+ });
14
+ }
15
+
16
+ /**
17
+ * @name RpcError
18
+ * @summary Extension to the basic JS Error.
19
+ * @description
20
+ * The built-in JavaScript Error class is extended by adding a code to allow for Error categorization. In addition to the normal `stack`, `message`, the numeric `code` and `data` (any types) parameters are available on the object.
21
+ * @example
22
+ * <BR>
23
+ *
24
+ * ```javascript
25
+ * const { RpcError } from '@pezkuwi/util');
26
+ *
27
+ * throw new RpcError('some message', RpcError.CODES.METHOD_NOT_FOUND); // => error.code = -32601
28
+ * ```
29
+ */
30
+ export default class RpcError<T = never> extends Error implements RpcErrorInterface<T> {
31
+ public code!: number;
32
+
33
+ public data?: T;
34
+
35
+ public override message!: string;
36
+
37
+ public override name!: string;
38
+
39
+ public override stack!: string;
40
+
41
+ public constructor (message = '', code: number = UNKNOWN, data?: T) {
42
+ super();
43
+
44
+ extend(this, 'message', String(message));
45
+ extend(this, 'name', this.constructor.name);
46
+ extend(this, 'data', data);
47
+ extend(this, 'code', code);
48
+
49
+ if (isFunction(Error.captureStackTrace)) {
50
+ Error.captureStackTrace(this, this.constructor);
51
+ } else {
52
+ const { stack } = new Error(message);
53
+
54
+ stack && extend(this, 'stack', stack);
55
+ }
56
+ }
57
+
58
+ public static CODES = {
59
+ ASSERT: -90009,
60
+ INVALID_JSONRPC: -99998,
61
+ METHOD_NOT_FOUND: -32601, // Rust client
62
+ UNKNOWN
63
+ };
64
+ }
@@ -0,0 +1,86 @@
1
+
2
+ import type { JsonRpcRequest, JsonRpcResponse, JsonRpcResponseBaseError } from '../types.ts';
3
+
4
+ import { isNumber, isString, isUndefined, stringify } from 'https://deno.land/x/pezkuwi/util/mod.ts';
5
+
6
+ import RpcError from './error.ts';
7
+
8
+ function formatErrorData (data?: string | number): string {
9
+ if (isUndefined(data)) {
10
+ return '';
11
+ }
12
+
13
+ const formatted = `: ${isString(data)
14
+ ? data.replace(/Error\("/g, '').replace(/\("/g, '(').replace(/"\)/g, ')').replace(/\(/g, ', ').replace(/\)/g, '')
15
+ : stringify(data)}`;
16
+
17
+ // We need some sort of cut-off here since these can be very large and
18
+ // very nested, pick a number and trim the result display to it
19
+ return formatted.length <= 256
20
+ ? formatted
21
+ : `${formatted.substring(0, 255)}…`;
22
+ }
23
+
24
+ function checkError (error?: JsonRpcResponseBaseError): void {
25
+ if (error) {
26
+ const { code, data, message } = error;
27
+
28
+ throw new RpcError(`${code}: ${message}${formatErrorData(data)}`, code, data);
29
+ }
30
+ }
31
+
32
+ /** @internal */
33
+ export class RpcCoder {
34
+ #id = 0;
35
+
36
+ public decodeResponse <T> (response?: JsonRpcResponse<T>): T {
37
+ if (!response || response.jsonrpc !== '2.0') {
38
+ throw new Error('Invalid jsonrpc field in decoded object');
39
+ }
40
+
41
+ const isSubscription = !isUndefined(response.params) && !isUndefined(response.method);
42
+
43
+ if (
44
+ !isNumber(response.id) &&
45
+ (
46
+ !isSubscription || (
47
+ !isNumber(response.params.subscription) &&
48
+ !isString(response.params.subscription)
49
+ )
50
+ )
51
+ ) {
52
+ throw new Error('Invalid id field in decoded object');
53
+ }
54
+
55
+ checkError(response.error);
56
+
57
+ if (response.result === undefined && !isSubscription) {
58
+ throw new Error('No result found in jsonrpc response');
59
+ }
60
+
61
+ if (isSubscription) {
62
+ checkError(response.params.error);
63
+
64
+ return response.params.result;
65
+ }
66
+
67
+ return response.result;
68
+ }
69
+
70
+ public encodeJson (method: string, params: unknown[]): [number, string] {
71
+ const [id, data] = this.encodeObject(method, params);
72
+
73
+ return [id, stringify(data)];
74
+ }
75
+
76
+ public encodeObject (method: string, params: unknown[]): [number, JsonRpcRequest] {
77
+ const id = ++this.#id;
78
+
79
+ return [id, {
80
+ id,
81
+ jsonrpc: '2.0',
82
+ method,
83
+ params
84
+ }];
85
+ }
86
+ }
@@ -0,0 +1,8 @@
1
+
2
+ const HTTP_URL = 'http://127.0.0.1:9933';
3
+ const WS_URL = 'ws://127.0.0.1:9944';
4
+
5
+ export default {
6
+ HTTP_URL,
7
+ WS_URL
8
+ };