@pezkuwi/rpc-provider 16.5.5

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 (64) hide show
  1. package/README.md +68 -0
  2. package/build/bundle.d.ts +5 -0
  3. package/build/coder/error.d.ts +29 -0
  4. package/build/coder/index.d.ts +8 -0
  5. package/build/defaults.d.ts +5 -0
  6. package/build/http/index.d.ts +81 -0
  7. package/build/http/types.d.ts +7 -0
  8. package/build/index.d.ts +2 -0
  9. package/build/lru.d.ts +15 -0
  10. package/build/mock/index.d.ts +35 -0
  11. package/build/mock/mockHttp.d.ts +9 -0
  12. package/build/mock/mockWs.d.ts +26 -0
  13. package/build/mock/types.d.ts +23 -0
  14. package/build/packageDetect.d.ts +1 -0
  15. package/build/packageInfo.d.ts +6 -0
  16. package/build/substrate-connect/Health.d.ts +7 -0
  17. package/build/substrate-connect/index.d.ts +22 -0
  18. package/build/substrate-connect/types.d.ts +12 -0
  19. package/build/types.d.ts +85 -0
  20. package/build/ws/errors.d.ts +1 -0
  21. package/build/ws/index.d.ts +121 -0
  22. package/package.json +43 -0
  23. package/src/bundle.ts +8 -0
  24. package/src/coder/decodeResponse.spec.ts +70 -0
  25. package/src/coder/encodeJson.spec.ts +20 -0
  26. package/src/coder/encodeObject.spec.ts +25 -0
  27. package/src/coder/error.spec.ts +111 -0
  28. package/src/coder/error.ts +66 -0
  29. package/src/coder/index.ts +88 -0
  30. package/src/defaults.ts +10 -0
  31. package/src/http/index.spec.ts +72 -0
  32. package/src/http/index.ts +238 -0
  33. package/src/http/send.spec.ts +61 -0
  34. package/src/http/types.ts +11 -0
  35. package/src/index.ts +6 -0
  36. package/src/lru.spec.ts +74 -0
  37. package/src/lru.ts +197 -0
  38. package/src/mock/index.ts +259 -0
  39. package/src/mock/mockHttp.ts +35 -0
  40. package/src/mock/mockWs.ts +92 -0
  41. package/src/mock/on.spec.ts +43 -0
  42. package/src/mock/send.spec.ts +38 -0
  43. package/src/mock/subscribe.spec.ts +81 -0
  44. package/src/mock/types.ts +36 -0
  45. package/src/mock/unsubscribe.spec.ts +57 -0
  46. package/src/mod.ts +4 -0
  47. package/src/packageDetect.ts +12 -0
  48. package/src/packageInfo.ts +6 -0
  49. package/src/substrate-connect/Health.ts +325 -0
  50. package/src/substrate-connect/index.spec.ts +638 -0
  51. package/src/substrate-connect/index.ts +415 -0
  52. package/src/substrate-connect/types.ts +16 -0
  53. package/src/types.ts +101 -0
  54. package/src/ws/connect.spec.ts +167 -0
  55. package/src/ws/errors.ts +41 -0
  56. package/src/ws/index.spec.ts +97 -0
  57. package/src/ws/index.ts +652 -0
  58. package/src/ws/send.spec.ts +126 -0
  59. package/src/ws/state.spec.ts +20 -0
  60. package/src/ws/subscribe.spec.ts +68 -0
  61. package/src/ws/unsubscribe.spec.ts +100 -0
  62. package/tsconfig.build.json +17 -0
  63. package/tsconfig.build.tsbuildinfo +1 -0
  64. package/tsconfig.spec.json +18 -0
