@pezkuwi/rpc-provider 16.5.16 → 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,81 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import { TypeRegistry } from '@pezkuwi/types/create';
7
+
8
+ import { MockProvider } from './index.js';
9
+
10
+ describe('subscribe', (): void => {
11
+ const registry = new TypeRegistry();
12
+ let mock: MockProvider;
13
+
14
+ beforeEach((): void => {
15
+ mock = new MockProvider(registry);
16
+ });
17
+
18
+ afterEach(async () => {
19
+ await mock.disconnect();
20
+ });
21
+
22
+ it('fails on unknown methods', async (): Promise<void> => {
23
+ await mock
24
+ .subscribe('test', 'test_notFound')
25
+ .catch((error): void => {
26
+ // eslint-disable-next-line jest/no-conditional-expect
27
+ expect((error as Error).message).toMatch(/Invalid method 'test_notFound'/);
28
+ });
29
+ });
30
+
31
+ it('returns a subscription id', async (): Promise<void> => {
32
+ await mock
33
+ .subscribe('chain_newHead', 'chain_subscribeNewHead', (): void => undefined)
34
+ .then((id): void => {
35
+ expect(id).toEqual(1);
36
+ });
37
+ });
38
+
39
+ it('calls back with the last known value', async (): Promise<void> => {
40
+ mock.isUpdating = false;
41
+ mock.subscriptions.chain_subscribeNewHead.lastValue = 'testValue';
42
+
43
+ await new Promise<boolean>((resolve) => {
44
+ mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, value: string): void => {
45
+ // eslint-disable-next-line jest/no-conditional-expect
46
+ expect(value).toEqual('testValue');
47
+ resolve(true);
48
+ }).catch(console.error);
49
+ });
50
+ });
51
+
52
+ // eslint-disable-next-line jest/expect-expect
53
+ it('calls back with new headers', async (): Promise<void> => {
54
+ await new Promise<boolean>((resolve) => {
55
+ mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, header: { number: number }): void => {
56
+ if (header.number === 4) {
57
+ resolve(true);
58
+ }
59
+ }).catch(console.error);
60
+ });
61
+ });
62
+
63
+ // eslint-disable-next-line jest/expect-expect
64
+ it('handles errors within callbacks gracefully', async (): Promise<void> => {
65
+ let hasThrown = false;
66
+
67
+ await new Promise<boolean>((resolve) => {
68
+ mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, header: { number: number }): void => {
69
+ if (!hasThrown) {
70
+ hasThrown = true;
71
+
72
+ throw new Error('testing');
73
+ }
74
+
75
+ if (header.number === 3) {
76
+ resolve(true);
77
+ }
78
+ }).catch(console.error);
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,36 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Server } from 'mock-socket';
5
+
6
+ export type Global = typeof globalThis & {
7
+ WebSocket: typeof WebSocket;
8
+ fetch: any;
9
+ }
10
+
11
+ export interface Mock {
12
+ body: Record<string, Record<string, unknown>>;
13
+ requests: number;
14
+ server: Server;
15
+ done: () => Promise<void>;
16
+ }
17
+
18
+ export type MockStateSubscriptionCallback = (error: Error | null, value: any) => void;
19
+
20
+ export interface MockStateSubscription {
21
+ callbacks: Record<number, MockStateSubscriptionCallback>;
22
+ lastValue: any;
23
+ }
24
+
25
+ export interface MockStateSubscriptions {
26
+ // known
27
+ chain_subscribeNewHead: MockStateSubscription;
28
+ state_subscribeStorage: MockStateSubscription;
29
+
30
+ // others
31
+ [key: string]: MockStateSubscription;
32
+ }
33
+
34
+ export type MockStateDb = Record<string, Uint8Array>;
35
+
36
+ export type MockStateRequests = Record<string, (db: MockStateDb, params: any[]) => string>;
@@ -0,0 +1,57 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import { TypeRegistry } from '@pezkuwi/types/create';
7
+
8
+ import { MockProvider } from './index.js';
9
+
10
+ describe('unsubscribe', (): void => {
11
+ const registry = new TypeRegistry();
12
+ let mock: MockProvider;
13
+ let id: number;
14
+
15
+ beforeEach((): Promise<void> => {
16
+ mock = new MockProvider(registry);
17
+
18
+ return mock
19
+ .subscribe('chain_newHead', 'chain_subscribeNewHead', (): void => undefined)
20
+ .then((_id): void => {
21
+ id = _id;
22
+ });
23
+ });
24
+
25
+ afterEach(async () => {
26
+ await mock.disconnect();
27
+ });
28
+
29
+ it('fails on unknown ids', async (): Promise<void> => {
30
+ await mock
31
+ .unsubscribe('chain_newHead', 'chain_subscribeNewHead', 5)
32
+ .catch((error): boolean => {
33
+ // eslint-disable-next-line jest/no-conditional-expect
34
+ expect((error as Error).message).toMatch(/Unable to find/);
35
+
36
+ return false;
37
+ });
38
+ });
39
+
40
+ // eslint-disable-next-line jest/expect-expect
41
+ it('unsubscribes successfully', async (): Promise<void> => {
42
+ await mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id);
43
+ });
44
+
45
+ it('fails on double unsubscribe', async (): Promise<void> => {
46
+ await mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id)
47
+ .then((): Promise<boolean> =>
48
+ mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id)
49
+ )
50
+ .catch((error): boolean => {
51
+ // eslint-disable-next-line jest/no-conditional-expect
52
+ expect((error as Error).message).toMatch(/Unable to find/);
53
+
54
+ return false;
55
+ });
56
+ });
57
+ });
package/src/mod.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export * from './index.js';
@@ -0,0 +1,12 @@
1
+ // Copyright 2017-2026 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Do not edit, auto-generated by @pezkuwi/dev
5
+ // (packageInfo imports will be kept as-is, user-editable)
6
+
7
+ import { packageInfo as typesInfo } from '@pezkuwi/types/packageInfo';
8
+ import { detectPackage } from '@pezkuwi/util';
9
+
10
+ import { packageInfo } from './packageInfo.js';
11
+
12
+ detectPackage(packageInfo, null, [typesInfo]);
@@ -0,0 +1,6 @@
1
+ // Copyright 2017-2026 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Do not edit, auto-generated by @pezkuwi/dev
5
+
6
+ export const packageInfo = { name: '@pezkuwi/rpc-provider', path: 'auto', type: 'auto', version: '16.5.18' };
package/src/types.ts ADDED
@@ -0,0 +1,101 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export interface JsonRpcObject {
5
+ id: number;
6
+ jsonrpc: '2.0';
7
+ }
8
+
9
+ export interface JsonRpcRequest extends JsonRpcObject {
10
+ method: string;
11
+ params: unknown[];
12
+ }
13
+
14
+ export interface JsonRpcResponseBaseError {
15
+ code: number;
16
+ data?: number | string;
17
+ message: string;
18
+ }
19
+
20
+ export interface RpcErrorInterface<T> {
21
+ code: number;
22
+ data?: T;
23
+ message: string;
24
+ stack: string;
25
+ }
26
+
27
+ interface JsonRpcResponseSingle<T> {
28
+ error?: JsonRpcResponseBaseError;
29
+ result: T;
30
+ }
31
+
32
+ interface JsonRpcResponseSubscription<T> {
33
+ method?: string;
34
+ params: {
35
+ error?: JsonRpcResponseBaseError;
36
+ result: T;
37
+ subscription: number | string;
38
+ };
39
+ }
40
+
41
+ export type JsonRpcResponseBase<T> = JsonRpcResponseSingle<T> & JsonRpcResponseSubscription<T>;
42
+
43
+ export type JsonRpcResponse<T> = JsonRpcObject & JsonRpcResponseBase<T>;
44
+
45
+ export type ProviderInterfaceCallback = (error: Error | null, result: any) => void;
46
+
47
+ export type ProviderInterfaceEmitted = 'connected' | 'disconnected' | 'error';
48
+
49
+ export type ProviderInterfaceEmitCb = (value?: any) => any;
50
+
51
+ export interface ProviderInterface {
52
+ /** true if the provider supports subscriptions (not available for HTTP) */
53
+ readonly hasSubscriptions: boolean;
54
+ /** true if the clone() functionality is available on the provider */
55
+ readonly isClonable: boolean;
56
+ /** true if the provider is currently connected (ws/sc has connection logic) */
57
+ readonly isConnected: boolean;
58
+ /** (optional) stats for the provider with connections/bytes */
59
+ readonly stats?: ProviderStats;
60
+ /** (optional) stats for the provider with connections/bytes */
61
+ readonly ttl?: number | null;
62
+
63
+ clone (): ProviderInterface;
64
+ connect (): Promise<void>;
65
+ disconnect (): Promise<void>;
66
+ on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void;
67
+ send <T = any> (method: string, params: unknown[], isCacheable?: boolean): Promise<T>;
68
+ subscribe (type: string, method: string, params: unknown[], cb: ProviderInterfaceCallback): Promise<number | string>;
69
+ unsubscribe (type: string, method: string, id: number | string): Promise<boolean>;
70
+ }
71
+
72
+ /** Stats for a specific endpoint */
73
+ export interface EndpointStats {
74
+ /** The total number of bytes sent */
75
+ bytesRecv: number;
76
+ /** The total number of bytes received */
77
+ bytesSent: number;
78
+ /** The number of cached/in-progress requests made */
79
+ cached: number;
80
+ /** The number of errors found */
81
+ errors: number;
82
+ /** The number of requests */
83
+ requests: number;
84
+ /** The number of subscriptions */
85
+ subscriptions: number;
86
+ /** The number of request timeouts */
87
+ timeout: number;
88
+ }
89
+
90
+ /** Overall stats for the provider */
91
+ export interface ProviderStats {
92
+ /** Details for the active/open requests */
93
+ active: {
94
+ /** Number of active requests */
95
+ requests: number;
96
+ /** Number of active subscriptions */
97
+ subscriptions: number;
98
+ };
99
+ /** The total requests that have been made */
100
+ total: EndpointStats;
101
+ }
@@ -0,0 +1,167 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import type { Mock } from '../mock/types.js';
7
+
8
+ import { mockWs } from '../mock/mockWs.js';
9
+ import { WsProvider } from './index.js';
10
+
11
+ const TEST_WS_URL = 'ws://localhost-connect.spec.ts:9988';
12
+
13
+ function sleep (ms = 100): Promise<void> {
14
+ return new Promise((resolve) => setTimeout(resolve, ms));
15
+ }
16
+
17
+ describe('onConnect', (): void => {
18
+ let mocks: Mock[];
19
+ let provider: WsProvider | null;
20
+
21
+ beforeEach((): void => {
22
+ mocks = [mockWs([], TEST_WS_URL)];
23
+ });
24
+
25
+ afterEach(async () => {
26
+ if (provider) {
27
+ await provider.disconnect();
28
+ await sleep();
29
+
30
+ provider = null;
31
+ }
32
+
33
+ await Promise.all(mocks.map((m) => m.done()));
34
+ await sleep();
35
+ });
36
+
37
+ it('Does not connect when autoConnect is false', async () => {
38
+ provider = new WsProvider(TEST_WS_URL, 0);
39
+
40
+ await sleep();
41
+
42
+ expect(provider.isConnected).toBe(false);
43
+
44
+ await provider.connect();
45
+ await sleep();
46
+
47
+ expect(provider.isConnected).toBe(true);
48
+
49
+ await provider.disconnect();
50
+ await sleep();
51
+
52
+ expect(provider.isConnected).toBe(false);
53
+ });
54
+
55
+ it('Does connect when autoConnect is true', async () => {
56
+ provider = new WsProvider(TEST_WS_URL, 1);
57
+
58
+ await sleep();
59
+
60
+ expect(provider.isConnected).toBe(true);
61
+ });
62
+
63
+ it('Creates a new WebSocket instance by calling the connect() method', async () => {
64
+ provider = new WsProvider(TEST_WS_URL, false);
65
+
66
+ expect(provider.isConnected).toBe(false);
67
+ expect(mocks[0].server.clients().length).toBe(0);
68
+
69
+ await provider.connect();
70
+ await sleep();
71
+
72
+ expect(provider.isConnected).toBe(true);
73
+ expect(mocks[0].server.clients()).toHaveLength(1);
74
+ });
75
+
76
+ it('Connects to first endpoint when an array is given', async () => {
77
+ provider = new WsProvider([TEST_WS_URL], 1);
78
+
79
+ await sleep();
80
+
81
+ expect(provider.isConnected).toBe(true);
82
+ expect(mocks[0].server.clients()).toHaveLength(1);
83
+ });
84
+
85
+ it('Does not allow connect() on already-connected', async () => {
86
+ provider = new WsProvider([TEST_WS_URL], 1);
87
+
88
+ await sleep();
89
+
90
+ expect(provider.isConnected).toBe(true);
91
+
92
+ await expect(
93
+ provider.connect()
94
+ ).rejects.toThrow(/already connected/);
95
+ });
96
+
97
+ it('Connects to the second endpoint when the first is unreachable', async () => {
98
+ const endpoints: string[] = ['ws://localhost-unreachable-connect.spec.ts:9956', TEST_WS_URL];
99
+
100
+ provider = new WsProvider(endpoints, 1);
101
+
102
+ await sleep();
103
+
104
+ expect(mocks[0].server.clients()).toHaveLength(1);
105
+ expect(provider.isConnected).toBe(true);
106
+ });
107
+
108
+ it('Connects to the second endpoint when the first is dropped', async () => {
109
+ const endpoints: string[] = [TEST_WS_URL, 'ws://localhost-connect.spec.ts:9957'];
110
+
111
+ mocks.push(mockWs([], endpoints[1]));
112
+
113
+ provider = new WsProvider(endpoints, 1);
114
+
115
+ await sleep();
116
+
117
+ // Check that first server is connected
118
+ expect(mocks[0].server.clients()).toHaveLength(1);
119
+ expect(mocks[1].server.clients()).toHaveLength(0);
120
+
121
+ // Close connection from first server
122
+ mocks[0].server.clients()[0].close();
123
+
124
+ await sleep();
125
+
126
+ // Check that second server is connected
127
+ expect(mocks[1].server.clients()).toHaveLength(1);
128
+ expect(provider.isConnected).toBe(true);
129
+ });
130
+
131
+ it('Round-robin of endpoints on WsProvider', async () => {
132
+ const endpoints: string[] = [
133
+ TEST_WS_URL,
134
+ 'ws://localhost-connect.spec.ts:9956',
135
+ 'ws://localhost-connect.spec.ts:9957',
136
+ 'ws://invalid-connect.spec.ts:9956',
137
+ 'ws://localhost-connect.spec.ts:9958'
138
+ ];
139
+
140
+ mocks.push(mockWs([], endpoints[1]));
141
+ mocks.push(mockWs([], endpoints[2]));
142
+ mocks.push(mockWs([], endpoints[4]));
143
+
144
+ const mockNext = [
145
+ mocks[1],
146
+ mocks[2],
147
+ mocks[3],
148
+ mocks[0]
149
+ ];
150
+
151
+ provider = new WsProvider(endpoints, 1);
152
+
153
+ for (let round = 0; round < 2; round++) {
154
+ for (let mock = 0; mock < mocks.length; mock++) {
155
+ await sleep();
156
+
157
+ // Wwe are connected, the current mock has the connection and the next doesn't
158
+ expect(provider.isConnected).toBe(true);
159
+ expect(mocks[mock].server.clients()).toHaveLength(1);
160
+ expect(mockNext[mock].server.clients()).toHaveLength(0);
161
+
162
+ // Close connection from first server
163
+ mocks[mock].server.clients()[0].close();
164
+ }
165
+ }
166
+ });
167
+ });
@@ -0,0 +1,41 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // from https://stackoverflow.com/questions/19304157/getting-the-reason-why-websockets-closed-with-close-code-1006
5
+
6
+ const known: Record<number, string> = {
7
+ 1000: 'Normal Closure',
8
+ 1001: 'Going Away',
9
+ 1002: 'Protocol Error',
10
+ 1003: 'Unsupported Data',
11
+ 1004: '(For future)',
12
+ 1005: 'No Status Received',
13
+ 1006: 'Abnormal Closure',
14
+ 1007: 'Invalid frame payload data',
15
+ 1008: 'Policy Violation',
16
+ 1009: 'Message too big',
17
+ 1010: 'Missing Extension',
18
+ 1011: 'Internal Error',
19
+ 1012: 'Service Restart',
20
+ 1013: 'Try Again Later',
21
+ 1014: 'Bad Gateway',
22
+ 1015: 'TLS Handshake'
23
+ };
24
+
25
+ export function getWSErrorString (code: number): string {
26
+ if (code >= 0 && code <= 999) {
27
+ return '(Unused)';
28
+ } else if (code >= 1016) {
29
+ if (code <= 1999) {
30
+ return '(For WebSocket standard)';
31
+ } else if (code <= 2999) {
32
+ return '(For WebSocket extensions)';
33
+ } else if (code <= 3999) {
34
+ return '(For libraries and frameworks)';
35
+ } else if (code <= 4999) {
36
+ return '(For applications)';
37
+ }
38
+ }
39
+
40
+ return known[code] || '(Unknown)';
41
+ }
@@ -0,0 +1,97 @@
1
+ // Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import type { Request } from '../mock/mockWs.js';
7
+ import type { Mock } from '../mock/types.js';
8
+
9
+ import { mockWs } from '../mock/mockWs.js';
10
+ import { WsProvider } from './index.js';
11
+
12
+ const TEST_WS_URL = 'ws://localhost-index.spec.ts:9977';
13
+
14
+ let provider: WsProvider | null;
15
+ let mock: Mock;
16
+
17
+ function createWs (requests: Request[], autoConnect = 1000, headers?: Record<string, string>, timeout?: number): WsProvider {
18
+ mock = mockWs(requests, TEST_WS_URL);
19
+ provider = new WsProvider(TEST_WS_URL, autoConnect, headers, timeout);
20
+
21
+ return provider;
22
+ }
23
+
24
+ describe('Ws', (): void => {
25
+ afterEach(async () => {
26
+ if (mock) {
27
+ await mock.done();
28
+ }
29
+
30
+ if (provider) {
31
+ await provider.disconnect();
32
+ provider = null;
33
+ }
34
+ });
35
+
36
+ it('returns the connected state', (): void => {
37
+ expect(
38
+ createWs([]).isConnected
39
+ ).toEqual(false);
40
+ });
41
+
42
+ // eslint-disable-next-line jest/expect-expect
43
+ it('allows you to initialize the provider with custom headers', () => {
44
+ createWs([], 100, { foo: 'bar' });
45
+ });
46
+
47
+ // eslint-disable-next-line jest/expect-expect
48
+ it('allows you to set custom timeout value for handlers', () => {
49
+ const CUSTOM_TIMEOUT_S = 90;
50
+ const CUSTOM_TIMEOUT_MS = CUSTOM_TIMEOUT_S * 1000;
51
+
52
+ createWs([], 100, { foo: 'bar' }, CUSTOM_TIMEOUT_MS);
53
+ });
54
+ });
55
+
56
+ describe('Endpoint Parsing', (): void => {
57
+ // eslint-disable-next-line jest/expect-expect
58
+ it('Succeeds when WsProvider endpoint is a valid string', () => {
59
+ /* eslint-disable no-new */
60
+ new WsProvider(TEST_WS_URL, 0);
61
+ });
62
+
63
+ it('should throw error on negative cache capacity or TTL', () => {
64
+ expect(() => new WsProvider(TEST_WS_URL, false, {}, undefined, -5, 30000)).toThrow(/'capacity' must be a non-negative integer/);
65
+ expect(() => new WsProvider(TEST_WS_URL, false, {}, undefined, 1024, -1000)).toThrow(/'ttl' must be between 0 and 1800000 ms or null to disable/);
66
+ });
67
+
68
+ it('Throws when WsProvider endpoint is an invalid string', () => {
69
+ expect(
70
+ () => new WsProvider('http://127.0.0.1:9955', 0)
71
+ ).toThrow(/^Endpoint should start with /);
72
+ });
73
+
74
+ // eslint-disable-next-line jest/expect-expect
75
+ it('Succeeds when WsProvider endpoint is a valid array', () => {
76
+ const endpoints: string[] = ['ws://127.0.0.1:9955', 'wss://testnet.io:9944', 'ws://mychain.com:9933'];
77
+
78
+ /* eslint-disable no-new */
79
+ new WsProvider(endpoints, 0);
80
+ });
81
+
82
+ it('Throws when WsProvider endpoint is an empty array', () => {
83
+ const endpoints: string[] = [];
84
+
85
+ expect(
86
+ () => new WsProvider(endpoints, 0)
87
+ ).toThrow('WsProvider requires at least one Endpoint');
88
+ });
89
+
90
+ it('Throws when WsProvider endpoint is an invalid array', () => {
91
+ const endpoints: string[] = ['ws://127.0.0.1:9955', 'http://bad.co:9944', 'ws://mychain.com:9933'];
92
+
93
+ expect(
94
+ () => new WsProvider(endpoints, 0)
95
+ ).toThrow(/^Endpoint should start with /);
96
+ });
97
+ });