@ckb-ccc/core 2.0.0-next.0 → 2.0.0-next.1
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 +133 -1
- package/README.md +3 -3
- package/dist/advanced.d.mts +2 -2
- package/dist/advanced.mjs +1 -1
- package/{dist.commonjs/advancedBarrel-DNdhwiU0.d.ts → dist/advancedBarrel-DfFMFhdl.d.mts} +2790 -2237
- package/dist/advancedBarrel-DfFMFhdl.d.mts.map +1 -0
- package/dist/advancedBarrel.d.mts +2 -2
- package/dist/advancedBarrel.mjs +1 -1
- package/dist/advancedBarrel.mjs.map +1 -1
- package/dist/barrel-ONYEMVg5.mjs +7 -0
- package/dist/barrel-ONYEMVg5.mjs.map +1 -0
- package/dist/barrel.d.mts +2 -2
- package/dist/barrel.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/transformers-CduZ1Js5.mjs +2 -0
- package/dist/transformers-CduZ1Js5.mjs.map +1 -0
- package/dist.commonjs/advanced.d.ts +2 -2
- package/dist.commonjs/advanced.js +1 -1
- package/{dist/advancedBarrel-BHwnMJqR.d.mts → dist.commonjs/advancedBarrel-Dl0mM9JO.d.ts} +2791 -2236
- package/dist.commonjs/advancedBarrel-Dl0mM9JO.d.ts.map +1 -0
- package/dist.commonjs/advancedBarrel.d.ts +2 -2
- package/dist.commonjs/advancedBarrel.js +1 -1
- package/dist.commonjs/advancedBarrel.js.map +1 -1
- package/dist.commonjs/barrel-HfgYMxP_.js +23 -0
- package/dist.commonjs/barrel-HfgYMxP_.js.map +1 -0
- package/dist.commonjs/barrel.d.ts +2 -2
- package/dist.commonjs/barrel.js +1 -1
- package/dist.commonjs/index.d.ts +2 -2
- package/dist.commonjs/index.js +1 -1
- package/dist.commonjs/{rolldown-runtime-DWNI8pZO.js → rolldown-runtime-Chgba0Kb.js} +1 -1
- package/dist.commonjs/transformers-BUlLwiyt.js +2 -0
- package/dist.commonjs/transformers-BUlLwiyt.js.map +1 -0
- package/package.json +8 -7
- package/src/advancedBarrel.ts +1 -0
- package/src/bytes/advanced.ts +1 -6
- package/src/bytes/index.ts +37 -10
- package/src/ckb/transaction.ts +7 -5
- package/src/client/client.ts +83 -21
- package/src/client/clientPublicMainnet.advanced.ts +9 -0
- package/src/client/clientPublicMainnet.ts +75 -36
- package/src/client/clientPublicTestnet.advanced.ts +9 -0
- package/src/client/clientPublicTestnet.ts +75 -36
- package/src/client/jsonRpc/client.ts +77 -30
- package/src/client/jsonRpc/transformers.ts +4 -4
- package/src/jsonRpc/index.ts +1 -0
- package/src/jsonRpc/requestor.ts +106 -29
- package/src/jsonRpc/transports/advanced.ts +0 -4
- package/src/jsonRpc/transports/factory.ts +12 -5
- package/src/jsonRpc/transports/fallback.ts +14 -6
- package/src/jsonRpc/transports/http.ts +19 -12
- package/src/jsonRpc/transports/index.ts +4 -0
- package/src/jsonRpc/transports/transport.ts +42 -6
- package/src/jsonRpc/transports/webSocket.ts +145 -40
- package/src/keystore/index.ts +3 -3
- package/src/molecule/codec.ts +20 -17
- package/src/signer/advanced.ts +1 -0
- package/src/signer/btc/psbt.ts +4 -2
- package/src/signer/btc/verify.ts +2 -2
- package/src/signer/ckb/secp256k1Signing.ts +3 -3
- package/src/signer/doge/signerDoge.ts +1 -1
- package/src/signer/doge/signerDogePrivateKey.ts +1 -1
- package/src/signer/index.ts +1 -0
- package/src/signer/jsonRpc/handler.ts +478 -0
- package/src/signer/jsonRpc/index.ts +2 -0
- package/src/signer/jsonRpc/network.ts +15 -0
- package/src/signer/jsonRpc/signer.ts +378 -0
- package/src/signer/jsonRpc/transformers.ts +71 -0
- package/src/signer/signer/index.ts +5 -13
- package/src/utils/abortSignal.ts +56 -0
- package/src/utils/constructor.ts +11 -0
- package/src/utils/index.ts +172 -5
- package/src/utils/owner/aggregated.ts +56 -0
- package/src/utils/owner/index.ts +4 -0
- package/src/utils/owner/owner.ts +141 -0
- package/src/utils/owner/refCount.ts +78 -0
- package/src/utils/owner/unique.ts +30 -0
- package/src/utils/proxy.ts +165 -0
- package/tsdown.config.mts +1 -0
- package/dist/address.advanced-LxntWbev.mjs +0 -2
- package/dist/address.advanced-LxntWbev.mjs.map +0 -1
- package/dist/advancedBarrel-BHwnMJqR.d.mts.map +0 -1
- package/dist/barrel-C-BaV9Pe.mjs +0 -7
- package/dist/barrel-C-BaV9Pe.mjs.map +0 -1
- package/dist.commonjs/address.advanced-BqcYOpZn.js +0 -2
- package/dist.commonjs/address.advanced-BqcYOpZn.js.map +0 -1
- package/dist.commonjs/advancedBarrel-DNdhwiU0.d.ts.map +0 -1
- package/dist.commonjs/barrel-BBOmj4RJ.js +0 -21
- package/dist.commonjs/barrel-BBOmj4RJ.js.map +0 -1
|
@@ -1,55 +1,94 @@
|
|
|
1
1
|
import WebSocket from "isomorphic-ws";
|
|
2
|
+
import { RequestorJsonRpc } from "../jsonRpc/requestor.js";
|
|
3
|
+
import type { ClientConfig } from "./client.js";
|
|
2
4
|
import { TESTNET_SCRIPTS } from "./clientPublicTestnet.advanced.js";
|
|
3
|
-
import {
|
|
4
|
-
import { ClientJsonRpc, ClientJsonRpcConfig } from "./jsonRpc/index.js";
|
|
5
|
-
import { KnownScript } from "./knownScript.js";
|
|
5
|
+
import { ClientJsonRpc, type ClientJsonRpcConfig } from "./jsonRpc/client.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
10
|
export class ClientPublicTestnet extends ClientJsonRpc {
|
|
11
|
+
private static defaultUrls(): readonly [string, ...string[]] {
|
|
12
|
+
return typeof WebSocket !== "undefined"
|
|
13
|
+
? [
|
|
14
|
+
"wss://testnet.ckb.dev/ws",
|
|
15
|
+
"https://testnet.ckb.dev/",
|
|
16
|
+
"https://testnet.ckbapp.dev/",
|
|
17
|
+
]
|
|
18
|
+
: ["https://testnet.ckb.dev/", "https://testnet.ckbapp.dev/"];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private static resolveConfig(
|
|
22
|
+
config?: ClientJsonRpcConfig & {
|
|
23
|
+
/** @deprecated URL belongs to Transport construction. */
|
|
24
|
+
url?: string;
|
|
25
|
+
},
|
|
26
|
+
): ClientJsonRpcConfig & { url: string; fallbacks: string[] } {
|
|
27
|
+
const defaultUrls = this.defaultUrls();
|
|
28
|
+
return {
|
|
29
|
+
...config,
|
|
30
|
+
url: config?.url ?? defaultUrls[0],
|
|
31
|
+
fallbacks: config?.fallbacks ?? [...defaultUrls],
|
|
32
|
+
scripts: config?.scripts ?? TESTNET_SCRIPTS,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated Use {@link ClientPublicTestnet.new} with a borrowed Transport or
|
|
38
|
+
* {@link ClientPublicTestnet.open} when creating Transports.
|
|
39
|
+
*/
|
|
11
40
|
constructor(
|
|
12
|
-
|
|
41
|
+
config?: ClientJsonRpcConfig & {
|
|
42
|
+
/** @deprecated URL belongs to Transport construction. */
|
|
13
43
|
url?: string;
|
|
14
|
-
scripts?: Record<KnownScript, ScriptInfoLike | undefined>;
|
|
15
44
|
},
|
|
16
45
|
) {
|
|
17
|
-
const
|
|
18
|
-
super(
|
|
19
|
-
config?.url ??
|
|
20
|
-
(hasWebSocket
|
|
21
|
-
? "wss://testnet.ckb.dev/ws"
|
|
22
|
-
: "https://testnet.ckb.dev/"),
|
|
23
|
-
{
|
|
24
|
-
...config,
|
|
25
|
-
fallbacks:
|
|
26
|
-
config?.fallbacks ??
|
|
27
|
-
(hasWebSocket
|
|
28
|
-
? [
|
|
29
|
-
"wss://testnet.ckb.dev/ws",
|
|
30
|
-
"https://testnet.ckb.dev/",
|
|
31
|
-
"https://testnet.ckbapp.dev/",
|
|
32
|
-
]
|
|
33
|
-
: ["https://testnet.ckb.dev/", "https://testnet.ckbapp.dev/"]),
|
|
34
|
-
},
|
|
35
|
-
);
|
|
46
|
+
const resolved = ClientPublicTestnet.resolveConfig(config);
|
|
47
|
+
super(resolved.url, resolved);
|
|
36
48
|
}
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
/** Creates a Client that borrows an existing Transport. */
|
|
51
|
+
static new(
|
|
52
|
+
config: Omit<Parameters<typeof RequestorJsonRpc.new>[0], "onError"> &
|
|
53
|
+
ClientConfig,
|
|
54
|
+
): ClientPublicTestnet {
|
|
55
|
+
const { cache, scripts, ...requestorConfig } = config;
|
|
56
|
+
const requestor = this.newRequestor(requestorConfig);
|
|
57
|
+
return new ClientPublicTestnet({ cache, scripts, requestor });
|
|
40
58
|
}
|
|
41
59
|
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
/** Opens a Client with explicit ownership of its default dependencies. */
|
|
61
|
+
static open(
|
|
62
|
+
config?: Omit<
|
|
63
|
+
Parameters<typeof RequestorJsonRpc.open>[0],
|
|
64
|
+
"onError" | "urls"
|
|
65
|
+
> &
|
|
66
|
+
ClientConfig & {
|
|
67
|
+
urls?: Parameters<typeof RequestorJsonRpc.open>[0]["urls"];
|
|
68
|
+
},
|
|
69
|
+
) {
|
|
70
|
+
const {
|
|
71
|
+
cache,
|
|
72
|
+
scripts,
|
|
73
|
+
urls = this.defaultUrls(),
|
|
74
|
+
...requestorConfig
|
|
75
|
+
} = config ?? {};
|
|
76
|
+
return this.openRequestor({
|
|
77
|
+
...requestorConfig,
|
|
78
|
+
urls,
|
|
79
|
+
}).map(
|
|
80
|
+
(requestor) =>
|
|
81
|
+
new ClientPublicTestnet({
|
|
82
|
+
cache,
|
|
83
|
+
scripts,
|
|
84
|
+
requestor,
|
|
85
|
+
url: urls[0],
|
|
86
|
+
fallbacks: [...urls.slice(1)],
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
44
89
|
}
|
|
45
90
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!found) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
`No script information was found for ${script} on ${this.addressPrefix}`,
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
return ScriptInfo.from(found);
|
|
91
|
+
get addressPrefix(): string {
|
|
92
|
+
return "ckt";
|
|
54
93
|
}
|
|
55
94
|
}
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
RequestorJsonRpcConfig,
|
|
11
11
|
} from "../../jsonRpc/requestor.js";
|
|
12
12
|
import { Num, NumLike, numFrom, numToHex } from "../../num/index.js";
|
|
13
|
-
import { apply } from "../../utils/index.js";
|
|
14
|
-
import {
|
|
13
|
+
import { Owner, apply } from "../../utils/index.js";
|
|
14
|
+
import type { ClientConfig } from "../client.js";
|
|
15
15
|
import { Client } from "../client.js";
|
|
16
16
|
import {
|
|
17
17
|
ClientFindCellsResponse,
|
|
@@ -67,10 +67,35 @@ const ERROR_PARSERS: [
|
|
|
67
67
|
],
|
|
68
68
|
];
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
function handleJsonRpcError(errAny: unknown): never {
|
|
71
|
+
if (
|
|
72
|
+
typeof errAny !== "object" ||
|
|
73
|
+
errAny === null ||
|
|
74
|
+
!("data" in errAny) ||
|
|
75
|
+
typeof errAny.data !== "string"
|
|
76
|
+
) {
|
|
77
|
+
throw errAny;
|
|
78
|
+
}
|
|
79
|
+
const err = errAny as ErrorClientBaseLike;
|
|
80
|
+
|
|
81
|
+
for (const [regexp, builder] of ERROR_PARSERS) {
|
|
82
|
+
const match = err.data.match(regexp);
|
|
83
|
+
if (match) {
|
|
84
|
+
throw builder(err, match);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
throw new ErrorClientBase(err);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type ClientJsonRpcConfig = RequestorJsonRpcConfig &
|
|
92
|
+
ClientConfig & {
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated Requestor injection is supported only by legacy constructors.
|
|
95
|
+
* Use a borrowed Transport with `Client.new` or let `Client.open` create one.
|
|
96
|
+
*/
|
|
97
|
+
requestor?: RequestorJsonRpc;
|
|
98
|
+
};
|
|
74
99
|
|
|
75
100
|
/**
|
|
76
101
|
* An abstract class implementing JSON-RPC client functionality for a specific URL and timeout.
|
|
@@ -84,42 +109,64 @@ export abstract class ClientJsonRpc extends Client {
|
|
|
84
109
|
*
|
|
85
110
|
* @param url_ - The URL of the JSON-RPC server.
|
|
86
111
|
* @param timeout - The timeout for requests in milliseconds
|
|
112
|
+
* @deprecated Use the concrete Client's `new` or `open` method.
|
|
87
113
|
*/
|
|
88
114
|
|
|
89
|
-
constructor(
|
|
115
|
+
constructor(
|
|
116
|
+
private readonly url_: string,
|
|
117
|
+
config?: ClientJsonRpcConfig,
|
|
118
|
+
) {
|
|
90
119
|
super(config);
|
|
91
120
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
) {
|
|
101
|
-
throw errAny;
|
|
102
|
-
}
|
|
103
|
-
const err = errAny as ErrorClientBaseLike;
|
|
104
|
-
|
|
105
|
-
for (const [regexp, builder] of ERROR_PARSERS) {
|
|
106
|
-
const match = err.data.match(regexp);
|
|
107
|
-
if (match) {
|
|
108
|
-
throw builder(err, match);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
throw new ErrorClientBase(err);
|
|
121
|
+
const requestor = config?.requestor;
|
|
122
|
+
if (requestor) {
|
|
123
|
+
this.requestor = requestor;
|
|
124
|
+
} else if (config?.transport) {
|
|
125
|
+
this.requestor = RequestorJsonRpc.new({
|
|
126
|
+
transport: config.transport,
|
|
127
|
+
maxConcurrent: config.maxConcurrent,
|
|
128
|
+
onError: handleJsonRpcError,
|
|
113
129
|
});
|
|
130
|
+
} else {
|
|
131
|
+
// Legacy constructor intentionally discards ownership.
|
|
132
|
+
this.requestor = RequestorJsonRpc.open({
|
|
133
|
+
urls: [url_, ...(config?.fallbacks ?? [])],
|
|
134
|
+
timeout: config?.timeout,
|
|
135
|
+
maxConcurrent: config?.maxConcurrent,
|
|
136
|
+
onError: handleJsonRpcError,
|
|
137
|
+
}).value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Creates a Requestor that borrows an existing Transport. */
|
|
142
|
+
protected static newRequestor(
|
|
143
|
+
config: Omit<Parameters<typeof RequestorJsonRpc.new>[0], "onError">,
|
|
144
|
+
): RequestorJsonRpc {
|
|
145
|
+
return RequestorJsonRpc.new({
|
|
146
|
+
...config,
|
|
147
|
+
onError: handleJsonRpcError,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Opens a Requestor with ownership of its default Transport. */
|
|
152
|
+
protected static openRequestor(
|
|
153
|
+
config: Omit<Parameters<typeof RequestorJsonRpc.open>[0], "onError">,
|
|
154
|
+
): Owner<RequestorJsonRpc> {
|
|
155
|
+
return RequestorJsonRpc.open({
|
|
156
|
+
...config,
|
|
157
|
+
onError: handleJsonRpcError,
|
|
158
|
+
});
|
|
114
159
|
}
|
|
115
160
|
|
|
116
161
|
/**
|
|
117
|
-
* Returns the URL of the JSON-RPC server.
|
|
162
|
+
* Returns the legacy primary URL of the JSON-RPC server.
|
|
118
163
|
*
|
|
119
164
|
* @returns The URL of the JSON-RPC server.
|
|
165
|
+
* @deprecated A Client may use multiple endpoints or a Transport without a
|
|
166
|
+
* URL, so this value does not reliably identify its connection.
|
|
120
167
|
*/
|
|
121
168
|
get url(): string {
|
|
122
|
-
return this.
|
|
169
|
+
return this.url_;
|
|
123
170
|
}
|
|
124
171
|
|
|
125
172
|
/**
|
|
@@ -212,10 +212,10 @@ export class JsonRpcTransformers {
|
|
|
212
212
|
return {
|
|
213
213
|
compactTarget: numFrom(header.compact_target),
|
|
214
214
|
dao: {
|
|
215
|
-
c: numLeFromBytes(dao.
|
|
216
|
-
ar: numLeFromBytes(dao.
|
|
217
|
-
s: numLeFromBytes(dao.
|
|
218
|
-
u: numLeFromBytes(dao.
|
|
215
|
+
c: numLeFromBytes(dao.subarray(0, 8)),
|
|
216
|
+
ar: numLeFromBytes(dao.subarray(8, 16)),
|
|
217
|
+
s: numLeFromBytes(dao.subarray(16, 24)),
|
|
218
|
+
u: numLeFromBytes(dao.subarray(24, 32)),
|
|
219
219
|
},
|
|
220
220
|
epoch: Epoch.fromNum(header.epoch),
|
|
221
221
|
extraHash: header.extra_hash,
|
package/src/jsonRpc/index.ts
CHANGED
package/src/jsonRpc/requestor.ts
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
import { abortSignalToPromise } from "../utils/abortSignal.js";
|
|
2
|
+
import { OwnerAggregated } from "../utils/owner/aggregated.js";
|
|
3
|
+
import { Owner } from "../utils/owner/owner.js";
|
|
4
|
+
import { jsonRpcTransportFromUri } from "./transports/factory.js";
|
|
5
|
+
import { JsonRpcTransportFallback } from "./transports/fallback.js";
|
|
1
6
|
import {
|
|
7
|
+
JsonRpcError,
|
|
2
8
|
JsonRpcPayload,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "./transports/
|
|
9
|
+
JsonRpcResponse,
|
|
10
|
+
JsonRpcTransport,
|
|
11
|
+
JsonRpcTransportRequestOptions,
|
|
12
|
+
} from "./transports/index.js";
|
|
13
|
+
|
|
14
|
+
function openTransports(
|
|
15
|
+
urls: readonly [string, ...string[]],
|
|
16
|
+
config?: { timeout?: number },
|
|
17
|
+
): Owner<JsonRpcTransportFallback> {
|
|
18
|
+
const transportOwners = Array.from(new Set(urls), (url) =>
|
|
19
|
+
jsonRpcTransportFromUri(url, config),
|
|
20
|
+
);
|
|
21
|
+
return OwnerAggregated.from(transportOwners).map(
|
|
22
|
+
(transports) => new JsonRpcTransportFallback([...transports]),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
7
25
|
|
|
8
26
|
/**
|
|
9
27
|
* Applies a transformation function to a value if the transformer is provided.
|
|
@@ -25,11 +43,15 @@ function transform(value: unknown, transformer?: (i: unknown) => unknown) {
|
|
|
25
43
|
return value;
|
|
26
44
|
}
|
|
27
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated Used only by the legacy positional constructor. Use the static
|
|
48
|
+
* {@link RequestorJsonRpc.new} or {@link RequestorJsonRpc.open} methods.
|
|
49
|
+
*/
|
|
28
50
|
export type RequestorJsonRpcConfig = {
|
|
29
51
|
fallbacks?: string[];
|
|
30
52
|
timeout?: number;
|
|
31
53
|
maxConcurrent?: number;
|
|
32
|
-
transport?:
|
|
54
|
+
transport?: JsonRpcTransport;
|
|
33
55
|
};
|
|
34
56
|
|
|
35
57
|
export class RequestorJsonRpc {
|
|
@@ -37,15 +59,17 @@ export class RequestorJsonRpc {
|
|
|
37
59
|
private concurrent = 0;
|
|
38
60
|
private readonly pending: (() => void)[] = [];
|
|
39
61
|
|
|
40
|
-
public readonly transport:
|
|
62
|
+
public readonly transport: JsonRpcTransport;
|
|
41
63
|
|
|
42
64
|
private id = 0;
|
|
43
65
|
|
|
44
66
|
/**
|
|
45
|
-
* Creates
|
|
67
|
+
* Creates a Requestor using legacy positional arguments.
|
|
46
68
|
*
|
|
47
69
|
* @param url_ - The URL of the JSON-RPC server.
|
|
48
70
|
* @param timeout - The timeout for requests in milliseconds
|
|
71
|
+
* @deprecated Use {@link RequestorJsonRpc.new} with a borrowed Transport or
|
|
72
|
+
* {@link RequestorJsonRpc.open} when creating Transports.
|
|
49
73
|
*/
|
|
50
74
|
constructor(
|
|
51
75
|
private readonly url_: string,
|
|
@@ -53,20 +77,56 @@ export class RequestorJsonRpc {
|
|
|
53
77
|
private readonly onError?: (err: unknown) => Promise<void> | void,
|
|
54
78
|
) {
|
|
55
79
|
this.maxConcurrent = config?.maxConcurrent;
|
|
56
|
-
|
|
57
|
-
config
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
80
|
+
if (config?.transport) {
|
|
81
|
+
this.transport = config.transport;
|
|
82
|
+
} else {
|
|
83
|
+
this.transport = openTransports(
|
|
84
|
+
[url_, ...(config?.fallbacks ?? [])],
|
|
85
|
+
config,
|
|
86
|
+
).value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Creates a Requestor that borrows an existing Transport. */
|
|
91
|
+
static new({
|
|
92
|
+
transport,
|
|
93
|
+
maxConcurrent,
|
|
94
|
+
onError,
|
|
95
|
+
}: {
|
|
96
|
+
transport: JsonRpcTransport;
|
|
97
|
+
maxConcurrent?: number;
|
|
98
|
+
onError?: (err: unknown) => Promise<void> | void;
|
|
99
|
+
}): RequestorJsonRpc {
|
|
100
|
+
return new RequestorJsonRpc("", { transport, maxConcurrent }, onError);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Opens a Requestor with ownership of its default transports. */
|
|
104
|
+
static open({
|
|
105
|
+
urls: [url, ...fallbacks],
|
|
106
|
+
onError,
|
|
107
|
+
...config
|
|
108
|
+
}: {
|
|
109
|
+
urls: readonly [string, ...string[]];
|
|
110
|
+
timeout?: number;
|
|
111
|
+
maxConcurrent?: number;
|
|
112
|
+
onError?: (err: unknown) => Promise<void> | void;
|
|
113
|
+
}): Owner<RequestorJsonRpc> {
|
|
114
|
+
const transportOwner = openTransports([url, ...fallbacks], config);
|
|
115
|
+
return transportOwner.map((transport) =>
|
|
116
|
+
RequestorJsonRpc.new({
|
|
117
|
+
transport,
|
|
118
|
+
...config,
|
|
119
|
+
onError,
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
64
122
|
}
|
|
65
123
|
|
|
66
124
|
/**
|
|
67
125
|
* Returns the URL of the JSON-RPC server.
|
|
68
126
|
*
|
|
69
127
|
* @returns The URL of the JSON-RPC server.
|
|
128
|
+
* @deprecated URL belongs to Transport construction and is unavailable for
|
|
129
|
+
* Requestors created with {@link RequestorJsonRpc.new}.
|
|
70
130
|
*/
|
|
71
131
|
|
|
72
132
|
get url(): string {
|
|
@@ -89,6 +149,7 @@ export class RequestorJsonRpc {
|
|
|
89
149
|
inTransformers?: (((_: any) => unknown) | undefined)[],
|
|
90
150
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
151
|
outTransformer?: (_: any) => unknown,
|
|
152
|
+
options?: JsonRpcTransportRequestOptions,
|
|
92
153
|
): Promise<unknown> {
|
|
93
154
|
const payload = this.buildPayload(
|
|
94
155
|
rpcMethod,
|
|
@@ -107,7 +168,7 @@ export class RequestorJsonRpc {
|
|
|
107
168
|
|
|
108
169
|
try {
|
|
109
170
|
return await transform(
|
|
110
|
-
await this.requestPayload(payload),
|
|
171
|
+
await this.requestPayload(payload, options),
|
|
111
172
|
outTransformer,
|
|
112
173
|
);
|
|
113
174
|
} catch (err: unknown) {
|
|
@@ -118,36 +179,52 @@ export class RequestorJsonRpc {
|
|
|
118
179
|
}
|
|
119
180
|
}
|
|
120
181
|
|
|
121
|
-
async requestPayload(
|
|
182
|
+
async requestPayload(
|
|
183
|
+
payload: JsonRpcPayload,
|
|
184
|
+
options?: JsonRpcTransportRequestOptions,
|
|
185
|
+
): Promise<unknown> {
|
|
122
186
|
if (
|
|
123
187
|
this.maxConcurrent !== undefined &&
|
|
124
188
|
this.concurrent >= this.maxConcurrent
|
|
125
189
|
) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
|
|
190
|
+
options?.signal?.throwIfAborted();
|
|
191
|
+
let resolvePending: (() => void) | undefined;
|
|
192
|
+
const pending = new Promise<void>((resolve) => {
|
|
193
|
+
resolvePending = resolve;
|
|
194
|
+
this.pending.push(resolve);
|
|
195
|
+
});
|
|
196
|
+
try {
|
|
197
|
+
await (options?.signal
|
|
198
|
+
? Promise.race([pending, abortSignalToPromise(options.signal)])
|
|
199
|
+
: pending);
|
|
200
|
+
} catch (cause) {
|
|
201
|
+
const index = resolvePending
|
|
202
|
+
? this.pending.indexOf(resolvePending)
|
|
203
|
+
: -1;
|
|
204
|
+
if (index !== -1) {
|
|
205
|
+
this.pending.splice(index, 1);
|
|
206
|
+
} else {
|
|
207
|
+
this.pending.shift()?.();
|
|
208
|
+
}
|
|
209
|
+
throw cause;
|
|
210
|
+
}
|
|
130
211
|
}
|
|
131
212
|
|
|
132
|
-
const res =
|
|
213
|
+
const res: JsonRpcResponse = await (async () => {
|
|
133
214
|
this.concurrent += 1;
|
|
134
215
|
try {
|
|
135
|
-
return await this.transport.request(payload);
|
|
216
|
+
return await this.transport.request(payload, options);
|
|
136
217
|
} finally {
|
|
137
218
|
this.concurrent -= 1;
|
|
138
219
|
this.pending.shift()?.();
|
|
139
220
|
}
|
|
140
|
-
})()
|
|
141
|
-
id: number;
|
|
142
|
-
error: unknown;
|
|
143
|
-
result: unknown;
|
|
144
|
-
};
|
|
221
|
+
})();
|
|
145
222
|
|
|
146
223
|
if (res.id !== payload.id) {
|
|
147
224
|
throw new Error(`Id mismatched, got ${res.id}, expected ${payload.id}`);
|
|
148
225
|
}
|
|
149
226
|
if (res.error != null) {
|
|
150
|
-
throw res.error
|
|
227
|
+
throw new JsonRpcError(res.error);
|
|
151
228
|
}
|
|
152
229
|
return res.result;
|
|
153
230
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { OwnerUnique } from "../../utils/owner/unique.js";
|
|
2
|
+
import { JsonRpcTransportHttp } from "./http.js";
|
|
3
|
+
import { JsonRpcTransportWebSocket } from "./webSocket.js";
|
|
3
4
|
|
|
4
|
-
export function
|
|
5
|
+
export function jsonRpcTransportFromUri(
|
|
6
|
+
uri: string,
|
|
7
|
+
config?: { timeout?: number },
|
|
8
|
+
) {
|
|
5
9
|
if (uri.startsWith("wss://") || uri.startsWith("ws://")) {
|
|
6
|
-
return
|
|
10
|
+
return JsonRpcTransportWebSocket.open(uri, config?.timeout);
|
|
7
11
|
}
|
|
8
12
|
|
|
9
|
-
return new
|
|
13
|
+
return new OwnerUnique(
|
|
14
|
+
new JsonRpcTransportHttp(uri, config?.timeout),
|
|
15
|
+
() => {},
|
|
16
|
+
);
|
|
10
17
|
}
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
JsonRpcPayload,
|
|
3
|
+
JsonRpcResponse,
|
|
4
|
+
JsonRpcTransport,
|
|
5
|
+
JsonRpcTransportRequestOptions,
|
|
6
|
+
} from "./transport.js";
|
|
2
7
|
|
|
3
|
-
export class
|
|
8
|
+
export class JsonRpcTransportFallback implements JsonRpcTransport {
|
|
4
9
|
// Current transport index
|
|
5
10
|
private i = 0;
|
|
6
11
|
|
|
7
|
-
constructor(private readonly transports:
|
|
12
|
+
constructor(private readonly transports: JsonRpcTransport[]) {}
|
|
8
13
|
|
|
9
|
-
async request(
|
|
14
|
+
async request(
|
|
15
|
+
data: JsonRpcPayload,
|
|
16
|
+
options?: JsonRpcTransportRequestOptions,
|
|
17
|
+
): Promise<JsonRpcResponse> {
|
|
10
18
|
const startI = this.i;
|
|
11
19
|
let lastErr: unknown = new Error(
|
|
12
|
-
"
|
|
20
|
+
"JsonRpcTransportFallback requires at least one transport",
|
|
13
21
|
);
|
|
14
22
|
|
|
15
23
|
for (let tried = 0; tried < this.transports.length; tried += 1) {
|
|
16
24
|
const i = (startI + tried) % this.transports.length;
|
|
17
25
|
|
|
18
26
|
try {
|
|
19
|
-
const res = await this.transports[i].request(data);
|
|
27
|
+
const res = await this.transports[i].request(data, options);
|
|
20
28
|
|
|
21
29
|
this.i = i;
|
|
22
30
|
return res;
|
|
@@ -1,28 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { abortSignalAny } from "../../utils/abortSignal.js";
|
|
2
|
+
import {
|
|
3
|
+
JsonRpcPayload,
|
|
4
|
+
JsonRpcResponse,
|
|
5
|
+
JsonRpcTransport,
|
|
6
|
+
JsonRpcTransportRequestOptions,
|
|
7
|
+
} from "./transport.js";
|
|
2
8
|
|
|
3
|
-
export class
|
|
9
|
+
export class JsonRpcTransportHttp implements JsonRpcTransport {
|
|
4
10
|
constructor(
|
|
5
11
|
private readonly url: string,
|
|
6
12
|
private readonly timeout = 30000,
|
|
7
13
|
) {}
|
|
8
14
|
|
|
9
|
-
async request(
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
async request(
|
|
16
|
+
payload: JsonRpcPayload,
|
|
17
|
+
options?: JsonRpcTransportRequestOptions,
|
|
18
|
+
): Promise<JsonRpcResponse> {
|
|
19
|
+
const timeoutSignal = AbortSignal.timeout(options?.timeout ?? this.timeout);
|
|
20
|
+
const signal = options?.signal
|
|
21
|
+
? abortSignalAny([options.signal, timeoutSignal])
|
|
22
|
+
: timeoutSignal;
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
const raw = await (
|
|
24
|
+
return (await (
|
|
15
25
|
await fetch(this.url, {
|
|
16
26
|
method: "POST",
|
|
17
27
|
headers: {
|
|
18
28
|
"content-type": "application/json",
|
|
19
29
|
},
|
|
20
30
|
body: JSON.stringify(payload),
|
|
21
|
-
signal
|
|
31
|
+
signal,
|
|
22
32
|
})
|
|
23
|
-
).json();
|
|
24
|
-
clearTimeout(abortTimer);
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
26
|
-
return raw;
|
|
33
|
+
).json()) as JsonRpcResponse;
|
|
27
34
|
}
|
|
28
35
|
}
|
|
@@ -1,18 +1,54 @@
|
|
|
1
|
+
export type JsonRpcId = string | number;
|
|
2
|
+
|
|
1
3
|
export type JsonRpcPayload = {
|
|
2
|
-
id:
|
|
4
|
+
id: JsonRpcId;
|
|
3
5
|
jsonrpc: "2.0";
|
|
4
6
|
method: string;
|
|
5
7
|
params: unknown[] | Record<string, unknown>;
|
|
6
8
|
};
|
|
7
9
|
|
|
8
|
-
export
|
|
10
|
+
export type JsonRpcErrorLike<Data = unknown> = {
|
|
11
|
+
code: number;
|
|
12
|
+
message: string;
|
|
13
|
+
data?: Data;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class JsonRpcError<Data = unknown> extends Error {
|
|
17
|
+
readonly code: number;
|
|
18
|
+
readonly data?: Data;
|
|
19
|
+
|
|
20
|
+
constructor(error: JsonRpcErrorLike<Data>) {
|
|
21
|
+
super(error.message);
|
|
22
|
+
this.name = "JsonRpcError";
|
|
23
|
+
this.code = error.code;
|
|
24
|
+
this.data = error.data;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type JsonRpcResponse<Result = unknown, ErrorData = unknown> = {
|
|
29
|
+
id: JsonRpcId;
|
|
30
|
+
jsonrpc: "2.0";
|
|
31
|
+
} & (
|
|
32
|
+
| { result: Result; error?: never }
|
|
33
|
+
| { result?: never; error: JsonRpcErrorLike<ErrorData> }
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export type JsonRpcTransportRequestOptions = {
|
|
37
|
+
/** Cancels this individual request without disposing the transport. */
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
/** Overrides the transport's default timeout for this request. */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface JsonRpcTransport {
|
|
9
44
|
/**
|
|
10
45
|
* Sends a JSON-RPC request to the server.
|
|
11
46
|
*
|
|
12
47
|
* @param payload - The JSON-RPC payload to send.
|
|
13
|
-
* @returns The
|
|
14
|
-
*
|
|
15
|
-
* @throws Will throw an error if the response ID does not match the request ID, or if the response contains an error.
|
|
48
|
+
* @returns The JSON-RPC response.
|
|
16
49
|
*/
|
|
17
|
-
request(
|
|
50
|
+
request(
|
|
51
|
+
payload: JsonRpcPayload,
|
|
52
|
+
options?: JsonRpcTransportRequestOptions,
|
|
53
|
+
): Promise<JsonRpcResponse>;
|
|
18
54
|
}
|