@mysten/sui 2.9.1 → 2.11.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.
- package/CHANGELOG.md +22 -0
- package/dist/bcs/bcs.d.mts +6 -6
- package/dist/client/core.d.mts +1 -0
- package/dist/client/core.d.mts.map +1 -1
- package/dist/client/core.mjs +17 -4
- package/dist/client/core.mjs.map +1 -1
- package/dist/client/types.d.mts +16 -0
- package/dist/client/types.d.mts.map +1 -1
- package/dist/cryptography/signature.d.mts +6 -6
- package/dist/graphql/core.d.mts +1 -0
- package/dist/graphql/core.d.mts.map +1 -1
- package/dist/graphql/core.mjs +13 -1
- package/dist/graphql/core.mjs.map +1 -1
- package/dist/graphql/generated/queries.d.mts.map +1 -1
- package/dist/graphql/generated/queries.mjs +18 -1
- package/dist/graphql/generated/queries.mjs.map +1 -1
- package/dist/grpc/core.d.mts +1 -0
- package/dist/grpc/core.d.mts.map +1 -1
- package/dist/grpc/core.mjs +12 -0
- package/dist/grpc/core.mjs.map +1 -1
- package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/transaction.d.mts.map +1 -1
- package/dist/jsonRpc/core.d.mts +1 -0
- package/dist/jsonRpc/core.d.mts.map +1 -1
- package/dist/jsonRpc/core.mjs +19 -0
- package/dist/jsonRpc/core.mjs.map +1 -1
- package/dist/jsonRpc/http-transport.d.mts +1 -17
- package/dist/jsonRpc/http-transport.d.mts.map +1 -1
- package/dist/jsonRpc/http-transport.mjs +0 -23
- package/dist/jsonRpc/http-transport.mjs.map +1 -1
- package/dist/jsonRpc/index.d.mts +3 -3
- package/dist/jsonRpc/types/common.d.mts +1 -2
- package/dist/jsonRpc/types/common.d.mts.map +1 -1
- package/dist/jsonRpc/types/index.d.mts +1 -1
- package/dist/transactions/Transaction.d.mts +6 -6
- package/dist/transactions/Transaction.d.mts.map +1 -1
- package/dist/transactions/data/internal.d.mts +109 -109
- package/dist/transactions/data/internal.d.mts.map +1 -1
- package/dist/transactions/data/v1.d.mts +220 -220
- package/dist/transactions/data/v1.d.mts.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/dist/zklogin/bcs.d.mts +14 -14
- package/package.json +2 -4
- package/src/client/core.ts +34 -4
- package/src/client/types.ts +20 -0
- package/src/graphql/core.ts +27 -0
- package/src/graphql/generated/queries.ts +22 -0
- package/src/graphql/queries/getProtocolConfig.graphql +18 -0
- package/src/grpc/core.ts +27 -0
- package/src/jsonRpc/core.ts +34 -0
- package/src/jsonRpc/http-transport.ts +0 -52
- package/src/jsonRpc/index.ts +0 -1
- package/src/jsonRpc/types/common.ts +0 -1
- package/src/version.ts +1 -1
- package/dist/jsonRpc/rpc-websocket-client.d.mts +0 -26
- package/dist/jsonRpc/rpc-websocket-client.d.mts.map +0 -1
- package/dist/jsonRpc/rpc-websocket-client.mjs +0 -135
- package/dist/jsonRpc/rpc-websocket-client.mjs.map +0 -1
- package/src/jsonRpc/rpc-websocket-client.ts +0 -241
package/src/client/core.ts
CHANGED
|
@@ -100,6 +100,10 @@ export abstract class CoreClient extends BaseClient implements SuiClientTypes.Tr
|
|
|
100
100
|
options?: SuiClientTypes.GetCurrentSystemStateOptions,
|
|
101
101
|
): Promise<SuiClientTypes.GetCurrentSystemStateResponse>;
|
|
102
102
|
|
|
103
|
+
abstract getProtocolConfig(
|
|
104
|
+
options?: SuiClientTypes.GetProtocolConfigOptions,
|
|
105
|
+
): Promise<SuiClientTypes.GetProtocolConfigResponse>;
|
|
106
|
+
|
|
103
107
|
abstract getChainIdentifier(
|
|
104
108
|
options?: SuiClientTypes.GetChainIdentifierOptions,
|
|
105
109
|
): Promise<SuiClientTypes.GetChainIdentifierResponse>;
|
|
@@ -217,7 +221,7 @@ export abstract class CoreClient extends BaseClient implements SuiClientTypes.Tr
|
|
|
217
221
|
async waitForTransaction<Include extends SuiClientTypes.TransactionInclude = {}>(
|
|
218
222
|
options: SuiClientTypes.WaitForTransactionOptions<Include>,
|
|
219
223
|
): Promise<SuiClientTypes.TransactionResult<Include>> {
|
|
220
|
-
const { signal, timeout = 60 * 1000, include } = options;
|
|
224
|
+
const { signal, timeout = 60 * 1000, pollSchedule, include } = options;
|
|
221
225
|
|
|
222
226
|
const digest =
|
|
223
227
|
'result' in options && options.result
|
|
@@ -236,7 +240,35 @@ export abstract class CoreClient extends BaseClient implements SuiClientTypes.Tr
|
|
|
236
240
|
// Swallow unhandled rejections that might be thrown after early return
|
|
237
241
|
});
|
|
238
242
|
|
|
243
|
+
// Default schedule tuned to testnet measurements:
|
|
244
|
+
// - Fullnode (gRPC/JSON-RPC): p50=130ms, p95=330ms
|
|
245
|
+
// - GraphQL indexer: p50=1300ms, p95=1430ms
|
|
246
|
+
// After schedule exhausted, repeats the last interval.
|
|
247
|
+
const schedule = pollSchedule ?? [0, 300, 600, 1500, 3500];
|
|
248
|
+
const t0 = Date.now();
|
|
249
|
+
let scheduleIndex = 0;
|
|
250
|
+
const lastInterval =
|
|
251
|
+
schedule.length > 0
|
|
252
|
+
? schedule[schedule.length - 1] - (schedule[schedule.length - 2] ?? 0)
|
|
253
|
+
: 2_000;
|
|
254
|
+
|
|
239
255
|
while (true) {
|
|
256
|
+
if (scheduleIndex < schedule.length) {
|
|
257
|
+
const remaining = t0 + schedule[scheduleIndex] - Date.now();
|
|
258
|
+
scheduleIndex++;
|
|
259
|
+
if (remaining > 0) {
|
|
260
|
+
await Promise.race([
|
|
261
|
+
new Promise((resolve) => setTimeout(resolve, remaining)),
|
|
262
|
+
abortPromise,
|
|
263
|
+
]);
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
await Promise.race([
|
|
267
|
+
new Promise((resolve) => setTimeout(resolve, lastInterval)),
|
|
268
|
+
abortPromise,
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
|
|
240
272
|
abortSignal.throwIfAborted();
|
|
241
273
|
try {
|
|
242
274
|
return await this.getTransaction({
|
|
@@ -244,9 +276,7 @@ export abstract class CoreClient extends BaseClient implements SuiClientTypes.Tr
|
|
|
244
276
|
include,
|
|
245
277
|
signal: abortSignal,
|
|
246
278
|
});
|
|
247
|
-
} catch {
|
|
248
|
-
await Promise.race([new Promise((resolve) => setTimeout(resolve, 2_000)), abortPromise]);
|
|
249
|
-
}
|
|
279
|
+
} catch {}
|
|
250
280
|
}
|
|
251
281
|
}
|
|
252
282
|
|
package/src/client/types.ts
CHANGED
|
@@ -404,6 +404,8 @@ export namespace SuiClientTypes {
|
|
|
404
404
|
Include extends TransactionInclude = {},
|
|
405
405
|
> extends GetTransactionOptions<Include> {
|
|
406
406
|
timeout?: number;
|
|
407
|
+
/** Absolute times (ms from start) to poll, e.g. [0, 300, 600, 1500]. After exhausted, repeats the last interval. */
|
|
408
|
+
pollSchedule?: number[];
|
|
407
409
|
result?: never;
|
|
408
410
|
}
|
|
409
411
|
|
|
@@ -413,6 +415,8 @@ export namespace SuiClientTypes {
|
|
|
413
415
|
result: TransactionResult<any>;
|
|
414
416
|
include?: Include & TransactionInclude;
|
|
415
417
|
timeout?: number;
|
|
418
|
+
/** Absolute times (ms from start) to poll, e.g. [0, 300, 600, 1500]. After exhausted, repeats the last interval. */
|
|
419
|
+
pollSchedule?: number[];
|
|
416
420
|
digest?: never;
|
|
417
421
|
}
|
|
418
422
|
|
|
@@ -464,6 +468,22 @@ export namespace SuiClientTypes {
|
|
|
464
468
|
systemState: SystemStateInfo;
|
|
465
469
|
}
|
|
466
470
|
|
|
471
|
+
export interface GetProtocolConfigOptions extends CoreClientMethodOptions {}
|
|
472
|
+
|
|
473
|
+
export interface TransportMethods {
|
|
474
|
+
getProtocolConfig?: (options?: GetProtocolConfigOptions) => Promise<GetProtocolConfigResponse>;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export interface GetProtocolConfigResponse {
|
|
478
|
+
protocolConfig: ProtocolConfig;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export interface ProtocolConfig {
|
|
482
|
+
protocolVersion: string;
|
|
483
|
+
featureFlags: Record<string, boolean>;
|
|
484
|
+
attributes: Record<string, string | null>;
|
|
485
|
+
}
|
|
486
|
+
|
|
467
487
|
export interface GetChainIdentifierOptions extends CoreClientMethodOptions {}
|
|
468
488
|
|
|
469
489
|
export interface TransportMethods {
|
package/src/graphql/core.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
GetCoinsDocument,
|
|
25
25
|
GetCurrentSystemStateDocument,
|
|
26
26
|
GetMoveFunctionDocument,
|
|
27
|
+
GetProtocolConfigDocument,
|
|
27
28
|
GetOwnedObjectsDocument,
|
|
28
29
|
GetReferenceGasPriceDocument,
|
|
29
30
|
GetTransactionBlockDocument,
|
|
@@ -453,6 +454,32 @@ export class GraphQLCoreClient extends CoreClient {
|
|
|
453
454
|
};
|
|
454
455
|
}
|
|
455
456
|
|
|
457
|
+
async getProtocolConfig(): Promise<SuiClientTypes.GetProtocolConfigResponse> {
|
|
458
|
+
const result = await this.#graphqlQuery(
|
|
459
|
+
{
|
|
460
|
+
query: GetProtocolConfigDocument,
|
|
461
|
+
},
|
|
462
|
+
(result) => result.epoch?.protocolConfigs,
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
const featureFlags: Record<string, boolean> = {};
|
|
466
|
+
for (const flag of result?.featureFlags ?? []) {
|
|
467
|
+
featureFlags[flag.key] = flag.value;
|
|
468
|
+
}
|
|
469
|
+
const attributes: Record<string, string | null> = {};
|
|
470
|
+
for (const config of result?.configs ?? []) {
|
|
471
|
+
attributes[config.key] = config.value ?? null;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
protocolConfig: {
|
|
476
|
+
protocolVersion: result?.protocolVersion?.toString() ?? (null as never),
|
|
477
|
+
featureFlags,
|
|
478
|
+
attributes,
|
|
479
|
+
},
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
456
483
|
async getCurrentSystemState(): Promise<SuiClientTypes.GetCurrentSystemStateResponse> {
|
|
457
484
|
const result = await this.#graphqlQuery(
|
|
458
485
|
{
|
|
@@ -4868,6 +4868,11 @@ export type GetMoveFunctionQueryVariables = Exact<{
|
|
|
4868
4868
|
|
|
4869
4869
|
export type GetMoveFunctionQuery = { __typename?: 'Query', package?: { __typename?: 'MovePackage', module?: { __typename?: 'MoveModule', function?: { __typename?: 'MoveFunction', name: string, visibility?: MoveVisibility | null, isEntry?: boolean | null, typeParameters?: Array<{ __typename?: 'MoveFunctionTypeParameter', constraints: Array<MoveAbility> }> | null, parameters?: Array<{ __typename?: 'OpenMoveType', signature: OpenMoveTypeSignature }> | null, return?: Array<{ __typename?: 'OpenMoveType', signature: OpenMoveTypeSignature }> | null } | null } | null } | null };
|
|
4870
4870
|
|
|
4871
|
+
export type GetProtocolConfigQueryVariables = Exact<{ [key: string]: never; }>;
|
|
4872
|
+
|
|
4873
|
+
|
|
4874
|
+
export type GetProtocolConfigQuery = { __typename?: 'Query', epoch?: { __typename?: 'Epoch', protocolConfigs?: { __typename?: 'ProtocolConfigs', protocolVersion: number, featureFlags: Array<{ __typename?: 'FeatureFlag', key: string, value: boolean }>, configs: Array<{ __typename?: 'ProtocolConfig', key: string, value?: string | null }> } | null } | null };
|
|
4875
|
+
|
|
4871
4876
|
export type GetReferenceGasPriceQueryVariables = Exact<{ [key: string]: never; }>;
|
|
4872
4877
|
|
|
4873
4878
|
|
|
@@ -5402,6 +5407,23 @@ export const GetMoveFunctionDocument = new TypedDocumentString(`
|
|
|
5402
5407
|
}
|
|
5403
5408
|
}
|
|
5404
5409
|
`) as unknown as TypedDocumentString<GetMoveFunctionQuery, GetMoveFunctionQueryVariables>;
|
|
5410
|
+
export const GetProtocolConfigDocument = new TypedDocumentString(`
|
|
5411
|
+
query getProtocolConfig {
|
|
5412
|
+
epoch {
|
|
5413
|
+
protocolConfigs {
|
|
5414
|
+
protocolVersion
|
|
5415
|
+
featureFlags {
|
|
5416
|
+
key
|
|
5417
|
+
value
|
|
5418
|
+
}
|
|
5419
|
+
configs {
|
|
5420
|
+
key
|
|
5421
|
+
value
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
`) as unknown as TypedDocumentString<GetProtocolConfigQuery, GetProtocolConfigQueryVariables>;
|
|
5405
5427
|
export const GetReferenceGasPriceDocument = new TypedDocumentString(`
|
|
5406
5428
|
query getReferenceGasPrice {
|
|
5407
5429
|
epoch {
|
package/src/grpc/core.ts
CHANGED
|
@@ -492,6 +492,33 @@ export class GrpcCoreClient extends CoreClient {
|
|
|
492
492
|
};
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
+
async getProtocolConfig(): Promise<SuiClientTypes.GetProtocolConfigResponse> {
|
|
496
|
+
const response = await this.#client.ledgerService.getEpoch({
|
|
497
|
+
readMask: { paths: ['protocol_config'] },
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
const protocolConfig = response.response.epoch?.protocolConfig;
|
|
501
|
+
if (!protocolConfig) {
|
|
502
|
+
throw new Error('Protocol config not found in response');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const featureFlags: Record<string, boolean> = { ...protocolConfig.featureFlags };
|
|
506
|
+
const attributes: Record<string, string | null> = {};
|
|
507
|
+
if (protocolConfig.attributes) {
|
|
508
|
+
for (const [key, value] of Object.entries(protocolConfig.attributes)) {
|
|
509
|
+
attributes[key] = value ?? null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
protocolConfig: {
|
|
515
|
+
protocolVersion: protocolConfig.protocolVersion?.toString() ?? (null as never),
|
|
516
|
+
featureFlags,
|
|
517
|
+
attributes,
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
495
522
|
async getCurrentSystemState(): Promise<SuiClientTypes.GetCurrentSystemStateResponse> {
|
|
496
523
|
const response = await this.#client.ledgerService.getEpoch({
|
|
497
524
|
readMask: {
|
package/src/jsonRpc/core.ts
CHANGED
|
@@ -457,6 +457,40 @@ export class JSONRpcCoreClient extends CoreClient {
|
|
|
457
457
|
};
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
async getProtocolConfig(
|
|
461
|
+
options?: SuiClientTypes.GetProtocolConfigOptions,
|
|
462
|
+
): Promise<SuiClientTypes.GetProtocolConfigResponse> {
|
|
463
|
+
const result = await this.#jsonRpcClient.getProtocolConfig({ signal: options?.signal });
|
|
464
|
+
|
|
465
|
+
const attributes: Record<string, string | null> = {};
|
|
466
|
+
for (const [key, value] of Object.entries(result.attributes)) {
|
|
467
|
+
if (value === null) {
|
|
468
|
+
attributes[key] = null;
|
|
469
|
+
} else if ('u16' in value) {
|
|
470
|
+
attributes[key] = value.u16;
|
|
471
|
+
} else if ('u32' in value) {
|
|
472
|
+
attributes[key] = value.u32;
|
|
473
|
+
} else if ('u64' in value) {
|
|
474
|
+
attributes[key] = value.u64;
|
|
475
|
+
} else if ('f64' in value) {
|
|
476
|
+
attributes[key] = value.f64;
|
|
477
|
+
} else if ('bool' in value) {
|
|
478
|
+
attributes[key] = value.bool;
|
|
479
|
+
} else {
|
|
480
|
+
const entries = Object.entries(value);
|
|
481
|
+
attributes[key] = entries.length === 1 ? String(entries[0][1]) : JSON.stringify(value);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
protocolConfig: {
|
|
487
|
+
protocolVersion: result.protocolVersion,
|
|
488
|
+
featureFlags: { ...result.featureFlags },
|
|
489
|
+
attributes,
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
460
494
|
async getCurrentSystemState(
|
|
461
495
|
options?: SuiClientTypes.GetCurrentSystemStateOptions,
|
|
462
496
|
): Promise<SuiClientTypes.GetCurrentSystemStateResponse> {
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
import { PACKAGE_VERSION, TARGETED_RPC_VERSION } from '../version.js';
|
|
5
5
|
import { JsonRpcError, SuiHTTPStatusError } from './errors.js';
|
|
6
|
-
import type { WebsocketClientOptions } from './rpc-websocket-client.js';
|
|
7
|
-
import { WebsocketClient } from './rpc-websocket-client.js';
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* An object defining headers to be passed to the RPC server
|
|
@@ -13,15 +11,11 @@ export type HttpHeaders = { [header: string]: string };
|
|
|
13
11
|
|
|
14
12
|
export interface JsonRpcHTTPTransportOptions {
|
|
15
13
|
fetch?: typeof fetch;
|
|
16
|
-
WebSocketConstructor?: typeof WebSocket;
|
|
17
14
|
url: string;
|
|
18
15
|
rpc?: {
|
|
19
16
|
headers?: HttpHeaders;
|
|
20
17
|
url?: string;
|
|
21
18
|
};
|
|
22
|
-
websocket?: WebsocketClientOptions & {
|
|
23
|
-
url?: string;
|
|
24
|
-
};
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
export interface JsonRpcTransportRequestOptions {
|
|
@@ -30,25 +24,13 @@ export interface JsonRpcTransportRequestOptions {
|
|
|
30
24
|
signal?: AbortSignal;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
|
-
export interface JsonRpcTransportSubscribeOptions<T> {
|
|
34
|
-
method: string;
|
|
35
|
-
unsubscribe: string;
|
|
36
|
-
params: unknown[];
|
|
37
|
-
onMessage: (event: T) => void;
|
|
38
|
-
signal?: AbortSignal;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
27
|
export interface JsonRpcTransport {
|
|
42
28
|
request<T = unknown>(input: JsonRpcTransportRequestOptions): Promise<T>;
|
|
43
|
-
subscribe<T = unknown>(
|
|
44
|
-
input: JsonRpcTransportSubscribeOptions<T>,
|
|
45
|
-
): Promise<() => Promise<boolean>>;
|
|
46
29
|
}
|
|
47
30
|
|
|
48
31
|
export class JsonRpcHTTPTransport implements JsonRpcTransport {
|
|
49
32
|
#requestId = 0;
|
|
50
33
|
#options: JsonRpcHTTPTransportOptions;
|
|
51
|
-
#websocketClient?: WebsocketClient;
|
|
52
34
|
|
|
53
35
|
constructor(options: JsonRpcHTTPTransportOptions) {
|
|
54
36
|
this.#options = options;
|
|
@@ -66,27 +48,6 @@ export class JsonRpcHTTPTransport implements JsonRpcTransport {
|
|
|
66
48
|
return fetchFn(input, init);
|
|
67
49
|
}
|
|
68
50
|
|
|
69
|
-
#getWebsocketClient(): WebsocketClient {
|
|
70
|
-
if (!this.#websocketClient) {
|
|
71
|
-
const WebSocketConstructor = this.#options.WebSocketConstructor ?? WebSocket;
|
|
72
|
-
if (!WebSocketConstructor) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
'The current environment does not support WebSocket, you can provide a WebSocketConstructor in the options for SuiHTTPTransport.',
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.#websocketClient = new WebsocketClient(
|
|
79
|
-
this.#options.websocket?.url ?? this.#options.url,
|
|
80
|
-
{
|
|
81
|
-
WebSocketConstructor,
|
|
82
|
-
...this.#options.websocket,
|
|
83
|
-
},
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return this.#websocketClient;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
51
|
async request<T>(input: JsonRpcTransportRequestOptions): Promise<T> {
|
|
91
52
|
this.#requestId += 1;
|
|
92
53
|
|
|
@@ -125,17 +86,4 @@ export class JsonRpcHTTPTransport implements JsonRpcTransport {
|
|
|
125
86
|
|
|
126
87
|
return data.result;
|
|
127
88
|
}
|
|
128
|
-
|
|
129
|
-
async subscribe<T>(input: JsonRpcTransportSubscribeOptions<T>): Promise<() => Promise<boolean>> {
|
|
130
|
-
const unsubscribe = await this.#getWebsocketClient().subscribe(input);
|
|
131
|
-
|
|
132
|
-
if (input.signal) {
|
|
133
|
-
input.signal.throwIfAborted();
|
|
134
|
-
input.signal.addEventListener('abort', () => {
|
|
135
|
-
unsubscribe();
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return async () => !!(await unsubscribe());
|
|
140
|
-
}
|
|
141
89
|
}
|
package/src/jsonRpc/index.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
//#region src/jsonRpc/rpc-websocket-client.d.ts
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Configuration options for the websocket connection
|
|
5
|
-
*/
|
|
6
|
-
type WebsocketClientOptions = {
|
|
7
|
-
/**
|
|
8
|
-
* Custom WebSocket class to use. Defaults to the global WebSocket class, if available.
|
|
9
|
-
*/
|
|
10
|
-
WebSocketConstructor?: typeof WebSocket;
|
|
11
|
-
/**
|
|
12
|
-
* Milliseconds before timing out while calling an RPC method
|
|
13
|
-
*/
|
|
14
|
-
callTimeout?: number;
|
|
15
|
-
/**
|
|
16
|
-
* Milliseconds between attempts to connect
|
|
17
|
-
*/
|
|
18
|
-
reconnectTimeout?: number;
|
|
19
|
-
/**
|
|
20
|
-
* Maximum number of times to try connecting before giving up
|
|
21
|
-
*/
|
|
22
|
-
maxReconnects?: number;
|
|
23
|
-
};
|
|
24
|
-
//#endregion
|
|
25
|
-
export { WebsocketClientOptions };
|
|
26
|
-
//# sourceMappingURL=rpc-websocket-client.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-websocket-client.d.mts","names":[],"sources":["../../src/jsonRpc/rpc-websocket-client.ts"],"sourcesContent":[],"mappings":";;;;;KA8CY,sBAAA;;;;gCAImB"}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { JsonRpcError } from "./errors.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/jsonRpc/rpc-websocket-client.ts
|
|
4
|
-
function getWebsocketUrl(httpUrl) {
|
|
5
|
-
const url = new URL(httpUrl);
|
|
6
|
-
url.protocol = url.protocol.replace("http", "ws");
|
|
7
|
-
return url.toString();
|
|
8
|
-
}
|
|
9
|
-
const DEFAULT_CLIENT_OPTIONS = {
|
|
10
|
-
WebSocketConstructor: typeof WebSocket !== "undefined" ? WebSocket : void 0,
|
|
11
|
-
callTimeout: 3e4,
|
|
12
|
-
reconnectTimeout: 3e3,
|
|
13
|
-
maxReconnects: 5
|
|
14
|
-
};
|
|
15
|
-
var WebsocketClient = class {
|
|
16
|
-
#requestId = 0;
|
|
17
|
-
#disconnects = 0;
|
|
18
|
-
#webSocket = null;
|
|
19
|
-
#connectionPromise = null;
|
|
20
|
-
#subscriptions = /* @__PURE__ */ new Set();
|
|
21
|
-
#pendingRequests = /* @__PURE__ */ new Map();
|
|
22
|
-
constructor(endpoint, options = {}) {
|
|
23
|
-
this.endpoint = endpoint;
|
|
24
|
-
this.options = {
|
|
25
|
-
...DEFAULT_CLIENT_OPTIONS,
|
|
26
|
-
...options
|
|
27
|
-
};
|
|
28
|
-
if (!this.options.WebSocketConstructor) throw new Error("Missing WebSocket constructor");
|
|
29
|
-
if (this.endpoint.startsWith("http")) this.endpoint = getWebsocketUrl(this.endpoint);
|
|
30
|
-
}
|
|
31
|
-
async makeRequest(method, params, signal) {
|
|
32
|
-
const webSocket = await this.#setupWebSocket();
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
this.#requestId += 1;
|
|
35
|
-
this.#pendingRequests.set(this.#requestId, {
|
|
36
|
-
resolve,
|
|
37
|
-
reject,
|
|
38
|
-
timeout: setTimeout(() => {
|
|
39
|
-
this.#pendingRequests.delete(this.#requestId);
|
|
40
|
-
reject(/* @__PURE__ */ new Error(`Request timeout: ${method}`));
|
|
41
|
-
}, this.options.callTimeout)
|
|
42
|
-
});
|
|
43
|
-
signal?.addEventListener("abort", () => {
|
|
44
|
-
this.#pendingRequests.delete(this.#requestId);
|
|
45
|
-
reject(signal.reason);
|
|
46
|
-
});
|
|
47
|
-
webSocket.send(JSON.stringify({
|
|
48
|
-
jsonrpc: "2.0",
|
|
49
|
-
id: this.#requestId,
|
|
50
|
-
method,
|
|
51
|
-
params
|
|
52
|
-
}));
|
|
53
|
-
}).then(({ error, result }) => {
|
|
54
|
-
if (error) throw new JsonRpcError(error.message, error.code);
|
|
55
|
-
return result;
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
#setupWebSocket() {
|
|
59
|
-
if (this.#connectionPromise) return this.#connectionPromise;
|
|
60
|
-
this.#connectionPromise = new Promise((resolve) => {
|
|
61
|
-
this.#webSocket?.close();
|
|
62
|
-
this.#webSocket = new this.options.WebSocketConstructor(this.endpoint);
|
|
63
|
-
this.#webSocket.addEventListener("open", () => {
|
|
64
|
-
this.#disconnects = 0;
|
|
65
|
-
resolve(this.#webSocket);
|
|
66
|
-
});
|
|
67
|
-
this.#webSocket.addEventListener("close", () => {
|
|
68
|
-
this.#disconnects++;
|
|
69
|
-
if (this.#disconnects <= this.options.maxReconnects) setTimeout(() => {
|
|
70
|
-
this.#reconnect();
|
|
71
|
-
}, this.options.reconnectTimeout);
|
|
72
|
-
});
|
|
73
|
-
this.#webSocket.addEventListener("message", ({ data }) => {
|
|
74
|
-
let json;
|
|
75
|
-
try {
|
|
76
|
-
json = JSON.parse(data);
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error(new Error(`Failed to parse RPC message: ${data}`, { cause: error }));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if ("id" in json && json.id != null && this.#pendingRequests.has(json.id)) {
|
|
82
|
-
const { resolve: resolve$1, timeout } = this.#pendingRequests.get(json.id);
|
|
83
|
-
clearTimeout(timeout);
|
|
84
|
-
resolve$1(json);
|
|
85
|
-
} else if ("params" in json) {
|
|
86
|
-
const { params } = json;
|
|
87
|
-
this.#subscriptions.forEach((subscription) => {
|
|
88
|
-
if (subscription.subscriptionId === params.subscription) {
|
|
89
|
-
if (params.subscription === subscription.subscriptionId) subscription.onMessage(params.result);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
return this.#connectionPromise;
|
|
96
|
-
}
|
|
97
|
-
async #reconnect() {
|
|
98
|
-
this.#webSocket?.close();
|
|
99
|
-
this.#connectionPromise = null;
|
|
100
|
-
return Promise.allSettled([...this.#subscriptions].map((subscription) => subscription.subscribe(this)));
|
|
101
|
-
}
|
|
102
|
-
async subscribe(input) {
|
|
103
|
-
const subscription = new RpcSubscription(input);
|
|
104
|
-
this.#subscriptions.add(subscription);
|
|
105
|
-
await subscription.subscribe(this);
|
|
106
|
-
return () => subscription.unsubscribe(this);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
var RpcSubscription = class {
|
|
110
|
-
constructor(input) {
|
|
111
|
-
this.subscriptionId = null;
|
|
112
|
-
this.subscribed = false;
|
|
113
|
-
this.input = input;
|
|
114
|
-
}
|
|
115
|
-
onMessage(message) {
|
|
116
|
-
if (this.subscribed) this.input.onMessage(message);
|
|
117
|
-
}
|
|
118
|
-
async unsubscribe(client) {
|
|
119
|
-
const { subscriptionId } = this;
|
|
120
|
-
this.subscribed = false;
|
|
121
|
-
if (subscriptionId == null) return false;
|
|
122
|
-
this.subscriptionId = null;
|
|
123
|
-
return client.makeRequest(this.input.unsubscribe, [subscriptionId]);
|
|
124
|
-
}
|
|
125
|
-
async subscribe(client) {
|
|
126
|
-
this.subscriptionId = null;
|
|
127
|
-
this.subscribed = true;
|
|
128
|
-
const newSubscriptionId = await client.makeRequest(this.input.method, this.input.params, this.input.signal);
|
|
129
|
-
if (this.subscribed) this.subscriptionId = newSubscriptionId;
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
//#endregion
|
|
134
|
-
export { WebsocketClient };
|
|
135
|
-
//# sourceMappingURL=rpc-websocket-client.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-websocket-client.mjs","names":["#setupWebSocket","#requestId","#pendingRequests","#connectionPromise","#webSocket","#disconnects","#reconnect","#subscriptions"],"sources":["../../src/jsonRpc/rpc-websocket-client.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { JsonRpcError } from './errors.js';\n\nfunction getWebsocketUrl(httpUrl: string): string {\n\tconst url = new URL(httpUrl);\n\turl.protocol = url.protocol.replace('http', 'ws');\n\treturn url.toString();\n}\n\ntype JsonRpcMessage =\n\t| {\n\t\t\tid: number;\n\t\t\tresult: never;\n\t\t\terror: {\n\t\t\t\tcode: number;\n\t\t\t\tmessage: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\tid: number;\n\t\t\tresult: unknown;\n\t\t\terror: never;\n\t }\n\t| {\n\t\t\tmethod: string;\n\t\t\tparams: NotificationMessageParams;\n\t };\n\ntype NotificationMessageParams = {\n\tsubscription?: number;\n\tresult: object;\n};\n\ntype SubscriptionRequest<T = any> = {\n\tmethod: string;\n\tunsubscribe: string;\n\tparams: any[];\n\tonMessage: (event: T) => void;\n\tsignal?: AbortSignal;\n};\n\n/**\n * Configuration options for the websocket connection\n */\nexport type WebsocketClientOptions = {\n\t/**\n\t * Custom WebSocket class to use. Defaults to the global WebSocket class, if available.\n\t */\n\tWebSocketConstructor?: typeof WebSocket;\n\t/**\n\t * Milliseconds before timing out while calling an RPC method\n\t */\n\tcallTimeout?: number;\n\t/**\n\t * Milliseconds between attempts to connect\n\t */\n\treconnectTimeout?: number;\n\t/**\n\t * Maximum number of times to try connecting before giving up\n\t */\n\tmaxReconnects?: number;\n};\n\nexport const DEFAULT_CLIENT_OPTIONS = {\n\t// We fudge the typing because we also check for undefined in the constructor:\n\tWebSocketConstructor: (typeof WebSocket !== 'undefined'\n\t\t? WebSocket\n\t\t: undefined) as typeof WebSocket,\n\tcallTimeout: 30000,\n\treconnectTimeout: 3000,\n\tmaxReconnects: 5,\n} satisfies WebsocketClientOptions;\n\nexport class WebsocketClient {\n\tendpoint: string;\n\toptions: Required<WebsocketClientOptions>;\n\t#requestId = 0;\n\t#disconnects = 0;\n\t#webSocket: WebSocket | null = null;\n\t#connectionPromise: Promise<WebSocket> | null = null;\n\t#subscriptions = new Set<RpcSubscription>();\n\t#pendingRequests = new Map<\n\t\tnumber,\n\t\t{\n\t\t\tresolve: (result: Extract<JsonRpcMessage, { id: number }>) => void;\n\t\t\treject: (reason: unknown) => void;\n\t\t\ttimeout: ReturnType<typeof setTimeout>;\n\t\t}\n\t>();\n\n\tconstructor(endpoint: string, options: WebsocketClientOptions = {}) {\n\t\tthis.endpoint = endpoint;\n\t\tthis.options = { ...DEFAULT_CLIENT_OPTIONS, ...options };\n\n\t\tif (!this.options.WebSocketConstructor) {\n\t\t\tthrow new Error('Missing WebSocket constructor');\n\t\t}\n\n\t\tif (this.endpoint.startsWith('http')) {\n\t\t\tthis.endpoint = getWebsocketUrl(this.endpoint);\n\t\t}\n\t}\n\n\tasync makeRequest<T>(method: string, params: any[], signal?: AbortSignal): Promise<T> {\n\t\tconst webSocket = await this.#setupWebSocket();\n\n\t\treturn new Promise<Extract<JsonRpcMessage, { id: number }>>((resolve, reject) => {\n\t\t\tthis.#requestId += 1;\n\t\t\tthis.#pendingRequests.set(this.#requestId, {\n\t\t\t\tresolve: resolve,\n\t\t\t\treject,\n\t\t\t\ttimeout: setTimeout(() => {\n\t\t\t\t\tthis.#pendingRequests.delete(this.#requestId);\n\t\t\t\t\treject(new Error(`Request timeout: ${method}`));\n\t\t\t\t}, this.options.callTimeout),\n\t\t\t});\n\n\t\t\tsignal?.addEventListener('abort', () => {\n\t\t\t\tthis.#pendingRequests.delete(this.#requestId);\n\t\t\t\treject(signal.reason);\n\t\t\t});\n\n\t\t\twebSocket.send(JSON.stringify({ jsonrpc: '2.0', id: this.#requestId, method, params }));\n\t\t}).then(({ error, result }) => {\n\t\t\tif (error) {\n\t\t\t\tthrow new JsonRpcError(error.message, error.code);\n\t\t\t}\n\n\t\t\treturn result as T;\n\t\t});\n\t}\n\n\t#setupWebSocket() {\n\t\tif (this.#connectionPromise) {\n\t\t\treturn this.#connectionPromise;\n\t\t}\n\n\t\tthis.#connectionPromise = new Promise<WebSocket>((resolve) => {\n\t\t\tthis.#webSocket?.close();\n\t\t\tthis.#webSocket = new this.options.WebSocketConstructor(this.endpoint);\n\n\t\t\tthis.#webSocket.addEventListener('open', () => {\n\t\t\t\tthis.#disconnects = 0;\n\t\t\t\tresolve(this.#webSocket!);\n\t\t\t});\n\n\t\t\tthis.#webSocket.addEventListener('close', () => {\n\t\t\t\tthis.#disconnects++;\n\t\t\t\tif (this.#disconnects <= this.options.maxReconnects) {\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tthis.#reconnect();\n\t\t\t\t\t}, this.options.reconnectTimeout);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis.#webSocket.addEventListener('message', ({ data }: { data: string }) => {\n\t\t\t\tlet json: JsonRpcMessage;\n\t\t\t\ttry {\n\t\t\t\t\tjson = JSON.parse(data) as JsonRpcMessage;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(new Error(`Failed to parse RPC message: ${data}`, { cause: error }));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif ('id' in json && json.id != null && this.#pendingRequests.has(json.id)) {\n\t\t\t\t\tconst { resolve, timeout } = this.#pendingRequests.get(json.id)!;\n\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tresolve(json);\n\t\t\t\t} else if ('params' in json) {\n\t\t\t\t\tconst { params } = json;\n\t\t\t\t\tthis.#subscriptions.forEach((subscription) => {\n\t\t\t\t\t\tif (subscription.subscriptionId === params.subscription)\n\t\t\t\t\t\t\tif (params.subscription === subscription.subscriptionId) {\n\t\t\t\t\t\t\t\tsubscription.onMessage(params.result);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn this.#connectionPromise;\n\t}\n\n\tasync #reconnect() {\n\t\tthis.#webSocket?.close();\n\t\tthis.#connectionPromise = null;\n\n\t\treturn Promise.allSettled(\n\t\t\t[...this.#subscriptions].map((subscription) => subscription.subscribe(this)),\n\t\t);\n\t}\n\n\tasync subscribe<T>(input: SubscriptionRequest<T>) {\n\t\tconst subscription = new RpcSubscription(input);\n\t\tthis.#subscriptions.add(subscription);\n\t\tawait subscription.subscribe(this);\n\t\treturn () => subscription.unsubscribe(this);\n\t}\n}\n\nclass RpcSubscription {\n\tsubscriptionId: number | null = null;\n\tinput: SubscriptionRequest<any>;\n\tsubscribed = false;\n\n\tconstructor(input: SubscriptionRequest) {\n\t\tthis.input = input;\n\t}\n\n\tonMessage(message: unknown) {\n\t\tif (this.subscribed) {\n\t\t\tthis.input.onMessage(message);\n\t\t}\n\t}\n\n\tasync unsubscribe(client: WebsocketClient) {\n\t\tconst { subscriptionId } = this;\n\t\tthis.subscribed = false;\n\t\tif (subscriptionId == null) return false;\n\t\tthis.subscriptionId = null;\n\n\t\treturn client.makeRequest(this.input.unsubscribe, [subscriptionId]);\n\t}\n\n\tasync subscribe(client: WebsocketClient) {\n\t\tthis.subscriptionId = null;\n\t\tthis.subscribed = true;\n\t\tconst newSubscriptionId = await client.makeRequest<number>(\n\t\t\tthis.input.method,\n\t\t\tthis.input.params,\n\t\t\tthis.input.signal,\n\t\t);\n\n\t\tif (this.subscribed) {\n\t\t\tthis.subscriptionId = newSubscriptionId;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|