@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.
Files changed (89) hide show
  1. package/CHANGELOG.md +133 -1
  2. package/README.md +3 -3
  3. package/dist/advanced.d.mts +2 -2
  4. package/dist/advanced.mjs +1 -1
  5. package/{dist.commonjs/advancedBarrel-DNdhwiU0.d.ts → dist/advancedBarrel-DfFMFhdl.d.mts} +2790 -2237
  6. package/dist/advancedBarrel-DfFMFhdl.d.mts.map +1 -0
  7. package/dist/advancedBarrel.d.mts +2 -2
  8. package/dist/advancedBarrel.mjs +1 -1
  9. package/dist/advancedBarrel.mjs.map +1 -1
  10. package/dist/barrel-ONYEMVg5.mjs +7 -0
  11. package/dist/barrel-ONYEMVg5.mjs.map +1 -0
  12. package/dist/barrel.d.mts +2 -2
  13. package/dist/barrel.mjs +1 -1
  14. package/dist/index.d.mts +2 -2
  15. package/dist/index.mjs +1 -1
  16. package/dist/transformers-CduZ1Js5.mjs +2 -0
  17. package/dist/transformers-CduZ1Js5.mjs.map +1 -0
  18. package/dist.commonjs/advanced.d.ts +2 -2
  19. package/dist.commonjs/advanced.js +1 -1
  20. package/{dist/advancedBarrel-BHwnMJqR.d.mts → dist.commonjs/advancedBarrel-Dl0mM9JO.d.ts} +2791 -2236
  21. package/dist.commonjs/advancedBarrel-Dl0mM9JO.d.ts.map +1 -0
  22. package/dist.commonjs/advancedBarrel.d.ts +2 -2
  23. package/dist.commonjs/advancedBarrel.js +1 -1
  24. package/dist.commonjs/advancedBarrel.js.map +1 -1
  25. package/dist.commonjs/barrel-HfgYMxP_.js +23 -0
  26. package/dist.commonjs/barrel-HfgYMxP_.js.map +1 -0
  27. package/dist.commonjs/barrel.d.ts +2 -2
  28. package/dist.commonjs/barrel.js +1 -1
  29. package/dist.commonjs/index.d.ts +2 -2
  30. package/dist.commonjs/index.js +1 -1
  31. package/dist.commonjs/{rolldown-runtime-DWNI8pZO.js → rolldown-runtime-Chgba0Kb.js} +1 -1
  32. package/dist.commonjs/transformers-BUlLwiyt.js +2 -0
  33. package/dist.commonjs/transformers-BUlLwiyt.js.map +1 -0
  34. package/package.json +8 -7
  35. package/src/advancedBarrel.ts +1 -0
  36. package/src/bytes/advanced.ts +1 -6
  37. package/src/bytes/index.ts +37 -10
  38. package/src/ckb/transaction.ts +7 -5
  39. package/src/client/client.ts +83 -21
  40. package/src/client/clientPublicMainnet.advanced.ts +9 -0
  41. package/src/client/clientPublicMainnet.ts +75 -36
  42. package/src/client/clientPublicTestnet.advanced.ts +9 -0
  43. package/src/client/clientPublicTestnet.ts +75 -36
  44. package/src/client/jsonRpc/client.ts +77 -30
  45. package/src/client/jsonRpc/transformers.ts +4 -4
  46. package/src/jsonRpc/index.ts +1 -0
  47. package/src/jsonRpc/requestor.ts +106 -29
  48. package/src/jsonRpc/transports/advanced.ts +0 -4
  49. package/src/jsonRpc/transports/factory.ts +12 -5
  50. package/src/jsonRpc/transports/fallback.ts +14 -6
  51. package/src/jsonRpc/transports/http.ts +19 -12
  52. package/src/jsonRpc/transports/index.ts +4 -0
  53. package/src/jsonRpc/transports/transport.ts +42 -6
  54. package/src/jsonRpc/transports/webSocket.ts +145 -40
  55. package/src/keystore/index.ts +3 -3
  56. package/src/molecule/codec.ts +20 -17
  57. package/src/signer/advanced.ts +1 -0
  58. package/src/signer/btc/psbt.ts +4 -2
  59. package/src/signer/btc/verify.ts +2 -2
  60. package/src/signer/ckb/secp256k1Signing.ts +3 -3
  61. package/src/signer/doge/signerDoge.ts +1 -1
  62. package/src/signer/doge/signerDogePrivateKey.ts +1 -1
  63. package/src/signer/index.ts +1 -0
  64. package/src/signer/jsonRpc/handler.ts +478 -0
  65. package/src/signer/jsonRpc/index.ts +2 -0
  66. package/src/signer/jsonRpc/network.ts +15 -0
  67. package/src/signer/jsonRpc/signer.ts +378 -0
  68. package/src/signer/jsonRpc/transformers.ts +71 -0
  69. package/src/signer/signer/index.ts +5 -13
  70. package/src/utils/abortSignal.ts +56 -0
  71. package/src/utils/constructor.ts +11 -0
  72. package/src/utils/index.ts +172 -5
  73. package/src/utils/owner/aggregated.ts +56 -0
  74. package/src/utils/owner/index.ts +4 -0
  75. package/src/utils/owner/owner.ts +141 -0
  76. package/src/utils/owner/refCount.ts +78 -0
  77. package/src/utils/owner/unique.ts +30 -0
  78. package/src/utils/proxy.ts +165 -0
  79. package/tsdown.config.mts +1 -0
  80. package/dist/address.advanced-LxntWbev.mjs +0 -2
  81. package/dist/address.advanced-LxntWbev.mjs.map +0 -1
  82. package/dist/advancedBarrel-BHwnMJqR.d.mts.map +0 -1
  83. package/dist/barrel-C-BaV9Pe.mjs +0 -7
  84. package/dist/barrel-C-BaV9Pe.mjs.map +0 -1
  85. package/dist.commonjs/address.advanced-BqcYOpZn.js +0 -2
  86. package/dist.commonjs/address.advanced-BqcYOpZn.js.map +0 -1
  87. package/dist.commonjs/advancedBarrel-DNdhwiU0.d.ts.map +0 -1
  88. package/dist.commonjs/barrel-BBOmj4RJ.js +0 -21
  89. package/dist.commonjs/barrel-BBOmj4RJ.js.map +0 -1
