@dedot/api 1.2.0 → 1.3.0

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.
@@ -257,15 +257,26 @@ class BaseSubstrateClient extends index_js_2.JsonRpcClient {
257
257
  this.on('connected', this.onConnected);
258
258
  // @ts-ignore
259
259
  this.on('disconnected', this.onDisconnected);
260
- return new Promise((resolve) => {
260
+ return new Promise((resolve, reject) => {
261
+ // @ts-ignore
262
+ const offError = this.on('error', (err) => {
263
+ reject(err instanceof Error ? err : new Error(String(err)));
264
+ });
261
265
  // @ts-ignore
262
266
  this.once('ready', () => {
267
+ offError();
263
268
  resolve(this);
264
269
  });
265
270
  });
266
271
  }
267
272
  onConnected = async () => {
268
- await this.initialize();
273
+ try {
274
+ await this.initialize();
275
+ }
276
+ catch (e) {
277
+ // @ts-ignore — surface init failure so the pending connect() promise can reject
278
+ this.emit('error', e);
279
+ }
269
280
  };
270
281
  onDisconnected = async () => { };
271
282
  async initialize() {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DedotClient = void 0;
4
+ const utils_1 = require("@dedot/utils");
4
5
  const index_js_1 = require("../json-rpc/index.js");
5
6
  const LegacyClient_js_1 = require("./LegacyClient.js");
6
7
  const V2Client_js_1 = require("./V2Client.js");
@@ -51,7 +52,12 @@ const V2Client_js_1 = require("./V2Client.js");
51
52
  */
52
53
  class DedotClient {
53
54
  #client;
54
- /** The JSON-RPC version being used ('v2' or 'legacy') */
55
+ #pendingOptions;
56
+ /**
57
+ * The JSON-RPC version being used ('v2' or 'legacy').
58
+ *
59
+ * In auto-detect mode (no `rpcVersion` option), this is resolved during `connect()`.
60
+ */
55
61
  rpcVersion;
56
62
  /**
57
63
  * Creates a new DedotClient instance.
@@ -61,18 +67,14 @@ class DedotClient {
61
67
  * @param options - Client configuration options or a JsonRpcProvider instance
62
68
  */
63
69
  constructor(options) {
64
- let rpcVersion = 'v2';
65
- if (!(0, index_js_1.isJsonRpcProvider)(options)) {
66
- if (options['rpcVersion'] === 'legacy') {
67
- rpcVersion = 'legacy';
68
- }
69
- }
70
- this.rpcVersion = rpcVersion;
71
- if (this.rpcVersion === 'legacy') {
72
- this.#client = new LegacyClient_js_1.LegacyClient(options);
70
+ const explicitVersion = (0, index_js_1.isJsonRpcProvider)(options) ? undefined : options.rpcVersion;
71
+ if (explicitVersion) {
72
+ this.rpcVersion = explicitVersion;
73
+ this.#client =
74
+ explicitVersion === 'legacy' ? new LegacyClient_js_1.LegacyClient(options) : new V2Client_js_1.V2Client(options);
73
75
  }
74
76
  else {
75
- this.#client = new V2Client_js_1.V2Client(options);
77
+ this.#pendingOptions = options;
76
78
  }
77
79
  }
78
80
  /**
@@ -284,10 +286,34 @@ class DedotClient {
284
286
  /**
285
287
  * Establishes connection to the blockchain network.
286
288
  *
289
+ * When `rpcVersion` was not specified, this tries JSON-RPC v2 first and
290
+ * transparently falls back to legacy if the node does not support v2.
291
+ *
287
292
  * @returns This client instance for method chaining
288
293
  */
289
294
  async connect() {
290
- await this.#client.connect();
295
+ if (this.#client) {
296
+ await this.#client.connect();
297
+ return this;
298
+ }
299
+ const options = this.#pendingOptions;
300
+ this.#pendingOptions = undefined;
301
+ const v2 = new V2Client_js_1.V2Client(options);
302
+ try {
303
+ await v2.connect();
304
+ this.#client = v2;
305
+ this.rpcVersion = 'v2';
306
+ }
307
+ catch (e) {
308
+ if (!(e instanceof utils_1.JsonRpcV2NotSupportedError))
309
+ throw e;
310
+ console.warn('JSON-RPC v2 is not supported by the connected node, falling back to legacy JSON-RPC.');
311
+ await v2.disconnect().catch(utils_1.noop);
312
+ const legacy = new LegacyClient_js_1.LegacyClient(options);
313
+ await legacy.connect();
314
+ this.#client = legacy;
315
+ this.rpcVersion = 'legacy';
316
+ }
291
317
  return this;
292
318
  }
293
319
  /**
@@ -78,6 +78,10 @@ class V2Client// prettier-end-here
78
78
  const shouldInitialize = !this._genesisHash;
79
79
  if (shouldInitialize) {
80
80
  const rpcMethods = (await this.rpc.rpc_methods()).methods;
81
+ if (!rpcMethods.some((m) => m.startsWith('chainHead_'))) {
82
+ throw new utils_1.JsonRpcV2NotSupportedError('The connected node does not support JSON-RPC v2 (no chainHead_* methods). ' +
83
+ 'Omit `rpcVersion` to auto-detect, or pass `rpcVersion: "legacy"`.');
84
+ }
81
85
  this._chainHead = new index_js_2.ChainHead(this, { rpcMethods });
82
86
  this._chainSpec = new index_js_2.ChainSpec(this, { rpcMethods });
83
87
  // Always initialize Archive, but only set up fallback if supported
@@ -253,15 +253,26 @@ export class BaseSubstrateClient extends JsonRpcClient {
253
253
  this.on('connected', this.onConnected);
254
254
  // @ts-ignore
255
255
  this.on('disconnected', this.onDisconnected);
256
- return new Promise((resolve) => {
256
+ return new Promise((resolve, reject) => {
257
+ // @ts-ignore
258
+ const offError = this.on('error', (err) => {
259
+ reject(err instanceof Error ? err : new Error(String(err)));
260
+ });
257
261
  // @ts-ignore
258
262
  this.once('ready', () => {
263
+ offError();
259
264
  resolve(this);
260
265
  });
261
266
  });
262
267
  }
263
268
  onConnected = async () => {
264
- await this.initialize();
269
+ try {
270
+ await this.initialize();
271
+ }
272
+ catch (e) {
273
+ // @ts-ignore — surface init failure so the pending connect() promise can reject
274
+ this.emit('error', e);
275
+ }
265
276
  };
266
277
  onDisconnected = async () => { };
267
278
  async initialize() {
@@ -9,9 +9,14 @@ import { ApiEvent, ApiOptions, BlockExplorer, ISubstrateClient, ISubstrateClient
9
9
  */
10
10
  export type ClientOptions = ApiOptions & {
11
11
  /**
12
- * The JSON-RPC version to use
13
- * - 'v2' (default): Uses the new JSON-RPC v2 specification
14
- * - 'legacy': Uses the legacy JSON-RPC specification for older nodes
12
+ * The JSON-RPC version to use.
13
+ *
14
+ * - _unset_ (default): auto-detect. Try JSON-RPC v2 first and transparently fall back
15
+ * to legacy if the connected node does not expose v2 methods (no `chainHead_*`).
16
+ * After `connect()` resolves, `client.rpcVersion` reflects the version that was picked.
17
+ * - `'v2'`: force JSON-RPC v2. Throws `JsonRpcV2NotSupportedError` during `connect()`
18
+ * if the node does not support v2.
19
+ * - `'legacy'`: force legacy JSON-RPC.
15
20
  */
16
21
  rpcVersion?: RpcVersion;
17
22
  };
@@ -62,7 +67,11 @@ export type ClientOptions = ApiOptions & {
62
67
  */
63
68
  export declare class DedotClient<ChainApi extends GenericSubstrateApi = SubstrateApi> implements ISubstrateClient<ChainApi, ApiEvent> {
64
69
  #private;
65
- /** The JSON-RPC version being used ('v2' or 'legacy') */
70
+ /**
71
+ * The JSON-RPC version being used ('v2' or 'legacy').
72
+ *
73
+ * In auto-detect mode (no `rpcVersion` option), this is resolved during `connect()`.
74
+ */
66
75
  rpcVersion: RpcVersion;
67
76
  /**
68
77
  * Creates a new DedotClient instance.
@@ -239,6 +248,9 @@ export declare class DedotClient<ChainApi extends GenericSubstrateApi = Substrat
239
248
  /**
240
249
  * Establishes connection to the blockchain network.
241
250
  *
251
+ * When `rpcVersion` was not specified, this tries JSON-RPC v2 first and
252
+ * transparently falls back to legacy if the node does not support v2.
253
+ *
242
254
  * @returns This client instance for method chaining
243
255
  */
244
256
  connect(): Promise<this>;
@@ -1,3 +1,4 @@
1
+ import { JsonRpcV2NotSupportedError, noop } from '@dedot/utils';
1
2
  import { isJsonRpcProvider } from '../json-rpc/index.js';
2
3
  import { LegacyClient } from './LegacyClient.js';
3
4
  import { V2Client } from './V2Client.js';
@@ -48,7 +49,12 @@ import { V2Client } from './V2Client.js';
48
49
  */
49
50
  export class DedotClient {
50
51
  #client;
51
- /** The JSON-RPC version being used ('v2' or 'legacy') */
52
+ #pendingOptions;
53
+ /**
54
+ * The JSON-RPC version being used ('v2' or 'legacy').
55
+ *
56
+ * In auto-detect mode (no `rpcVersion` option), this is resolved during `connect()`.
57
+ */
52
58
  rpcVersion;
53
59
  /**
54
60
  * Creates a new DedotClient instance.
@@ -58,18 +64,14 @@ export class DedotClient {
58
64
  * @param options - Client configuration options or a JsonRpcProvider instance
59
65
  */
60
66
  constructor(options) {
61
- let rpcVersion = 'v2';
62
- if (!isJsonRpcProvider(options)) {
63
- if (options['rpcVersion'] === 'legacy') {
64
- rpcVersion = 'legacy';
65
- }
66
- }
67
- this.rpcVersion = rpcVersion;
68
- if (this.rpcVersion === 'legacy') {
69
- this.#client = new LegacyClient(options);
67
+ const explicitVersion = isJsonRpcProvider(options) ? undefined : options.rpcVersion;
68
+ if (explicitVersion) {
69
+ this.rpcVersion = explicitVersion;
70
+ this.#client =
71
+ explicitVersion === 'legacy' ? new LegacyClient(options) : new V2Client(options);
70
72
  }
71
73
  else {
72
- this.#client = new V2Client(options);
74
+ this.#pendingOptions = options;
73
75
  }
74
76
  }
75
77
  /**
@@ -281,10 +283,34 @@ export class DedotClient {
281
283
  /**
282
284
  * Establishes connection to the blockchain network.
283
285
  *
286
+ * When `rpcVersion` was not specified, this tries JSON-RPC v2 first and
287
+ * transparently falls back to legacy if the node does not support v2.
288
+ *
284
289
  * @returns This client instance for method chaining
285
290
  */
286
291
  async connect() {
287
- await this.#client.connect();
292
+ if (this.#client) {
293
+ await this.#client.connect();
294
+ return this;
295
+ }
296
+ const options = this.#pendingOptions;
297
+ this.#pendingOptions = undefined;
298
+ const v2 = new V2Client(options);
299
+ try {
300
+ await v2.connect();
301
+ this.#client = v2;
302
+ this.rpcVersion = 'v2';
303
+ }
304
+ catch (e) {
305
+ if (!(e instanceof JsonRpcV2NotSupportedError))
306
+ throw e;
307
+ console.warn('JSON-RPC v2 is not supported by the connected node, falling back to legacy JSON-RPC.');
308
+ await v2.disconnect().catch(noop);
309
+ const legacy = new LegacyClient(options);
310
+ await legacy.connect();
311
+ this.#client = legacy;
312
+ this.rpcVersion = 'legacy';
313
+ }
288
314
  return this;
289
315
  }
290
316
  /**
@@ -1,6 +1,6 @@
1
1
  import { $H256, $Header, $RuntimeVersion, PortableRegistry, } from '@dedot/codecs';
2
2
  import { u32 } from '@dedot/shape';
3
- import { assert, concatU8a, DedotError, twox64Concat, u8aToHex, xxhashAsU8a } from '@dedot/utils';
3
+ import { assert, concatU8a, DedotError, JsonRpcV2NotSupportedError, twox64Concat, u8aToHex, xxhashAsU8a, } from '@dedot/utils';
4
4
  import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutorV2, StorageQueryExecutorV2, TxExecutorV2, ViewFunctionExecutorV2, } from '../executor/index.js';
5
5
  import { SubmittableExtrinsicV2 } from '../extrinsic/submittable/SubmittableExtrinsicV2.js';
6
6
  import { Archive, ChainHead, ChainSpec, Transaction, TransactionWatch } from '../json-rpc/index.js';
@@ -75,6 +75,10 @@ export class V2Client// prettier-end-here
75
75
  const shouldInitialize = !this._genesisHash;
76
76
  if (shouldInitialize) {
77
77
  const rpcMethods = (await this.rpc.rpc_methods()).methods;
78
+ if (!rpcMethods.some((m) => m.startsWith('chainHead_'))) {
79
+ throw new JsonRpcV2NotSupportedError('The connected node does not support JSON-RPC v2 (no chainHead_* methods). ' +
80
+ 'Omit `rpcVersion` to auto-detect, or pass `rpcVersion: "legacy"`.');
81
+ }
78
82
  this._chainHead = new ChainHead(this, { rpcMethods });
79
83
  this._chainSpec = new ChainSpec(this, { rpcMethods });
80
84
  // Always initialize Archive, but only set up fallback if supported
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dedot/api",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
5
5
  "author": "Thang X. Vu <thang@dedot.dev>",
6
6
  "homepage": "https://dedot.dev",
@@ -13,13 +13,13 @@
13
13
  "type": "module",
14
14
  "sideEffects": false,
15
15
  "dependencies": {
16
- "@dedot/codecs": "^1.2.0",
17
- "@dedot/providers": "^1.2.0",
18
- "@dedot/runtime-specs": "^1.2.0",
19
- "@dedot/shape": "^1.2.0",
20
- "@dedot/storage": "^1.2.0",
21
- "@dedot/types": "^1.2.0",
22
- "@dedot/utils": "^1.2.0"
16
+ "@dedot/codecs": "^1.3.0",
17
+ "@dedot/providers": "^1.3.0",
18
+ "@dedot/runtime-specs": "^1.3.0",
19
+ "@dedot/shape": "^1.3.0",
20
+ "@dedot/storage": "^1.3.0",
21
+ "@dedot/types": "^1.3.0",
22
+ "@dedot/utils": "^1.3.0"
23
23
  },
24
24
  "scripts": {
25
25
  "build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json",
@@ -48,7 +48,7 @@
48
48
  "node": ">=18"
49
49
  },
50
50
  "license": "Apache-2.0",
51
- "gitHead": "d0da366e311d33389d0fd1b9e9435c1d61da5799",
51
+ "gitHead": "1179ba75b371517a21eb1162881e6e4c655514a0",
52
52
  "module": "./index.js",
53
53
  "types": "./index.d.ts"
54
54
  }