@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,66 @@
1
+ # @pezkuwi/rpc-provider
2
+
3
+ Generic transport providers to handle the transport of method calls to and from Pezkuwi clients from applications interacting with it. It provides an interface to making RPC calls and is generally, unless you are operating at a low-level and taking care of encoding and decoding of parameters/results, it won't be directly used, rather only passed to a higher-level interface.
4
+
5
+ ## Provider Selection
6
+
7
+ There are three flavours of the providers provided, one allowing for using HTTP as a transport mechanism, the other using WebSockets, and the third one uses bizinikiwi light-client through @bizinikiwi/connect. It is generally recommended to use the [[WsProvider]] since in addition to standard calls, it allows for subscriptions where all changes to state can be pushed from the node to the client.
8
+
9
+ All providers are usable (as is the API), in both browser-based and Node.js environments. Polyfills for unsupported functionality are automatically applied based on feature-detection.
10
+
11
+ ## Usage
12
+
13
+ Installation -
14
+
15
+ ```
16
+ yarn add @pezkuwi/rpc-provider
17
+ ```
18
+
19
+ WebSocket Initialization -
20
+
21
+ ```javascript
22
+ import { WsProvider } from 'https://deno.land/x/pezkuwi/rpc-provider/mod.ts';
23
+
24
+ const provider = new WsProvider('ws://127.0.0.1:9944');
25
+ const version = await provider.send('client_version', []);
26
+
27
+ console.log('client version', version);
28
+ ```
29
+
30
+ HTTP Initialization -
31
+
32
+ ```javascript
33
+ import { HttpProvider } from 'https://deno.land/x/pezkuwi/rpc-provider/mod.ts';
34
+
35
+ const provider = new HttpProvider('http://127.0.0.1:9933');
36
+ const version = await provider.send('chain_getBlockHash', []);
37
+
38
+ console.log('latest block Hash', hash);
39
+ ```
40
+
41
+ @bizinikiwi/connect Initialization -
42
+
43
+ Instantiating a Provider for the Pezkuwi Relay Chain:
44
+ ```javascript
45
+ import { ScProvider } from 'https://deno.land/x/pezkuwi/rpc-provider/mod.ts';
46
+ import * as Sc from 'https://esm.sh/@bizinikiwi/connect@2.1.9';
47
+
48
+ const provider = new ScProvider(Sc, Sc.WellKnownChain.pezkuwi);
49
+
50
+ await provider.connect();
51
+
52
+ const version = await provider.send('chain_getBlockHash', []);
53
+ ```
54
+
55
+ Instantiating a Provider for a Pezkuwi teyrchain:
56
+ ```javascript
57
+ import { ScProvider } from 'https://deno.land/x/pezkuwi/rpc-provider/mod.ts';
58
+ import * as Sc from 'https://esm.sh/@bizinikiwi/connect@2.1.9';
59
+
60
+ const pezkuwiProvider = new ScProvider(Sc, Sc.WellKnownChain.pezkuwi);
61
+ const teyrchainProvider = new ScProvider(Sc, teyrchainSpec, pezkuwiProvider);
62
+
63
+ await teyrchainProvider.connect();
64
+
65
+ const version = await teyrchainProvider.send('chain_getBlockHash', []);
66
+ ```
@@ -0,0 +1,323 @@
1
+
2
+ import type { HealthChecker, SmoldotHealth } from './types.ts';
3
+
4
+ import { stringify } from 'https://deno.land/x/pezkuwi/util/mod.ts';
5
+
6
+ interface JSONRequest {
7
+ id: string;
8
+ jsonrpc: '2.0',
9
+ method: string;
10
+ params: unknown[];
11
+ }
12
+
13
+ /*
14
+ * Creates a new health checker.
15
+ *
16
+ * The role of the health checker is to report to the user the health of a smoldot chain.
17
+ *
18
+ * In order to use it, start by creating a health checker, and call `setSendJsonRpc` to set the
19
+ * way to send a JSON-RPC request to a chain. The health checker is disabled by default. Use
20
+ * `start()` in order to start the health checks. The `start()` function must be passed a callback called
21
+ * when an update to the health of the node is available.
22
+ *
23
+ * In order to send a JSON-RPC request to the chain, you **must** use the `sendJsonRpc` function
24
+ * of the health checker. The health checker rewrites the `id` of the requests it receives.
25
+ *
26
+ * When the chain send a JSON-RPC response, it must be passed to `responsePassThrough()`. This
27
+ * function intercepts the responses destined to the requests that have been emitted by the health
28
+ * checker and returns `null`. If the response doesn't concern the health checker, the response is
29
+ * simply returned by the function.
30
+ *
31
+ * # How it works
32
+ *
33
+ * The health checker periodically calls the `system_health` JSON-RPC call in order to determine
34
+ * the health of the chain.
35
+ *
36
+ * In addition to this, as long as the health check reports that `isSyncing` is `true`, the
37
+ * health checker also maintains a subscription to new best blocks using `chain_subscribeNewHeads`.
38
+ * Whenever a new block is notified, a health check is performed immediately in order to determine
39
+ * whether `isSyncing` has changed to `false`.
40
+ *
41
+ * Thanks to this subscription, the latency of the report of the switch from `isSyncing: true` to
42
+ * `isSyncing: false` is very low.
43
+ *
44
+ */
45
+ export function healthChecker (): HealthChecker {
46
+ // `null` if health checker is not started.
47
+ let checker: null | InnerChecker = null;
48
+ let sendJsonRpc: null | ((request: string) => void) = null;
49
+
50
+ return {
51
+ responsePassThrough: (jsonRpcResponse) => {
52
+ if (checker === null) {
53
+ return jsonRpcResponse;
54
+ }
55
+
56
+ return checker.responsePassThrough(jsonRpcResponse);
57
+ },
58
+ sendJsonRpc: (request) => {
59
+ if (!sendJsonRpc) {
60
+ throw new Error('setSendJsonRpc must be called before sending requests');
61
+ }
62
+
63
+ if (checker === null) {
64
+ sendJsonRpc(request);
65
+ } else {
66
+ checker.sendJsonRpc(request);
67
+ }
68
+ },
69
+ setSendJsonRpc: (cb) => {
70
+ sendJsonRpc = cb;
71
+ },
72
+ start: (healthCallback) => {
73
+ if (checker !== null) {
74
+ throw new Error("Can't start the health checker multiple times in parallel");
75
+ } else if (!sendJsonRpc) {
76
+ throw new Error('setSendJsonRpc must be called before starting the health checks');
77
+ }
78
+
79
+ checker = new InnerChecker(healthCallback, sendJsonRpc);
80
+ checker.update(true);
81
+ },
82
+ stop: () => {
83
+ if (checker === null) {
84
+ return;
85
+ } // Already stopped.
86
+
87
+ checker.destroy();
88
+ checker = null;
89
+ }
90
+ };
91
+ }
92
+
93
+ class InnerChecker {
94
+ #healthCallback: (health: SmoldotHealth) => void;
95
+ #currentHealthCheckId: string | null = null;
96
+ #currentHealthTimeout: ReturnType<typeof setTimeout> | null = null;
97
+ #currentSubunsubRequestId: string | null = null;
98
+ #currentSubscriptionId: string | null = null;
99
+ #requestToSmoldot: (request: JSONRequest) => void;
100
+ #isSyncing = false;
101
+ #nextRequestId = 0;
102
+
103
+ constructor (healthCallback: (health: SmoldotHealth) => void, requestToSmoldot: (request: string) => void) {
104
+ this.#healthCallback = healthCallback;
105
+ this.#requestToSmoldot = (request: JSONRequest) => requestToSmoldot(stringify(request));
106
+ }
107
+
108
+ sendJsonRpc = (request: string): void => {
109
+ // Replace the `id` in the request to prefix the request ID with `extern:`.
110
+ let parsedRequest: JSONRequest;
111
+
112
+ try {
113
+ parsedRequest = JSON.parse(request) as JSONRequest;
114
+ } catch {
115
+ return;
116
+ }
117
+
118
+ if (parsedRequest.id) {
119
+ const newId = 'extern:' + stringify(parsedRequest.id);
120
+
121
+ parsedRequest.id = newId;
122
+ }
123
+
124
+ this.#requestToSmoldot(parsedRequest);
125
+ };
126
+
127
+ responsePassThrough = (jsonRpcResponse: string): string | null => {
128
+ let parsedResponse: {id: string, result?: SmoldotHealth, params?: { subscription: string }};
129
+
130
+ try {
131
+ parsedResponse = JSON.parse(jsonRpcResponse) as { id: string, result?: SmoldotHealth };
132
+ } catch {
133
+ return jsonRpcResponse;
134
+ }
135
+
136
+ // Check whether response is a response to `system_health`.
137
+ if (parsedResponse.id && this.#currentHealthCheckId === parsedResponse.id) {
138
+ this.#currentHealthCheckId = null;
139
+
140
+ // Check whether query was successful. It is possible for queries to fail for
141
+ // various reasons, such as the client being overloaded.
142
+ if (!parsedResponse.result) {
143
+ this.update(false);
144
+
145
+ return null;
146
+ }
147
+
148
+ this.#healthCallback(parsedResponse.result);
149
+ this.#isSyncing = parsedResponse.result.isSyncing;
150
+ this.update(false);
151
+
152
+ return null;
153
+ }
154
+
155
+ // Check whether response is a response to the subscription or unsubscription.
156
+ if (
157
+ parsedResponse.id &&
158
+ this.#currentSubunsubRequestId === parsedResponse.id
159
+ ) {
160
+ this.#currentSubunsubRequestId = null;
161
+
162
+ // Check whether query was successful. It is possible for queries to fail for
163
+ // various reasons, such as the client being overloaded.
164
+ if (!parsedResponse.result) {
165
+ this.update(false);
166
+
167
+ return null;
168
+ }
169
+
170
+ if (this.#currentSubscriptionId) {
171
+ this.#currentSubscriptionId = null;
172
+ } else {
173
+ this.#currentSubscriptionId = parsedResponse.result as unknown as string;
174
+ }
175
+
176
+ this.update(false);
177
+
178
+ return null;
179
+ }
180
+
181
+ // Check whether response is a notification to a subscription.
182
+ if (
183
+ parsedResponse.params &&
184
+ this.#currentSubscriptionId &&
185
+ parsedResponse.params.subscription === this.#currentSubscriptionId
186
+ ) {
187
+ // Note that after a successful subscription, a notification containing
188
+ // the current best block is always returned. Considering that a
189
+ // subscription is performed in response to a health check, calling
190
+ // `startHealthCheck()` here will lead to a second health check.
191
+ // It might seem redundant to perform two health checks in a quick
192
+ // succession, but doing so doesn't lead to any problem, and it is
193
+ // actually possible for the health to have changed in between as the
194
+ // current best block might have been updated during the subscription
195
+ // request.
196
+ this.update(true);
197
+
198
+ return null;
199
+ }
200
+
201
+ // Response doesn't concern us.
202
+ if (parsedResponse.id) {
203
+ const id: string = parsedResponse.id;
204
+
205
+ // Need to remove the `extern:` prefix.
206
+ if (!id.startsWith('extern:')) {
207
+ throw new Error('State inconsistency in health checker');
208
+ }
209
+
210
+ const newId = JSON.parse(id.slice('extern:'.length)) as string;
211
+
212
+ parsedResponse.id = newId;
213
+ }
214
+
215
+ return stringify(parsedResponse);
216
+ };
217
+
218
+ update = (startNow: boolean): void => {
219
+ // If `startNow`, clear `#currentHealthTimeout` so that it is set below.
220
+ if (startNow && this.#currentHealthTimeout) {
221
+ clearTimeout(this.#currentHealthTimeout);
222
+ this.#currentHealthTimeout = null;
223
+ }
224
+
225
+ if (!this.#currentHealthTimeout) {
226
+ const startHealthRequest = () => {
227
+ this.#currentHealthTimeout = null;
228
+
229
+ // No matter what, don't start a health request if there is already one in progress.
230
+ // This is sane to do because receiving a response to a health request calls `update()`.
231
+ if (this.#currentHealthCheckId) {
232
+ return;
233
+ }
234
+
235
+ // Actual request starting.
236
+ this.#currentHealthCheckId = `health-checker:${this.#nextRequestId}`;
237
+ this.#nextRequestId += 1;
238
+
239
+ this.#requestToSmoldot({
240
+ id: this.#currentHealthCheckId,
241
+ jsonrpc: '2.0',
242
+ method: 'system_health',
243
+ params: []
244
+ });
245
+ };
246
+
247
+ if (startNow) {
248
+ startHealthRequest();
249
+ } else {
250
+ this.#currentHealthTimeout = setTimeout(startHealthRequest, 1000);
251
+ }
252
+ }
253
+
254
+ if (
255
+ this.#isSyncing &&
256
+ !this.#currentSubscriptionId &&
257
+ !this.#currentSubunsubRequestId
258
+ ) {
259
+ this.startSubscription();
260
+ }
261
+
262
+ if (
263
+ !this.#isSyncing &&
264
+ this.#currentSubscriptionId &&
265
+ !this.#currentSubunsubRequestId
266
+ ) {
267
+ this.endSubscription();
268
+ }
269
+ };
270
+
271
+ startSubscription = (): void => {
272
+ if (this.#currentSubunsubRequestId || this.#currentSubscriptionId) {
273
+ throw new Error('Internal error in health checker');
274
+ }
275
+
276
+ this.#currentSubunsubRequestId = `health-checker:${this.#nextRequestId}`;
277
+ this.#nextRequestId += 1;
278
+
279
+ this.#requestToSmoldot({
280
+ id: this.#currentSubunsubRequestId,
281
+ jsonrpc: '2.0',
282
+ method: 'chain_subscribeNewHeads',
283
+ params: []
284
+ });
285
+ };
286
+
287
+ endSubscription = (): void => {
288
+ if (this.#currentSubunsubRequestId || !this.#currentSubscriptionId) {
289
+ throw new Error('Internal error in health checker');
290
+ }
291
+
292
+ this.#currentSubunsubRequestId = `health-checker:${this.#nextRequestId}`;
293
+ this.#nextRequestId += 1;
294
+
295
+ this.#requestToSmoldot({
296
+ id: this.#currentSubunsubRequestId,
297
+ jsonrpc: '2.0',
298
+ method: 'chain_unsubscribeNewHeads',
299
+ params: [this.#currentSubscriptionId]
300
+ });
301
+ };
302
+
303
+ destroy = (): void => {
304
+ if (this.#currentHealthTimeout) {
305
+ clearTimeout(this.#currentHealthTimeout);
306
+ this.#currentHealthTimeout = null;
307
+ }
308
+ };
309
+ }
310
+
311
+ export class HealthCheckError extends Error {
312
+ readonly #cause: unknown;
313
+
314
+ getCause (): unknown {
315
+ return this.#cause;
316
+ }
317
+
318
+ constructor (response: unknown, message = 'Got error response asking for system health') {
319
+ super(message);
320
+
321
+ this.#cause = response;
322
+ }
323
+ }