@@ -0,0 +1,378 @@
1
+ import { randomBytes } from "@noble/hashes/utils.js";
2
+ import { Address } from "../../address/index.js";
3
+ import { Script } from "../../ckb/index.js";
4
+ import { Client } from "../../client/index.js";
5
+ import {
6
+ JsonRpcScript,
7
+ JsonRpcTransformers,
8
+ } from "../../client/jsonRpc/advanced.js";
9
+ import { hexFrom } from "../../hex/index.js";
10
+ import { JsonRpcError, RequestorJsonRpc } from "../../jsonRpc/index.js";
11
+ import { retry, sleep, waitForAvailability } from "../../utils/index.js";
12
+ import { Signer } from "../signer/index.js";
13
+ import {
14
+ SignerJsonRpcErrorCode,
15
+ type SignerJsonRpcResultRecord,
16
+ } from "./handler.js";
17
+ import { signerJsonRpcNetworkIdFromAddressPrefix } from "./network.js";
18
+ import {
19
+ SignerJsonRpcInfo,
20
+ SignerJsonRpcTransformers,
21
+ } from "./transformers.js";
22
+
23
+ export type SignerJsonRpcConfig = Omit<
24
+ Parameters<typeof RequestorJsonRpc.new>[0],
25
+ "onError"
26
+ > & {
27
+ /** Cleans up integration-owned resources before replacement is announced. */
28
+ disconnectHandler?: () => PromiseLike<void> | void;
29
+ };
30
+
31
+ const GET_RESULT_RETRY_DELAYS = [1_000, 2_000, 4_000, 8_000] as const;
32
+ const GET_RESULT_MIN_POLL_INTERVAL_MS = 5_000;
33
+ const GET_RESULT_RETRY_REPEAT_MS = 10_000;
34
+ const GET_RESULT_TIMEOUT_MS = 20_000;
35
+ const REQUEST_RETRY_DELAYS = [5_000, 10_000, 20_000] as const;
36
+ const REQUEST_TIMEOUT_MS = 10_000;
37
+
38
+ export class SignerJsonRpc extends Signer {
39
+ private connecting?: Promise<void>;
40
+ private disconnecting?: Promise<void>;
41
+ private scriptsPromise?: Promise<Script[]>;
42
+ private internalAddressPromise?: Promise<string>;
43
+ private identityPromise?: Promise<string>;
44
+ private requestsController = new AbortController();
45
+ private readonly replacedListeners = new Set<() => void>();
46
+
47
+ private constructor(
48
+ client: Client,
49
+ readonly requestor: RequestorJsonRpc,
50
+ private info: SignerJsonRpcInfo,
51
+ private readonly disconnectHandler?: () => PromiseLike<void> | void,
52
+ ) {
53
+ super(client);
54
+ }
55
+
56
+ /** Creates a Signer that borrows an existing Transport. */
57
+ static async new(client: Client, config: SignerJsonRpcConfig) {
58
+ const { disconnectHandler, ...requestorConfig } = config;
59
+ const requestor = RequestorJsonRpc.new(requestorConfig);
60
+ const signer = new SignerJsonRpc(
61
+ client,
62
+ requestor,
63
+ {} as SignerJsonRpcInfo,
64
+ disconnectHandler,
65
+ );
66
+ signer.info = (await signer.request(
67
+ "get_info",
68
+ [],
69
+ [],
70
+ SignerJsonRpcTransformers.infoTo,
71
+ )) as SignerJsonRpcInfo;
72
+ return signer;
73
+ }
74
+
75
+ get type() {
76
+ return this.info.type;
77
+ }
78
+
79
+ get signType() {
80
+ return this.info.signType;
81
+ }
82
+
83
+ get name() {
84
+ return this.info.name;
85
+ }
86
+
87
+ get icon() {
88
+ return this.info.icon;
89
+ }
90
+
91
+ connect(): Promise<void> {
92
+ if (this.disconnecting) {
93
+ return this.disconnecting.then(() => this.connect());
94
+ }
95
+ if (this.connecting) {
96
+ return this.connecting;
97
+ }
98
+
99
+ const connecting = this.requestConnect(
100
+ signerJsonRpcNetworkIdFromAddressPrefix(this.client.addressPrefix),
101
+ ).catch((cause: unknown) => {
102
+ if (this.connecting === connecting) {
103
+ this.connecting = undefined;
104
+ }
105
+ throw cause;
106
+ });
107
+ this.connecting = connecting;
108
+ return connecting;
109
+ }
110
+
111
+ disconnect(): Promise<void> {
112
+ this.clearReadCache();
113
+ if (this.disconnecting) {
114
+ return this.disconnecting;
115
+ }
116
+ if (!this.connecting) {
117
+ return Promise.resolve();
118
+ }
119
+
120
+ this.connecting = undefined;
121
+ this.disconnecting = Promise.resolve()
122
+ .then(() => this.disconnectHandler?.())
123
+ .finally(() => {
124
+ this.replace();
125
+ this.disconnecting = undefined;
126
+ });
127
+ return this.disconnecting;
128
+ }
129
+
130
+ private requestConnect = this.buildSender("connect", []) as (
131
+ networkId: string,
132
+ ) => Promise<void>;
133
+
134
+ onReplaced(listener: () => void) {
135
+ this.replacedListeners.add(listener);
136
+ return () => this.replacedListeners.delete(listener);
137
+ }
138
+
139
+ replace() {
140
+ const requestsController = this.requestsController;
141
+ this.requestsController = new AbortController();
142
+ requestsController.abort(new Error("Signer JSON-RPC was replaced"));
143
+ this.connecting = undefined;
144
+ this.clearReadCache();
145
+ const listeners = [...this.replacedListeners];
146
+ this.replacedListeners.clear();
147
+ listeners.forEach((listener) => listener());
148
+ }
149
+
150
+ async isConnected() {
151
+ return (
152
+ this.connecting?.then(
153
+ () => true,
154
+ () => false,
155
+ ) ?? false
156
+ );
157
+ }
158
+
159
+ getScripts(): Promise<Script[]> {
160
+ if (this.scriptsPromise) {
161
+ return this.scriptsPromise.then((scripts) => [...scripts]);
162
+ }
163
+
164
+ const pending = this.request(
165
+ "get_scripts",
166
+ [],
167
+ [],
168
+ (scripts: JsonRpcScript[]) =>
169
+ scripts.map((script) => JsonRpcTransformers.scriptTo(script)),
170
+ ).catch((cause: unknown) => {
171
+ if (this.scriptsPromise === pending) {
172
+ this.scriptsPromise = undefined;
173
+ }
174
+ throw cause;
175
+ }) as Promise<Script[]>;
176
+ this.scriptsPromise = pending;
177
+ return pending.then((scripts) => [...scripts]);
178
+ }
179
+
180
+ async getAddressObjs() {
181
+ return (await this.getScripts()).map((script) =>
182
+ Address.fromScript(script, this.client),
183
+ );
184
+ }
185
+
186
+ getInternalAddress(): Promise<string> {
187
+ if (this.internalAddressPromise) {
188
+ return this.internalAddressPromise;
189
+ }
190
+
191
+ const pending = this.request("get_native_address", [], []).catch(
192
+ (cause: unknown) => {
193
+ if (this.internalAddressPromise === pending) {
194
+ this.internalAddressPromise = undefined;
195
+ }
196
+ throw cause;
197
+ },
198
+ ) as Promise<string>;
199
+ this.internalAddressPromise = pending;
200
+ return pending;
201
+ }
202
+
203
+ getIdentity(): Promise<string> {
204
+ if (this.identityPromise) {
205
+ return this.identityPromise;
206
+ }
207
+
208
+ const pending = this.request("get_identity", [], []).catch(
209
+ (cause: unknown) => {
210
+ if (this.identityPromise === pending) {
211
+ this.identityPromise = undefined;
212
+ }
213
+ throw cause;
214
+ },
215
+ ) as Promise<string>;
216
+ this.identityPromise = pending;
217
+ return pending;
218
+ }
219
+
220
+ signMessageRaw = this.buildSender("sign_message", [
221
+ SignerJsonRpcTransformers.messageFrom,
222
+ ]) as Signer["signMessageRaw"];
223
+
224
+ prepareTransaction = this.buildSender(
225
+ "prepare_transaction",
226
+ [JsonRpcTransformers.transactionFrom],
227
+ JsonRpcTransformers.transactionTo,
228
+ ) as Signer["prepareTransaction"];
229
+
230
+ signOnlyTransaction = this.buildSender(
231
+ "sign_transaction",
232
+ [JsonRpcTransformers.transactionFrom],
233
+ JsonRpcTransformers.transactionTo,
234
+ ) as Signer["signOnlyTransaction"];
235
+
236
+ buildSender(
237
+ rpcMethod: Parameters<RequestorJsonRpc["request"]>[0],
238
+ inTransformers?: Parameters<RequestorJsonRpc["request"]>[2],
239
+ outTransformer?: Parameters<RequestorJsonRpc["request"]>[3],
240
+ ): (...request: unknown[]) => Promise<unknown> {
241
+ return async (...request: unknown[]) =>
242
+ this.request(rpcMethod, request, inTransformers, outTransformer);
243
+ }
244
+
245
+ private clearReadCache() {
246
+ this.scriptsPromise = undefined;
247
+ this.internalAddressPromise = undefined;
248
+ this.identityPromise = undefined;
249
+ }
250
+
251
+ private async request(
252
+ method: string,
253
+ params: unknown[],
254
+ inTransformers?: Parameters<RequestorJsonRpc["request"]>[2],
255
+ outTransformer?: Parameters<RequestorJsonRpc["request"]>[3],
256
+ ): Promise<unknown> {
257
+ const requestId = hexFrom(randomBytes(16));
258
+ const sessionId = method === "get_info" ? undefined : this.info.sessionId;
259
+ const signal = this.requestsController.signal;
260
+
261
+ const result = retry(
262
+ REQUEST_RETRY_DELAYS,
263
+ async ({ resolve, reject }) => {
264
+ try {
265
+ return resolve(
266
+ await this.requestor.request(
267
+ method,
268
+ [
269
+ {
270
+ request_id: requestId,
271
+ ...(sessionId ? { session_id: sessionId } : {}),
272
+ },
273
+ ...params,
274
+ ],
275
+ inTransformers ? [undefined, ...inTransformers] : undefined,
276
+ outTransformer,
277
+ { signal, timeout: REQUEST_TIMEOUT_MS },
278
+ ),
279
+ );
280
+ } catch (cause) {
281
+ if (cause instanceof JsonRpcError) {
282
+ return reject(cause);
283
+ }
284
+ throw cause;
285
+ }
286
+ },
287
+ { signal },
288
+ ).catch((cause: unknown) => {
289
+ signal.throwIfAborted();
290
+ if (
291
+ cause instanceof JsonRpcError &&
292
+ cause.code !== Number(SignerJsonRpcErrorCode.DuplicateRequestId)
293
+ ) {
294
+ throw cause;
295
+ }
296
+
297
+ return this.recoverResult(requestId, cause, signal, outTransformer);
298
+ });
299
+
300
+ try {
301
+ const value = await result;
302
+ signal.throwIfAborted();
303
+ return value;
304
+ } catch (cause) {
305
+ if (
306
+ cause instanceof JsonRpcError &&
307
+ cause.code === Number(SignerJsonRpcErrorCode.InvalidSession)
308
+ ) {
309
+ this.replace();
310
+ }
311
+ throw cause;
312
+ }
313
+ }
314
+
315
+ private async recoverResult(
316
+ requestId: string,
317
+ requestError: unknown,
318
+ signal: AbortSignal,
319
+ outTransformer?: Parameters<RequestorJsonRpc["request"]>[3],
320
+ ) {
321
+ return retry(
322
+ [],
323
+ async ({ resolve, reject, next }) => {
324
+ try {
325
+ let getResultStartedAt = Date.now();
326
+ const result = await retry<SignerJsonRpcResultRecord>(
327
+ GET_RESULT_RETRY_DELAYS,
328
+ async ({ resolve, reject }) => {
329
+ await waitForAvailability(signal);
330
+ getResultStartedAt = Date.now();
331
+
332
+ try {
333
+ return resolve(
334
+ (await this.requestor.request(
335
+ "get_result",
336
+ [{}, requestId],
337
+ undefined,
338
+ undefined,
339
+ { signal, timeout: GET_RESULT_TIMEOUT_MS },
340
+ )) as SignerJsonRpcResultRecord,
341
+ );
342
+ } catch (cause) {
343
+ if (cause instanceof JsonRpcError) {
344
+ return reject(cause);
345
+ }
346
+ throw cause;
347
+ }
348
+ },
349
+ { repeat: GET_RESULT_RETRY_REPEAT_MS, signal },
350
+ );
351
+ signal.throwIfAborted();
352
+
353
+ if (result.status === "not_found") {
354
+ return reject(requestError);
355
+ }
356
+ if (result.status === "pending") {
357
+ const remaining =
358
+ GET_RESULT_MIN_POLL_INTERVAL_MS -
359
+ (Date.now() - getResultStartedAt);
360
+ if (remaining > 0) {
361
+ await sleep(remaining, signal);
362
+ }
363
+ return next();
364
+ }
365
+ if (result.status !== "completed" || !("result" in result)) {
366
+ return reject(new Error("Invalid signer request result"));
367
+ }
368
+ return resolve(
369
+ outTransformer ? outTransformer(result.result) : result.result,
370
+ );
371
+ } catch (cause) {
372
+ return reject(cause);
373
+ }
374
+ },
375
+ { repeat: 0, signal },
376
+ );
377
+ }
378
+ }
@@ -0,0 +1,71 @@
1
+ import { BytesLike, bytesFrom } from "../../bytes/index.js";
2
+ import { Hex, hexFrom } from "../../hex/index.js";
3
+ import { SignerSignType, SignerType } from "../signer/index.js";
4
+
5
+ export type SignerJsonRpcInfo = {
6
+ sessionId: string;
7
+ type: SignerType;
8
+ signType: SignerSignType;
9
+ name?: string;
10
+ icon?: string;
11
+ };
12
+
13
+ export type SignerJsonRpcInfoPayload = {
14
+ session_id: string;
15
+ type: SignerType;
16
+ sign_type: SignerSignType;
17
+ name?: string;
18
+ icon?: string;
19
+ };
20
+
21
+ export type SignerJsonRpcMessageToSign =
22
+ { type: "string"; value: string } | { type: "bytes"; value: Hex };
23
+
24
+ export class SignerJsonRpcTransformers {
25
+ static infoFrom(info: SignerJsonRpcInfo): SignerJsonRpcInfoPayload {
26
+ return {
27
+ session_id: info.sessionId,
28
+ type: info.type,
29
+ sign_type: info.signType,
30
+ name: info.name,
31
+ icon: info.icon,
32
+ };
33
+ }
34
+
35
+ static infoTo(info: SignerJsonRpcInfoPayload): SignerJsonRpcInfo {
36
+ return {
37
+ sessionId: info.session_id,
38
+ type: info.type,
39
+ signType: info.sign_type,
40
+ name: info.name,
41
+ icon: info.icon,
42
+ };
43
+ }
44
+
45
+ static messageFrom(message: string | BytesLike): SignerJsonRpcMessageToSign {
46
+ if (typeof message === "string") {
47
+ return { type: "string", value: message };
48
+ }
49
+
50
+ return { type: "bytes", value: hexFrom(message) };
51
+ }
52
+
53
+ static messageTo(message: unknown): string | Uint8Array {
54
+ if (!isRecord(message) || typeof message.value !== "string") {
55
+ throw new Error("Invalid signer message");
56
+ }
57
+
58
+ if (message.type === "string") {
59
+ return message.value;
60
+ }
61
+ if (message.type === "bytes") {
62
+ return bytesFrom(message.value);
63
+ }
64
+
65
+ throw new Error("Invalid signer message type");
66
+ }
67
+ }
68
+
69
+ function isRecord(value: unknown): value is Record<string, unknown> {
70
+ return typeof value === "object" && value !== null;
71
+ }
@@ -44,21 +44,12 @@ export enum SignerType {
44
44
 
45
45
  /**
46
46
  * @public
47
+ * @deprecated Wallet integrations now expose one signer per selectable network.
48
+ * This compatibility type will be removed in the next major release.
47
49
  */
48
50
  export type NetworkPreference = {
49
51
  addressPrefix: string;
50
52
  signerType: SignerType;
51
- /**
52
- * Wallet signers should check if the wallet is using preferred networks.
53
- * If not, try to switch to the first preferred network.
54
- * If non preferred, let users choose what they want.
55
- * BTC: // They made a mess...
56
- * btc
57
- * btcTestnet
58
- * btcTestnet4 // UTXO Global
59
- * btcSignet // OKX & UTXO Global
60
- * fractalBtc // UniSat
61
- */
62
53
  network: string;
63
54
  };
64
55
 
@@ -88,8 +79,9 @@ export abstract class Signer {
88
79
  return this.client_;
89
80
  }
90
81
 
91
- // Returns the preference if we need to switch network
92
- // undefined otherwise
82
+ /**
83
+ * @deprecated Wallet integrations now use fixed-network signers.
84
+ */
93
85
  matchNetworkPreference(
94
86
  preferences: NetworkPreference[],
95
87
  currentNetwork: string | undefined,
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Creates a signal that aborts when any of the input signals aborts.
3
+ *
4
+ * Uses the native `AbortSignal.any` when available and falls back to an
5
+ * `AbortController` implementation for older runtimes.
6
+ *
7
+ * @public
8
+ */
9
+ export function abortSignalAny(signals: readonly AbortSignal[]): AbortSignal {
10
+ if (typeof AbortSignal.any === "function") {
11
+ return AbortSignal.any([...signals]);
12
+ }
13
+
14
+ const controller = new AbortController();
15
+ const listeners = new Map<AbortSignal, () => void>();
16
+ const cleanup = () => {
17
+ for (const [signal, listener] of listeners) {
18
+ signal.removeEventListener("abort", listener);
19
+ }
20
+ listeners.clear();
21
+ };
22
+ const abortFrom = (signal: AbortSignal) => {
23
+ cleanup();
24
+ controller.abort(signal.reason);
25
+ };
26
+
27
+ for (const signal of signals) {
28
+ if (signal.aborted) {
29
+ abortFrom(signal);
30
+ break;
31
+ }
32
+ if (listeners.has(signal)) {
33
+ continue;
34
+ }
35
+
36
+ const listener = () => abortFrom(signal);
37
+ listeners.set(signal, listener);
38
+ signal.addEventListener("abort", listener, { once: true });
39
+ }
40
+
41
+ return controller.signal;
42
+ }
43
+
44
+ /**
45
+ * Returns a promise that rejects with the abort reason when the signal aborts.
46
+ *
47
+ * @public
48
+ */
49
+ export function abortSignalToPromise(signal: AbortSignal): Promise<never> {
50
+ return new Promise<never>((_resolve, reject) => {
51
+ signal.throwIfAborted();
52
+ signal.addEventListener("abort", () => reject(signal.reason), {
53
+ once: true,
54
+ });
55
+ });
56
+ }
@@ -0,0 +1,11 @@
1
+ export type Constructor<
2
+ T,
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ Args extends any[] = any[],
5
+ > = new (...args: Args) => T;
6
+
7
+ export type AbstractConstructor<
8
+ T,
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ Args extends any[] = any[],
11
+ > = abstract new (...args: Args) => T;