@@ -0,0 +1,70 @@
1
+ // Copyright 2017-2025 @polkadot/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 { JsonRpcResponse } from '../types.js';
7
+
8
+ import { RpcCoder } from './index.js';
9
+
10
+ describe('decodeResponse', (): void => {
11
+ let coder: RpcCoder;
12
+
13
+ beforeEach((): void => {
14
+ coder = new RpcCoder();
15
+ });
16
+
17
+ it('expects a non-empty input object', (): void => {
18
+ expect(
19
+ () => coder.decodeResponse(undefined as unknown as JsonRpcResponse<unknown>)
20
+ ).toThrow(/Invalid jsonrpc/);
21
+ });
22
+
23
+ it('expects a valid jsonrpc field', (): void => {
24
+ expect(
25
+ () => coder.decodeResponse({} as JsonRpcResponse<unknown>)
26
+ ).toThrow(/Invalid jsonrpc/);
27
+ });
28
+
29
+ it('expects a valid id field', (): void => {
30
+ expect(
31
+ () => coder.decodeResponse({ jsonrpc: '2.0' } as JsonRpcResponse<unknown>)
32
+ ).toThrow(/Invalid id/);
33
+ });
34
+
35
+ it('expects a valid result field', (): void => {
36
+ expect(
37
+ () => coder.decodeResponse({ id: 1, jsonrpc: '2.0' } as JsonRpcResponse<unknown>)
38
+ ).toThrow(/No result/);
39
+ });
40
+
41
+ it('throws any error found', (): void => {
42
+ expect(
43
+ () => coder.decodeResponse({ error: { code: 123, message: 'test error' }, id: 1, jsonrpc: '2.0' } as JsonRpcResponse<unknown>)
44
+ ).toThrow(/123: test error/);
45
+ });
46
+
47
+ it('throws any error found, with data', (): void => {
48
+ expect(
49
+ () => coder.decodeResponse({ error: { code: 123, data: 'Error("Some random error description")', message: 'test error' }, id: 1, jsonrpc: '2.0' } as JsonRpcResponse<unknown>)
50
+ ).toThrow(/123: test error: Some random error description/);
51
+ });
52
+
53
+ it('allows for number subscription ids', (): void => {
54
+ expect(
55
+ coder.decodeResponse({ id: 1, jsonrpc: '2.0', method: 'test', params: { result: 'test result', subscription: 1 } } as JsonRpcResponse<unknown>)
56
+ ).toEqual('test result');
57
+ });
58
+
59
+ it('allows for string subscription ids', (): void => {
60
+ expect(
61
+ coder.decodeResponse({ id: 1, jsonrpc: '2.0', method: 'test', params: { result: 'test result', subscription: 'abc' } } as JsonRpcResponse<unknown>)
62
+ ).toEqual('test result');
63
+ });
64
+
65
+ it('returns the result', (): void => {
66
+ expect(
67
+ coder.decodeResponse({ id: 1, jsonrpc: '2.0', result: 'some result' } as JsonRpcResponse<unknown>)
68
+ ).toEqual('some result');
69
+ });
70
+ });
@@ -0,0 +1,20 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import { RpcCoder } from './index.js';
7
+
8
+ describe('encodeJson', (): void => {
9
+ let coder: RpcCoder;
10
+
11
+ beforeEach((): void => {
12
+ coder = new RpcCoder();
13
+ });
14
+
15
+ it('encodes a valid JsonRPC JSON string', (): void => {
16
+ expect(
17
+ coder.encodeJson('method', ['params'])
18
+ ).toEqual([1, '{"id":1,"jsonrpc":"2.0","method":"method","params":["params"]}']);
19
+ });
20
+ });
@@ -0,0 +1,25 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import { RpcCoder } from './index.js';
7
+
8
+ describe('encodeObject', (): void => {
9
+ let coder: RpcCoder;
10
+
11
+ beforeEach((): void => {
12
+ coder = new RpcCoder();
13
+ });
14
+
15
+ it('encodes a valid JsonRPC object', (): void => {
16
+ expect(
17
+ coder.encodeObject('method', ['a', 'b'])
18
+ ).toEqual([1, {
19
+ id: 1,
20
+ jsonrpc: '2.0',
21
+ method: 'method',
22
+ params: ['a', 'b']
23
+ }]);
24
+ });
25
+ });
@@ -0,0 +1,111 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import { isError } from '@pezkuwi/util';
7
+
8
+ import RpcError from './error.js';
9
+
10
+ describe('RpcError', (): void => {
11
+ describe('constructor', (): void => {
12
+ it('constructs an Error that is still an Error', (): void => {
13
+ expect(
14
+ isError(
15
+ new RpcError()
16
+ )
17
+ ).toEqual(true);
18
+ });
19
+ });
20
+
21
+ describe('static', (): void => {
22
+ it('exposes the .CODES as a static', (): void => {
23
+ expect(
24
+ Object.keys(RpcError.CODES)
25
+ ).not.toEqual(0);
26
+ });
27
+ });
28
+
29
+ describe('constructor properties', (): void => {
30
+ it('sets the .message property', (): void => {
31
+ expect(
32
+ new RpcError('test message').message
33
+ ).toEqual('test message');
34
+ });
35
+
36
+ it("sets the .message to '' when not set", (): void => {
37
+ expect(
38
+ new RpcError().message
39
+ ).toEqual('');
40
+ });
41
+
42
+ it('sets the .code property', (): void => {
43
+ expect(
44
+ new RpcError('test message', 1234).code
45
+ ).toEqual(1234);
46
+ });
47
+
48
+ it('sets the .code to UKNOWN when not set', (): void => {
49
+ expect(
50
+ new RpcError('test message').code
51
+ ).toEqual(RpcError.CODES.UNKNOWN);
52
+ });
53
+
54
+ it('sets the .data property', (): void => {
55
+ const data = 'here';
56
+
57
+ expect(
58
+ new RpcError('test message', 1234, data).data
59
+ ).toEqual(data);
60
+ });
61
+
62
+ it('sets the .data property to generic value', (): void => {
63
+ const data = { custom: 'value' } as const;
64
+
65
+ expect(
66
+ new RpcError('test message', 1234, data).data
67
+ ).toEqual(data);
68
+ });
69
+ });
70
+
71
+ describe('stack traces', (): void => {
72
+ // eslint-disable-next-line @typescript-eslint/ban-types
73
+ let captureStackTrace: (targetObject: Record<string, any>, constructorOpt?: Function | undefined) => void;
74
+
75
+ beforeEach((): void => {
76
+ captureStackTrace = Error.captureStackTrace;
77
+
78
+ Error.captureStackTrace = function (error): void {
79
+ Object.defineProperty(error, 'stack', {
80
+ configurable: true,
81
+ get: function getStack (): string {
82
+ const value = 'some stack returned';
83
+
84
+ Object.defineProperty(this, 'stack', { value });
85
+
86
+ return value;
87
+ }
88
+ });
89
+ };
90
+ });
91
+
92
+ afterEach((): void => {
93
+ Error.captureStackTrace = captureStackTrace;
94
+ });
95
+
96
+ it('captures via captureStackTrace when available', (): void => {
97
+ expect(
98
+ new RpcError().stack
99
+ ).toEqual('some stack returned');
100
+ });
101
+
102
+ it('captures via stack when captureStackTrace not available', (): void => {
103
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
104
+ Error.captureStackTrace = null as any;
105
+
106
+ expect(
107
+ new RpcError().stack.length
108
+ ).not.toEqual(0);
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,66 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { RpcErrorInterface } from '../types.js';
5
+
6
+ import { isFunction } from '@pezkuwi/util';
7
+
8
+ const UNKNOWN = -99999;
9
+
10
+ function extend<Data, K extends keyof RpcError<Data>> (that: RpcError<Data>, name: K, value: RpcError<Data>[K]): void {
11
+ Object.defineProperty(that, name, {
12
+ configurable: true,
13
+ enumerable: false,
14
+ value
15
+ });
16
+ }
17
+
18
+ /**
19
+ * @name RpcError
20
+ * @summary Extension to the basic JS Error.
21
+ * @description
22
+ * 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.
23
+ * @example
24
+ * <BR>
25
+ *
26
+ * ```javascript
27
+ * const { RpcError } from '@pezkuwi/util');
28
+ *
29
+ * throw new RpcError('some message', RpcError.CODES.METHOD_NOT_FOUND); // => error.code = -32601
30
+ * ```
31
+ */
32
+ export default class RpcError<T = never> extends Error implements RpcErrorInterface<T> {
33
+ public code!: number;
34
+
35
+ public data?: T;
36
+
37
+ public override message!: string;
38
+
39
+ public override name!: string;
40
+
41
+ public override stack!: string;
42
+
43
+ public constructor (message = '', code: number = UNKNOWN, data?: T) {
44
+ super();
45
+
46
+ extend(this, 'message', String(message));
47
+ extend(this, 'name', this.constructor.name);
48
+ extend(this, 'data', data);
49
+ extend(this, 'code', code);
50
+
51
+ if (isFunction(Error.captureStackTrace)) {
52
+ Error.captureStackTrace(this, this.constructor);
53
+ } else {
54
+ const { stack } = new Error(message);
55
+
56
+ stack && extend(this, 'stack', stack);
57
+ }
58
+ }
59
+
60
+ public static CODES = {
61
+ ASSERT: -90009,
62
+ INVALID_JSONRPC: -99998,
63
+ METHOD_NOT_FOUND: -32601, // Rust client
64
+ UNKNOWN
65
+ };
66
+ }
@@ -0,0 +1,88 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { JsonRpcRequest, JsonRpcResponse, JsonRpcResponseBaseError } from '../types.js';
5
+
6
+ import { isNumber, isString, isUndefined, stringify } from '@pezkuwi/util';
7
+
8
+ import RpcError from './error.js';
9
+
10
+ function formatErrorData (data?: string | number): string {
11
+ if (isUndefined(data)) {
12
+ return '';
13
+ }
14
+
15
+ const formatted = `: ${isString(data)
16
+ ? data.replace(/Error\("/g, '').replace(/\("/g, '(').replace(/"\)/g, ')').replace(/\(/g, ', ').replace(/\)/g, '')
17
+ : stringify(data)}`;
18
+
19
+ // We need some sort of cut-off here since these can be very large and
20
+ // very nested, pick a number and trim the result display to it
21
+ return formatted.length <= 256
22
+ ? formatted
23
+ : `${formatted.substring(0, 255)}…`;
24
+ }
25
+
26
+ function checkError (error?: JsonRpcResponseBaseError): void {
27
+ if (error) {
28
+ const { code, data, message } = error;
29
+
30
+ throw new RpcError(`${code}: ${message}${formatErrorData(data)}`, code, data);
31
+ }
32
+ }
33
+
34
+ /** @internal */
35
+ export class RpcCoder {
36
+ #id = 0;
37
+
38
+ public decodeResponse <T> (response?: JsonRpcResponse<T>): T {
39
+ if (!response || response.jsonrpc !== '2.0') {
40
+ throw new Error('Invalid jsonrpc field in decoded object');
41
+ }
42
+
43
+ const isSubscription = !isUndefined(response.params) && !isUndefined(response.method);
44
+
45
+ if (
46
+ !isNumber(response.id) &&
47
+ (
48
+ !isSubscription || (
49
+ !isNumber(response.params.subscription) &&
50
+ !isString(response.params.subscription)
51
+ )
52
+ )
53
+ ) {
54
+ throw new Error('Invalid id field in decoded object');
55
+ }
56
+
57
+ checkError(response.error);
58
+
59
+ if (response.result === undefined && !isSubscription) {
60
+ throw new Error('No result found in jsonrpc response');
61
+ }
62
+
63
+ if (isSubscription) {
64
+ checkError(response.params.error);
65
+
66
+ return response.params.result;
67
+ }
68
+
69
+ return response.result;
70
+ }
71
+
72
+ public encodeJson (method: string, params: unknown[]): [number, string] {
73
+ const [id, data] = this.encodeObject(method, params);
74
+
75
+ return [id, stringify(data)];
76
+ }
77
+
78
+ public encodeObject (method: string, params: unknown[]): [number, JsonRpcRequest] {
79
+ const id = ++this.#id;
80
+
81
+ return [id, {
82
+ id,
83
+ jsonrpc: '2.0',
84
+ method,
85
+ params
86
+ }];
87
+ }
88
+ }
@@ -0,0 +1,10 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ const HTTP_URL = 'http://127.0.0.1:9933';
5
+ const WS_URL = 'ws://127.0.0.1:9944';
6
+
7
+ export default {
8
+ HTTP_URL,
9
+ WS_URL
10
+ };
@@ -0,0 +1,72 @@
1
+ // Copyright 2017-2025 @polkadot/rpc-provider authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@pezkuwi/dev-test/globals.d.ts" />
5
+
6
+ import { TEST_HTTP_URL } from '../mock/mockHttp.js';
7
+ import { HttpProvider } from './index.js';
8
+
9
+ describe('Http', (): void => {
10
+ let http: HttpProvider;
11
+
12
+ beforeEach((): void => {
13
+ http = new HttpProvider(TEST_HTTP_URL);
14
+ });
15
+
16
+ it('requires an http:// prefixed endpoint', (): void => {
17
+ expect(
18
+ () => new HttpProvider('ws://')
19
+ ).toThrow(/with 'http/);
20
+ });
21
+
22
+ it('allows https:// endpoints', (): void => {
23
+ expect(
24
+ () => new HttpProvider('https://')
25
+ ).not.toThrow();
26
+ });
27
+
28
+ it('allows custom headers', (): void => {
29
+ expect(
30
+ () => new HttpProvider('https://', { foo: 'bar' })
31
+ ).not.toThrow();
32
+ });
33
+
34
+ it('should throw error on negative cache capacity or TTL', () => {
35
+ expect(() =>
36
+ new HttpProvider(TEST_HTTP_URL, {}, -5, 30000)
37
+ ).toThrow(/'capacity' must be a non-negative integer/);
38
+
39
+ expect(() =>
40
+ new HttpProvider(TEST_HTTP_URL, {}, 1024, -1000)
41
+ ).toThrow(/'ttl' must be between 0 and 1800000 ms or null to disable/);
42
+ });
43
+
44
+ it('allow clone', (): void => {
45
+ const clone = http.clone();
46
+ /* eslint-disable */
47
+ expect((clone as any)['#endpoint']).toEqual((http as any)['#endpoint']);
48
+ expect((clone as any)['#headers']).toEqual((http as any)['#headers']);
49
+ /* eslint-enable */
50
+ });
51
+
52
+ it('always returns isConnected true', (): void => {
53
+ expect(http.isConnected).toEqual(true);
54
+ });
55
+
56
+ it('does not (yet) support subscribe', async (): Promise<void> => {
57
+ await http.subscribe('', '', [], (cb): void => {
58
+ // eslint-disable-next-line jest/no-conditional-expect
59
+ expect(cb).toEqual(expect.anything());
60
+ }).catch((error): void => {
61
+ // eslint-disable-next-line jest/no-conditional-expect
62
+ expect((error as Error).message).toMatch(/does not have subscriptions/);
63
+ });
64
+ });
65
+
66
+ it('does not (yet) support unsubscribe', async (): Promise<void> => {
67
+ await http.unsubscribe('', '', 0).catch((error): void => {
68
+ // eslint-disable-next-line jest/no-conditional-expect
69
+ expect((error as Error).message).toMatch(/does not have subscriptions/);
70
+ });
71
+ });
72
+ });