coinbase-api 0.3.2 → 1.0.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/README.md +438 -14
- package/dist/cjs/CBAdvancedTradeClient.d.ts +559 -0
- package/dist/cjs/CBAdvancedTradeClient.js +574 -0
- package/dist/cjs/CBAdvancedTradeClient.js.map +1 -0
- package/dist/cjs/CBAppClient.d.ts +359 -0
- package/dist/cjs/CBAppClient.js +336 -0
- package/dist/cjs/CBAppClient.js.map +1 -0
- package/dist/cjs/CBCommerceClient.d.ts +110 -0
- package/dist/cjs/CBCommerceClient.js +105 -0
- package/dist/cjs/CBCommerceClient.js.map +1 -0
- package/dist/cjs/CBExchangeClient.d.ts +640 -0
- package/dist/cjs/CBExchangeClient.js +751 -0
- package/dist/cjs/CBExchangeClient.js.map +1 -0
- package/dist/cjs/CBInternationalClient.d.ts +381 -0
- package/dist/cjs/CBInternationalClient.js +437 -0
- package/dist/cjs/CBInternationalClient.js.map +1 -0
- package/dist/cjs/CBPrimeClient.d.ts +363 -0
- package/dist/cjs/CBPrimeClient.js +451 -0
- package/dist/cjs/CBPrimeClient.js.map +1 -0
- package/dist/cjs/WebsocketClient.d.ts +78 -0
- package/dist/cjs/WebsocketClient.js +534 -0
- package/dist/cjs/WebsocketClient.js.map +1 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.js +30 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/lib/BaseRestClient.d.ts +66 -0
- package/dist/cjs/lib/BaseRestClient.js +436 -0
- package/dist/cjs/lib/BaseRestClient.js.map +1 -0
- package/dist/cjs/lib/BaseWSClient.d.ts +169 -0
- package/dist/cjs/lib/BaseWSClient.js +638 -0
- package/dist/cjs/lib/BaseWSClient.js.map +1 -0
- package/dist/cjs/lib/jwtNode.d.ts +13 -0
- package/dist/cjs/lib/jwtNode.js +52 -0
- package/dist/cjs/lib/jwtNode.js.map +1 -0
- package/dist/cjs/lib/misc-util.d.ts +1 -0
- package/dist/cjs/lib/misc-util.js +7 -0
- package/dist/cjs/lib/misc-util.js.map +1 -0
- package/dist/cjs/lib/requestUtils.d.ts +98 -0
- package/dist/cjs/lib/requestUtils.js +75 -0
- package/dist/cjs/lib/requestUtils.js.map +1 -0
- package/dist/cjs/lib/webCryptoAPI.d.ts +10 -0
- package/dist/cjs/lib/webCryptoAPI.js +83 -0
- package/dist/cjs/lib/webCryptoAPI.js.map +1 -0
- package/dist/cjs/lib/websocket/WsStore.d.ts +63 -0
- package/dist/cjs/lib/websocket/WsStore.js +254 -0
- package/dist/cjs/lib/websocket/WsStore.js.map +1 -0
- package/dist/cjs/lib/websocket/WsStore.types.d.ts +49 -0
- package/dist/cjs/lib/websocket/WsStore.types.js +14 -0
- package/dist/cjs/lib/websocket/WsStore.types.js.map +1 -0
- package/dist/cjs/lib/websocket/logger.d.ts +6 -0
- package/dist/cjs/lib/websocket/logger.js +16 -0
- package/dist/cjs/lib/websocket/logger.js.map +1 -0
- package/dist/cjs/lib/websocket/typeGuards.d.ts +27 -0
- package/dist/cjs/lib/websocket/typeGuards.js +86 -0
- package/dist/cjs/lib/websocket/typeGuards.js.map +1 -0
- package/dist/cjs/lib/websocket/websocket-util.d.ts +120 -0
- package/dist/cjs/lib/websocket/websocket-util.js +173 -0
- package/dist/cjs/lib/websocket/websocket-util.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types/request/advanced-trade-client.d.ts +194 -0
- package/dist/cjs/types/request/advanced-trade-client.js +18 -0
- package/dist/cjs/types/request/advanced-trade-client.js.map +1 -0
- package/dist/cjs/types/request/coinbase-app-client.d.ts +80 -0
- package/dist/cjs/types/request/coinbase-app-client.js +28 -0
- package/dist/cjs/types/request/coinbase-app-client.js.map +1 -0
- package/dist/cjs/types/request/coinbase-exchange.d.ts +332 -0
- package/dist/cjs/types/request/coinbase-exchange.js +3 -0
- package/dist/cjs/types/request/coinbase-exchange.js.map +1 -0
- package/dist/cjs/types/request/coinbase-international.d.ts +160 -0
- package/dist/cjs/types/request/coinbase-international.js +13 -0
- package/dist/cjs/types/request/coinbase-international.js.map +1 -0
- package/dist/cjs/types/request/coinbase-prime.d.ts +298 -0
- package/dist/cjs/types/request/coinbase-prime.js +8 -0
- package/dist/cjs/types/request/coinbase-prime.js.map +1 -0
- package/dist/cjs/types/response/advanced-trade-client.d.ts +759 -0
- package/dist/cjs/types/response/advanced-trade-client.js +8 -0
- package/dist/cjs/types/response/advanced-trade-client.js.map +1 -0
- package/dist/cjs/types/response/coinbase-app-client.d.ts +168 -0
- package/dist/cjs/types/response/coinbase-app-client.js +18 -0
- package/dist/cjs/types/response/coinbase-app-client.js.map +1 -0
- package/dist/cjs/types/response/coinbase-exchange.d.ts +80 -0
- package/dist/cjs/types/response/coinbase-exchange.js +82 -0
- package/dist/cjs/types/response/coinbase-exchange.js.map +1 -0
- package/dist/cjs/types/response/coinbase-international.d.ts +40 -0
- package/dist/cjs/types/response/coinbase-international.js +42 -0
- package/dist/cjs/types/response/coinbase-international.js.map +1 -0
- package/dist/cjs/types/response/coinbase-prime.d.ts +70 -0
- package/dist/cjs/types/response/coinbase-prime.js +72 -0
- package/dist/cjs/types/response/coinbase-prime.js.map +1 -0
- package/dist/cjs/types/response/ws.d.ts +7 -0
- package/dist/cjs/types/response/ws.js +3 -0
- package/dist/cjs/types/response/ws.js.map +1 -0
- package/dist/cjs/types/shared.types.d.ts +60 -0
- package/dist/cjs/types/shared.types.js +3 -0
- package/dist/cjs/types/shared.types.js.map +1 -0
- package/dist/cjs/types/websockets/client.d.ts +73 -0
- package/dist/cjs/types/websockets/client.js +3 -0
- package/dist/cjs/types/websockets/client.js.map +1 -0
- package/dist/cjs/types/websockets/events.d.ts +40 -0
- package/dist/cjs/types/websockets/events.js +3 -0
- package/dist/cjs/types/websockets/events.js.map +1 -0
- package/dist/cjs/types/websockets/requests.d.ts +76 -0
- package/dist/cjs/types/websockets/requests.js +3 -0
- package/dist/cjs/types/websockets/requests.js.map +1 -0
- package/dist/cjs/types/websockets/wsAPI.d.ts +9 -0
- package/dist/cjs/types/websockets/wsAPI.js +3 -0
- package/dist/cjs/types/websockets/wsAPI.js.map +1 -0
- package/dist/mjs/CBAdvancedTradeClient.d.ts +559 -0
- package/dist/mjs/CBAdvancedTradeClient.js +570 -0
- package/dist/mjs/CBAdvancedTradeClient.js.map +1 -0
- package/dist/mjs/CBAppClient.d.ts +359 -0
- package/dist/mjs/CBAppClient.js +332 -0
- package/dist/mjs/CBAppClient.js.map +1 -0
- package/dist/mjs/CBCommerceClient.d.ts +110 -0
- package/dist/mjs/CBCommerceClient.js +101 -0
- package/dist/mjs/CBCommerceClient.js.map +1 -0
- package/dist/mjs/CBExchangeClient.d.ts +640 -0
- package/dist/mjs/CBExchangeClient.js +747 -0
- package/dist/mjs/CBExchangeClient.js.map +1 -0
- package/dist/mjs/CBInternationalClient.d.ts +381 -0
- package/dist/mjs/CBInternationalClient.js +433 -0
- package/dist/mjs/CBInternationalClient.js.map +1 -0
- package/dist/mjs/CBPrimeClient.d.ts +363 -0
- package/dist/mjs/CBPrimeClient.js +447 -0
- package/dist/mjs/CBPrimeClient.js.map +1 -0
- package/dist/mjs/WebsocketClient.d.ts +78 -0
- package/dist/mjs/WebsocketClient.js +530 -0
- package/dist/mjs/WebsocketClient.js.map +1 -0
- package/dist/mjs/index.d.ts +13 -0
- package/dist/mjs/index.js +14 -0
- package/dist/mjs/index.js.map +1 -0
- package/dist/mjs/lib/BaseRestClient.d.ts +66 -0
- package/dist/mjs/lib/BaseRestClient.js +429 -0
- package/dist/mjs/lib/BaseRestClient.js.map +1 -0
- package/dist/mjs/lib/BaseWSClient.d.ts +169 -0
- package/dist/mjs/lib/BaseWSClient.js +631 -0
- package/dist/mjs/lib/BaseWSClient.js.map +1 -0
- package/dist/mjs/lib/jwtNode.d.ts +13 -0
- package/dist/mjs/lib/jwtNode.js +45 -0
- package/dist/mjs/lib/jwtNode.js.map +1 -0
- package/dist/mjs/lib/misc-util.d.ts +1 -0
- package/dist/mjs/lib/misc-util.js +4 -0
- package/dist/mjs/lib/misc-util.js.map +1 -0
- package/dist/mjs/lib/requestUtils.d.ts +98 -0
- package/dist/mjs/lib/requestUtils.js +69 -0
- package/dist/mjs/lib/requestUtils.js.map +1 -0
- package/dist/mjs/lib/webCryptoAPI.d.ts +10 -0
- package/dist/mjs/lib/webCryptoAPI.js +79 -0
- package/dist/mjs/lib/webCryptoAPI.js.map +1 -0
- package/dist/mjs/lib/websocket/WsStore.d.ts +63 -0
- package/dist/mjs/lib/websocket/WsStore.js +249 -0
- package/dist/mjs/lib/websocket/WsStore.js.map +1 -0
- package/dist/mjs/lib/websocket/WsStore.types.d.ts +49 -0
- package/dist/mjs/lib/websocket/WsStore.types.js +11 -0
- package/dist/mjs/lib/websocket/WsStore.types.js.map +1 -0
- package/dist/mjs/lib/websocket/logger.d.ts +6 -0
- package/dist/mjs/lib/websocket/logger.js +13 -0
- package/dist/mjs/lib/websocket/logger.js.map +1 -0
- package/dist/mjs/lib/websocket/typeGuards.d.ts +27 -0
- package/dist/mjs/lib/websocket/typeGuards.js +78 -0
- package/dist/mjs/lib/websocket/typeGuards.js.map +1 -0
- package/dist/mjs/lib/websocket/websocket-util.d.ts +120 -0
- package/dist/mjs/lib/websocket/websocket-util.js +164 -0
- package/dist/mjs/lib/websocket/websocket-util.js.map +1 -0
- package/dist/mjs/package.json +3 -0
- package/dist/mjs/types/request/advanced-trade-client.d.ts +194 -0
- package/dist/mjs/types/request/advanced-trade-client.js +17 -0
- package/dist/mjs/types/request/advanced-trade-client.js.map +1 -0
- package/dist/mjs/types/request/coinbase-app-client.d.ts +80 -0
- package/dist/mjs/types/request/coinbase-app-client.js +27 -0
- package/dist/mjs/types/request/coinbase-app-client.js.map +1 -0
- package/dist/mjs/types/request/coinbase-exchange.d.ts +332 -0
- package/dist/mjs/types/request/coinbase-exchange.js +2 -0
- package/dist/mjs/types/request/coinbase-exchange.js.map +1 -0
- package/dist/mjs/types/request/coinbase-international.d.ts +160 -0
- package/dist/mjs/types/request/coinbase-international.js +12 -0
- package/dist/mjs/types/request/coinbase-international.js.map +1 -0
- package/dist/mjs/types/request/coinbase-prime.d.ts +298 -0
- package/dist/mjs/types/request/coinbase-prime.js +7 -0
- package/dist/mjs/types/request/coinbase-prime.js.map +1 -0
- package/dist/mjs/types/response/advanced-trade-client.d.ts +759 -0
- package/dist/mjs/types/response/advanced-trade-client.js +7 -0
- package/dist/mjs/types/response/advanced-trade-client.js.map +1 -0
- package/dist/mjs/types/response/coinbase-app-client.d.ts +168 -0
- package/dist/mjs/types/response/coinbase-app-client.js +17 -0
- package/dist/mjs/types/response/coinbase-app-client.js.map +1 -0
- package/dist/mjs/types/response/coinbase-exchange.d.ts +80 -0
- package/dist/mjs/types/response/coinbase-exchange.js +82 -0
- package/dist/mjs/types/response/coinbase-exchange.js.map +1 -0
- package/dist/mjs/types/response/coinbase-international.d.ts +40 -0
- package/dist/mjs/types/response/coinbase-international.js +42 -0
- package/dist/mjs/types/response/coinbase-international.js.map +1 -0
- package/dist/mjs/types/response/coinbase-prime.d.ts +70 -0
- package/dist/mjs/types/response/coinbase-prime.js +72 -0
- package/dist/mjs/types/response/coinbase-prime.js.map +1 -0
- package/dist/mjs/types/response/ws.d.ts +7 -0
- package/dist/mjs/types/response/ws.js +2 -0
- package/dist/mjs/types/response/ws.js.map +1 -0
- package/dist/mjs/types/shared.types.d.ts +60 -0
- package/dist/mjs/types/shared.types.js +2 -0
- package/dist/mjs/types/shared.types.js.map +1 -0
- package/dist/mjs/types/websockets/client.d.ts +73 -0
- package/dist/mjs/types/websockets/client.js +2 -0
- package/dist/mjs/types/websockets/client.js.map +1 -0
- package/dist/mjs/types/websockets/events.d.ts +40 -0
- package/dist/mjs/types/websockets/events.js +2 -0
- package/dist/mjs/types/websockets/events.js.map +1 -0
- package/dist/mjs/types/websockets/requests.d.ts +76 -0
- package/dist/mjs/types/websockets/requests.js +2 -0
- package/dist/mjs/types/websockets/requests.js.map +1 -0
- package/dist/mjs/types/websockets/wsAPI.d.ts +9 -0
- package/dist/mjs/types/websockets/wsAPI.js +2 -0
- package/dist/mjs/types/websockets/wsAPI.js.map +1 -0
- package/package.json +42 -30
- package/index.js +0 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -16
- package/lib/index.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface JWTSignParams {
|
|
2
|
+
algorithm: 'ES256';
|
|
3
|
+
timestampMs: number;
|
|
4
|
+
jwtExpiresSeconds: number;
|
|
5
|
+
apiPubKey: string;
|
|
6
|
+
apiPrivKey: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function signJWT(params: {
|
|
9
|
+
url: string;
|
|
10
|
+
method: string;
|
|
11
|
+
} & JWTSignParams): string;
|
|
12
|
+
export declare function signWSJWT(params: JWTSignParams): string;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
export function signJWT(params) {
|
|
4
|
+
const { url, method, algorithm, timestampMs, jwtExpiresSeconds, apiPrivKey, apiPubKey, } = params;
|
|
5
|
+
// Remove https:// but keep the rest
|
|
6
|
+
const urlWithEndpoint = url.slice(8);
|
|
7
|
+
const uri = `${method} ${urlWithEndpoint}`;
|
|
8
|
+
const payload = {
|
|
9
|
+
iss: 'cdp',
|
|
10
|
+
nbf: Math.floor(timestampMs / 1000),
|
|
11
|
+
exp: Math.floor(timestampMs / 1000) + jwtExpiresSeconds,
|
|
12
|
+
sub: apiPubKey,
|
|
13
|
+
uri,
|
|
14
|
+
};
|
|
15
|
+
const header = {
|
|
16
|
+
alg: algorithm,
|
|
17
|
+
kid: apiPubKey,
|
|
18
|
+
nonce: nanoid(16),
|
|
19
|
+
};
|
|
20
|
+
const options = {
|
|
21
|
+
algorithm: algorithm,
|
|
22
|
+
header: header,
|
|
23
|
+
};
|
|
24
|
+
return jwt.sign(payload, apiPrivKey, options);
|
|
25
|
+
}
|
|
26
|
+
export function signWSJWT(params) {
|
|
27
|
+
const { algorithm, timestampMs, jwtExpiresSeconds, apiPrivKey, apiPubKey } = params;
|
|
28
|
+
const payload = {
|
|
29
|
+
iss: 'cdp',
|
|
30
|
+
nbf: Math.floor(timestampMs / 1000),
|
|
31
|
+
exp: Math.floor(timestampMs / 1000) + jwtExpiresSeconds,
|
|
32
|
+
sub: apiPubKey,
|
|
33
|
+
};
|
|
34
|
+
const header = {
|
|
35
|
+
alg: algorithm,
|
|
36
|
+
kid: apiPubKey,
|
|
37
|
+
nonce: nanoid(16),
|
|
38
|
+
};
|
|
39
|
+
const options = {
|
|
40
|
+
algorithm: algorithm,
|
|
41
|
+
header: header,
|
|
42
|
+
};
|
|
43
|
+
return jwt.sign(payload, apiPrivKey, options);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=jwtNode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwtNode.js","sourceRoot":"","sources":["../../../src/lib/jwtNode.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAUhC,MAAM,UAAU,OAAO,CACrB,MAGiB;IAEjB,MAAM,EACJ,GAAG,EACH,MAAM,EACN,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,SAAS,GACV,GAAG,MAAM,CAAC;IAEX,oCAAoC;IACpC,MAAM,eAAe,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,eAAe,EAAE,CAAC;IAE3C,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,KAAK;QACV,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;QACnC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,iBAAiB;QACvD,GAAG,EAAE,SAAS;QACd,GAAG;KACJ,CAAC;IAEF,MAAM,MAAM,GAAG;QACb,GAAG,EAAE,SAAS;QACd,GAAG,EAAE,SAAS;QACd,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;KAClB,CAAC;IAEF,MAAM,OAAO,GAAoB;QAC/B,SAAS,EAAE,SAA0B;QACrC,MAAM,EAAE,MAAM;KACf,CAAC;IAEF,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAqB;IAC7C,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,GACxE,MAAM,CAAC;IACT,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,KAAK;QACV,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;QACnC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,iBAAiB;QACvD,GAAG,EAAE,SAAS;KACf,CAAC;IAEF,MAAM,MAAM,GAAG;QACb,GAAG,EAAE,SAAS;QACd,GAAG,EAAE,SAAS;QACd,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;KAClB,CAAC;IAEF,MAAM,OAAO,GAAoB;QAC/B,SAAS,EAAE,SAA0B;QACrC,MAAM,EAAE,MAAM;KACf,CAAC;IAEF,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function neverGuard(x: never, msg: string): Error;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misc-util.js","sourceRoot":"","sources":["../../../src/lib/misc-util.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,CAAQ,EAAE,GAAW;IAC9C,OAAO,IAAI,KAAK,CAAC,8BAA8B,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { CustomOrderIdProperty } from '../types/shared.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Used to switch how authentication/requests work under the hood
|
|
4
|
+
*/
|
|
5
|
+
export declare const REST_CLIENT_TYPE_ENUM: {
|
|
6
|
+
/** Coinbase Advanced Trade API */
|
|
7
|
+
readonly advancedTrade: "advancedTrade";
|
|
8
|
+
/** Coinbase App API */
|
|
9
|
+
readonly coinbaseApp: "coinbaseApp";
|
|
10
|
+
/** Coinbase Exchange API */
|
|
11
|
+
readonly exchange: "exchange";
|
|
12
|
+
/** Coinbase Prime API */
|
|
13
|
+
readonly prime: "prime";
|
|
14
|
+
/** Coinbase International API */
|
|
15
|
+
readonly international: "international";
|
|
16
|
+
/** Coinbase Commerce API */
|
|
17
|
+
readonly commerce: "commerce";
|
|
18
|
+
};
|
|
19
|
+
export type RestClientType = (typeof REST_CLIENT_TYPE_ENUM)[keyof typeof REST_CLIENT_TYPE_ENUM];
|
|
20
|
+
export interface RestClientOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Your API key name.
|
|
23
|
+
*
|
|
24
|
+
* - For the Advanced Trade or App APIs, this is your API Key Name.
|
|
25
|
+
*/
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Your API key secret.
|
|
29
|
+
*
|
|
30
|
+
* - For the Advanced Trade or App APIs, this is your API private key (including the -----BEGIN EC PRIVATE KEY-----\n etc).
|
|
31
|
+
*/
|
|
32
|
+
apiSecret?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Your API passphrase (NOT your account password). Only used for the API groups that use an API passphrase:
|
|
35
|
+
* - Coinbase Exchange API
|
|
36
|
+
* - Coinbase International API
|
|
37
|
+
* - Coinbase Prime API
|
|
38
|
+
*/
|
|
39
|
+
apiPassphrase?: string;
|
|
40
|
+
/**
|
|
41
|
+
* For the Advanced Trade or App APIs, instead of passing the key name and
|
|
42
|
+
* private key, you can also parse the exported "cdp_api_key.json" into an object and pass it here.
|
|
43
|
+
*
|
|
44
|
+
* It will automatically get parsed into the apiKey & apiSecret configuration parameters.
|
|
45
|
+
*/
|
|
46
|
+
cdpApiKey?: {
|
|
47
|
+
name: string;
|
|
48
|
+
privateKey: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* https://docs.cdp.coinbase.com/coinbase-app/docs/localization
|
|
52
|
+
*
|
|
53
|
+
* The Coinbase App API supports localization for error messages and other strings.
|
|
54
|
+
* Localization is defined in each request with Accept-Language header.
|
|
55
|
+
*
|
|
56
|
+
* Accepted values are currently as below. Note: this may not work for all coinbase product groups.
|
|
57
|
+
*/
|
|
58
|
+
localisation?: 'de' | 'en' | 'es' | 'es-mx' | 'fr' | 'id' | 'it' | 'nl' | 'pt' | 'pt-br';
|
|
59
|
+
/**
|
|
60
|
+
* Connect to the sandbox for supported products
|
|
61
|
+
*
|
|
62
|
+
* - Coinbase Exchange: https://docs.cdp.coinbase.com/exchange/docs/sandbox
|
|
63
|
+
* - Coinbase International: https://docs.cdp.coinbase.com/intx/docs/sandbox
|
|
64
|
+
*
|
|
65
|
+
* - Coinbase App: No sandbox available.
|
|
66
|
+
* - Coinbase Advanced Trade: No sandbox available.
|
|
67
|
+
* - Coinbase Prime: No sandbox available.
|
|
68
|
+
*/
|
|
69
|
+
useSandbox?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Enable keep alive for REST API requests (via axios).
|
|
72
|
+
* See: https://github.com/tiagosiebler/bybit-api/issues/368
|
|
73
|
+
*/
|
|
74
|
+
keepAlive?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000.
|
|
77
|
+
* Only relevant if keepAlive is set to true.
|
|
78
|
+
* Default: 1000 (defaults comes from https agent)
|
|
79
|
+
*/
|
|
80
|
+
keepAliveMsecs?: number;
|
|
81
|
+
/**
|
|
82
|
+
* For JWT auth (adv trade & app API), seconds until jwt expires. Defaults to 120 seconds.
|
|
83
|
+
*/
|
|
84
|
+
jwtExpiresSeconds?: number;
|
|
85
|
+
/** Default: false. If true, we'll throw errors if any params are undefined */
|
|
86
|
+
strictParamValidation?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Optionally override API protocol + domain
|
|
89
|
+
* e.g baseUrl: 'https://api.differentUrl.com'
|
|
90
|
+
**/
|
|
91
|
+
baseUrl?: string;
|
|
92
|
+
/** Default: true. whether to try and post-process request exceptions (and throw them). */
|
|
93
|
+
parseExceptions?: boolean;
|
|
94
|
+
}
|
|
95
|
+
export declare function serializeParams<T extends Record<string, any> | undefined = {}>(params: T, strict_validation: boolean | undefined, encodeValues: boolean, prefixWith: string): string;
|
|
96
|
+
export declare const APIIDPrefix = "cbnode";
|
|
97
|
+
export declare function logInvalidOrderId(orderIdProperty: CustomOrderIdProperty, expectedOrderIdPrefix: string, params: unknown): void;
|
|
98
|
+
export declare function getRestBaseUrl(restClientOptions: RestClientOptions, restClientType: RestClientType): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Used to switch how authentication/requests work under the hood
|
|
3
|
+
*/
|
|
4
|
+
export const REST_CLIENT_TYPE_ENUM = {
|
|
5
|
+
/** Coinbase Advanced Trade API */
|
|
6
|
+
advancedTrade: 'advancedTrade',
|
|
7
|
+
/** Coinbase App API */
|
|
8
|
+
coinbaseApp: 'coinbaseApp',
|
|
9
|
+
/** Coinbase Exchange API */
|
|
10
|
+
exchange: 'exchange',
|
|
11
|
+
/** Coinbase Prime API */
|
|
12
|
+
prime: 'prime',
|
|
13
|
+
/** Coinbase International API */
|
|
14
|
+
international: 'international',
|
|
15
|
+
/** Coinbase Commerce API */
|
|
16
|
+
commerce: 'commerce',
|
|
17
|
+
};
|
|
18
|
+
const exchangeBaseURLMap = {
|
|
19
|
+
[REST_CLIENT_TYPE_ENUM.advancedTrade]: 'https://api.coinbase.com',
|
|
20
|
+
[REST_CLIENT_TYPE_ENUM.coinbaseApp]: 'https://api.coinbase.com',
|
|
21
|
+
[REST_CLIENT_TYPE_ENUM.exchange]: 'https://api.exchange.coinbase.com',
|
|
22
|
+
[REST_CLIENT_TYPE_ENUM.prime]: 'https://api.prime.coinbase.com',
|
|
23
|
+
[REST_CLIENT_TYPE_ENUM.international]: 'https://api.international.coinbase.com',
|
|
24
|
+
[REST_CLIENT_TYPE_ENUM.commerce]: 'https://api.commerce.coinbase.com',
|
|
25
|
+
};
|
|
26
|
+
const exchangeSandboxURLMap = {
|
|
27
|
+
[REST_CLIENT_TYPE_ENUM.exchange]: 'https://api-public.sandbox.exchange.coinbase.com',
|
|
28
|
+
[REST_CLIENT_TYPE_ENUM.international]: 'https://api-n5e1.coinbase.com',
|
|
29
|
+
[REST_CLIENT_TYPE_ENUM.advancedTrade]: 'NoSandboxAvailable',
|
|
30
|
+
[REST_CLIENT_TYPE_ENUM.coinbaseApp]: 'NoSandboxAvailable',
|
|
31
|
+
[REST_CLIENT_TYPE_ENUM.prime]: 'NoSandboxAvailable',
|
|
32
|
+
[REST_CLIENT_TYPE_ENUM.commerce]: 'NoSandboxAvailable',
|
|
33
|
+
};
|
|
34
|
+
export function serializeParams(params, strict_validation, encodeValues, prefixWith) {
|
|
35
|
+
if (!params) {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
const queryString = Object.keys(params)
|
|
39
|
+
.sort()
|
|
40
|
+
.map((key) => {
|
|
41
|
+
const value = params[key];
|
|
42
|
+
if (strict_validation === true && typeof value === 'undefined') {
|
|
43
|
+
throw new Error('Failed to sign API request due to undefined parameter');
|
|
44
|
+
}
|
|
45
|
+
const encodedValue = encodeValues ? encodeURIComponent(value) : value;
|
|
46
|
+
return `${key}=${encodedValue}`;
|
|
47
|
+
})
|
|
48
|
+
.join('&');
|
|
49
|
+
// Only prefix if there's a value
|
|
50
|
+
return queryString ? prefixWith + queryString : queryString;
|
|
51
|
+
}
|
|
52
|
+
export const APIIDPrefix = 'cbnode';
|
|
53
|
+
export function logInvalidOrderId(orderIdProperty, expectedOrderIdPrefix, params) {
|
|
54
|
+
console.error(`'${orderIdProperty}' must be prefixed with ${expectedOrderIdPrefix}. Use the 'client.generateNewOrderId()' REST client utility method to generate a fresh order ID on demand. Original request: ${JSON.stringify(params)}`);
|
|
55
|
+
}
|
|
56
|
+
export function getRestBaseUrl(restClientOptions, restClientType) {
|
|
57
|
+
const exchangeBaseUrls = {
|
|
58
|
+
livenet: exchangeBaseURLMap[restClientType],
|
|
59
|
+
testnet: exchangeSandboxURLMap[restClientType],
|
|
60
|
+
};
|
|
61
|
+
if (restClientOptions.baseUrl) {
|
|
62
|
+
return restClientOptions.baseUrl;
|
|
63
|
+
}
|
|
64
|
+
if (restClientOptions.useSandbox === true) {
|
|
65
|
+
return exchangeBaseUrls.testnet;
|
|
66
|
+
}
|
|
67
|
+
return exchangeBaseUrls.livenet;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=requestUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestUtils.js","sourceRoot":"","sources":["../../../src/lib/requestUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type SignEncodeMethod = 'hex' | 'base64';
|
|
2
|
+
export type SignAlgorithm = 'SHA-256' | 'SHA-512';
|
|
3
|
+
/**
|
|
4
|
+
* Similar to node crypto's `createHash()` function
|
|
5
|
+
*/
|
|
6
|
+
export declare function hashMessage(message: string, method: SignEncodeMethod, algorithm: SignAlgorithm): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Sign a message, with a secret, using the Web Crypto API
|
|
9
|
+
*/
|
|
10
|
+
export declare function signMessage(message: string, secret: string, method: SignEncodeMethod, algorithm: SignAlgorithm, secretEncodeMethod: 'base64:web' | 'utf'): Promise<string>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { neverGuard } from './misc-util.js';
|
|
2
|
+
function bufferToB64(buffer) {
|
|
3
|
+
let binary = '';
|
|
4
|
+
const bytes = new Uint8Array(buffer);
|
|
5
|
+
const len = bytes.byteLength;
|
|
6
|
+
for (let i = 0; i < len; i++) {
|
|
7
|
+
binary += String.fromCharCode(bytes[i]);
|
|
8
|
+
}
|
|
9
|
+
return globalThis.btoa(binary);
|
|
10
|
+
}
|
|
11
|
+
function b64StringToBuffer(input) {
|
|
12
|
+
const binaryString = atob(input); // Decode base64 string
|
|
13
|
+
const buffer = new Uint8Array(binaryString.length);
|
|
14
|
+
// Convert binary string to a Uint8Array
|
|
15
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
16
|
+
buffer[i] = binaryString.charCodeAt(i);
|
|
17
|
+
}
|
|
18
|
+
return buffer;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Similar to node crypto's `createHash()` function
|
|
22
|
+
*/
|
|
23
|
+
export async function hashMessage(message, method, algorithm) {
|
|
24
|
+
const encoder = new TextEncoder();
|
|
25
|
+
const buffer = await globalThis.crypto.subtle.digest(algorithm, encoder.encode(message));
|
|
26
|
+
switch (method) {
|
|
27
|
+
case 'hex': {
|
|
28
|
+
return Array.from(new Uint8Array(buffer))
|
|
29
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
30
|
+
.join('');
|
|
31
|
+
}
|
|
32
|
+
case 'base64': {
|
|
33
|
+
return bufferToB64(buffer);
|
|
34
|
+
}
|
|
35
|
+
default: {
|
|
36
|
+
throw neverGuard(method, `Unhandled sign method: "${method}"`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sign a message, with a secret, using the Web Crypto API
|
|
42
|
+
*/
|
|
43
|
+
export async function signMessage(message, secret, method, algorithm, secretEncodeMethod) {
|
|
44
|
+
const encoder = new TextEncoder();
|
|
45
|
+
let encodedSecret;
|
|
46
|
+
switch (secretEncodeMethod) {
|
|
47
|
+
// case 'base64:node': {
|
|
48
|
+
// encodedSecret = Buffer.from(secret, 'base64');
|
|
49
|
+
// break;
|
|
50
|
+
// }
|
|
51
|
+
case 'base64:web': {
|
|
52
|
+
encodedSecret = b64StringToBuffer(secret);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case 'utf': {
|
|
56
|
+
encodedSecret = encoder.encode(secret);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default: {
|
|
60
|
+
throw new Error(`Unhandled encoding: "${secretEncodeMethod}"`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const key = await globalThis.crypto.subtle.importKey('raw', encodedSecret, { name: 'HMAC', hash: algorithm }, false, ['sign']);
|
|
64
|
+
const buffer = await globalThis.crypto.subtle.sign('HMAC', key, encoder.encode(message));
|
|
65
|
+
switch (method) {
|
|
66
|
+
case 'hex': {
|
|
67
|
+
return Array.from(new Uint8Array(buffer))
|
|
68
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
69
|
+
.join('');
|
|
70
|
+
}
|
|
71
|
+
case 'base64': {
|
|
72
|
+
return bufferToB64(buffer);
|
|
73
|
+
}
|
|
74
|
+
default: {
|
|
75
|
+
throw neverGuard(method, `Unhandled sign method: "${method}"`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=webCryptoAPI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webCryptoAPI.js","sourceRoot":"","sources":["../../../src/lib/webCryptoAPI.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import WebSocket from 'isomorphic-ws';
|
|
2
|
+
import { DefaultLogger } from './logger.js';
|
|
3
|
+
import { DeferredPromise, WSConnectedResult, WsConnectionStateEnum, WsStoredState } from './WsStore.types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Simple comparison of two objects, only checks 1-level deep (nested objects won't match)
|
|
6
|
+
*/
|
|
7
|
+
export declare function isDeepObjectMatch(object1: unknown, object2: unknown): boolean;
|
|
8
|
+
declare const DEFERRED_PROMISE_REF: {
|
|
9
|
+
readonly CONNECTION_IN_PROGRESS: "CONNECTION_IN_PROGRESS";
|
|
10
|
+
};
|
|
11
|
+
type DeferredPromiseRef = (typeof DEFERRED_PROMISE_REF)[keyof typeof DEFERRED_PROMISE_REF];
|
|
12
|
+
export declare class WsStore<WsKey extends string, TWSTopicSubscribeEventArgs extends string | object> {
|
|
13
|
+
private wsState;
|
|
14
|
+
private logger;
|
|
15
|
+
constructor(logger: typeof DefaultLogger);
|
|
16
|
+
/** Get WS stored state for key, optionally create if missing */
|
|
17
|
+
get(key: WsKey, createIfMissing?: true): WsStoredState<TWSTopicSubscribeEventArgs>;
|
|
18
|
+
get(key: WsKey, createIfMissing?: false): WsStoredState<TWSTopicSubscribeEventArgs> | undefined;
|
|
19
|
+
getKeys(): WsKey[];
|
|
20
|
+
create(key: WsKey): WsStoredState<TWSTopicSubscribeEventArgs> | undefined;
|
|
21
|
+
delete(key: WsKey): void;
|
|
22
|
+
hasExistingActiveConnection(key: WsKey): boolean;
|
|
23
|
+
getWs(key: WsKey): WebSocket | undefined;
|
|
24
|
+
setWs(key: WsKey, wsConnection: WebSocket): WebSocket;
|
|
25
|
+
getDeferredPromise<TSuccessResult = any>(wsKey: WsKey, promiseRef: string | DeferredPromiseRef): DeferredPromise<TSuccessResult> | undefined;
|
|
26
|
+
createDeferredPromise<TSuccessResult = any>(wsKey: WsKey, promiseRef: string | DeferredPromiseRef, throwIfExists: boolean): DeferredPromise<TSuccessResult>;
|
|
27
|
+
resolveDeferredPromise(wsKey: WsKey, promiseRef: string | DeferredPromiseRef, value: unknown, removeAfter: boolean): void;
|
|
28
|
+
rejectDeferredPromise(wsKey: WsKey, promiseRef: string | DeferredPromiseRef, value: unknown, removeAfter: boolean): void;
|
|
29
|
+
removeDeferredPromise(wsKey: WsKey, promiseRef: string | DeferredPromiseRef): void;
|
|
30
|
+
rejectAllDeferredPromises(wsKey: WsKey, reason: string): void;
|
|
31
|
+
/** Get promise designed to track a connection attempt in progress. Resolves once connected. */
|
|
32
|
+
getConnectionInProgressPromise(wsKey: WsKey): DeferredPromise<WSConnectedResult> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Create a deferred promise designed to track a connection attempt in progress.
|
|
35
|
+
*
|
|
36
|
+
* Will throw if existing promise is found.
|
|
37
|
+
*/
|
|
38
|
+
createConnectionInProgressPromise(wsKey: WsKey, throwIfExists: boolean): DeferredPromise<WSConnectedResult>;
|
|
39
|
+
/** Remove promise designed to track a connection attempt in progress */
|
|
40
|
+
removeConnectingInProgressPromise(wsKey: WsKey): void;
|
|
41
|
+
isWsOpen(key: WsKey): boolean;
|
|
42
|
+
getConnectionState(key: WsKey): WsConnectionStateEnum;
|
|
43
|
+
setConnectionState(key: WsKey, state: WsConnectionStateEnum): void;
|
|
44
|
+
isConnectionState(key: WsKey, state: WsConnectionStateEnum): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check if we're currently in the process of opening a connection for any reason. Safer than only checking "CONNECTING" as the state
|
|
47
|
+
* @param key
|
|
48
|
+
* @returns
|
|
49
|
+
*/
|
|
50
|
+
isConnectionAttemptInProgress(key: WsKey): boolean;
|
|
51
|
+
getTopics(key: WsKey): Set<TWSTopicSubscribeEventArgs>;
|
|
52
|
+
getTopicsByKey(): Record<string, Set<TWSTopicSubscribeEventArgs>>;
|
|
53
|
+
/**
|
|
54
|
+
* Find matching "topic" request from the store
|
|
55
|
+
* @param key
|
|
56
|
+
* @param topic
|
|
57
|
+
* @returns
|
|
58
|
+
*/
|
|
59
|
+
getMatchingTopic(key: WsKey, topic: TWSTopicSubscribeEventArgs): TWSTopicSubscribeEventArgs | undefined;
|
|
60
|
+
addTopic(key: WsKey, topic: TWSTopicSubscribeEventArgs): Set<TWSTopicSubscribeEventArgs>;
|
|
61
|
+
deleteTopic(key: WsKey, topic: TWSTopicSubscribeEventArgs): Set<TWSTopicSubscribeEventArgs>;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { DefaultLogger } from './logger.js';
|
|
2
|
+
import { WsConnectionStateEnum, } from './WsStore.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Simple comparison of two objects, only checks 1-level deep (nested objects won't match)
|
|
5
|
+
*/
|
|
6
|
+
export function isDeepObjectMatch(object1, object2) {
|
|
7
|
+
if (typeof object1 === 'string' && typeof object2 === 'string') {
|
|
8
|
+
return object1 === object2;
|
|
9
|
+
}
|
|
10
|
+
if (typeof object1 !== 'object' || typeof object2 !== 'object') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
for (const key in object1) {
|
|
14
|
+
const value1 = object1[key];
|
|
15
|
+
const value2 = object2[key];
|
|
16
|
+
if (value1 !== value2) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
const DEFERRED_PROMISE_REF = {
|
|
23
|
+
CONNECTION_IN_PROGRESS: 'CONNECTION_IN_PROGRESS',
|
|
24
|
+
};
|
|
25
|
+
export class WsStore {
|
|
26
|
+
wsState = {};
|
|
27
|
+
logger;
|
|
28
|
+
constructor(logger) {
|
|
29
|
+
this.logger = logger || DefaultLogger;
|
|
30
|
+
}
|
|
31
|
+
get(key, createIfMissing) {
|
|
32
|
+
if (this.wsState[key]) {
|
|
33
|
+
return this.wsState[key];
|
|
34
|
+
}
|
|
35
|
+
if (createIfMissing) {
|
|
36
|
+
return this.create(key);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
getKeys() {
|
|
40
|
+
return Object.keys(this.wsState);
|
|
41
|
+
}
|
|
42
|
+
create(key) {
|
|
43
|
+
if (this.hasExistingActiveConnection(key)) {
|
|
44
|
+
this.logger.info('WsStore setConnection() overwriting existing open connection: ', this.getWs(key));
|
|
45
|
+
}
|
|
46
|
+
this.wsState[key] = {
|
|
47
|
+
subscribedTopics: new Set(),
|
|
48
|
+
connectionState: WsConnectionStateEnum.INITIAL,
|
|
49
|
+
deferredPromiseStore: {},
|
|
50
|
+
};
|
|
51
|
+
return this.get(key);
|
|
52
|
+
}
|
|
53
|
+
delete(key) {
|
|
54
|
+
// TODO: should we allow this at all? Perhaps block this from happening...
|
|
55
|
+
if (this.hasExistingActiveConnection(key)) {
|
|
56
|
+
const ws = this.getWs(key);
|
|
57
|
+
this.logger.info('WsStore deleting state for connection still open: ', ws);
|
|
58
|
+
ws?.close();
|
|
59
|
+
}
|
|
60
|
+
delete this.wsState[key];
|
|
61
|
+
}
|
|
62
|
+
/* connection websocket */
|
|
63
|
+
hasExistingActiveConnection(key) {
|
|
64
|
+
return this.get(key) && this.isWsOpen(key);
|
|
65
|
+
}
|
|
66
|
+
getWs(key) {
|
|
67
|
+
return this.get(key)?.ws;
|
|
68
|
+
}
|
|
69
|
+
setWs(key, wsConnection) {
|
|
70
|
+
if (this.isWsOpen(key)) {
|
|
71
|
+
this.logger.info('WsStore setConnection() overwriting existing open connection: ', this.getWs(key));
|
|
72
|
+
}
|
|
73
|
+
this.get(key, true).ws = wsConnection;
|
|
74
|
+
return wsConnection;
|
|
75
|
+
}
|
|
76
|
+
getDeferredPromise(wsKey, promiseRef) {
|
|
77
|
+
const storeForKey = this.get(wsKey);
|
|
78
|
+
if (!storeForKey) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const deferredPromiseStore = storeForKey.deferredPromiseStore;
|
|
82
|
+
return deferredPromiseStore[promiseRef];
|
|
83
|
+
}
|
|
84
|
+
createDeferredPromise(wsKey, promiseRef, throwIfExists) {
|
|
85
|
+
const existingPromise = this.getDeferredPromise(wsKey, promiseRef);
|
|
86
|
+
if (existingPromise) {
|
|
87
|
+
if (throwIfExists) {
|
|
88
|
+
throw new Error(`Promise exists for "${wsKey}"`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// console.log('existing promise');
|
|
92
|
+
return existingPromise;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// console.log('create promise');
|
|
96
|
+
const createIfMissing = true;
|
|
97
|
+
const storeForKey = this.get(wsKey, createIfMissing);
|
|
98
|
+
// TODO: Once stable, use Promise.withResolvers in future
|
|
99
|
+
const deferredPromise = {};
|
|
100
|
+
deferredPromise.promise = new Promise((resolve, reject) => {
|
|
101
|
+
deferredPromise.resolve = resolve;
|
|
102
|
+
deferredPromise.reject = reject;
|
|
103
|
+
});
|
|
104
|
+
const deferredPromiseStore = storeForKey.deferredPromiseStore;
|
|
105
|
+
deferredPromiseStore[promiseRef] = deferredPromise;
|
|
106
|
+
return deferredPromise;
|
|
107
|
+
}
|
|
108
|
+
resolveDeferredPromise(wsKey, promiseRef, value, removeAfter) {
|
|
109
|
+
const promise = this.getDeferredPromise(wsKey, promiseRef);
|
|
110
|
+
if (promise?.resolve) {
|
|
111
|
+
promise.resolve(value);
|
|
112
|
+
}
|
|
113
|
+
if (removeAfter) {
|
|
114
|
+
this.removeDeferredPromise(wsKey, promiseRef);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
rejectDeferredPromise(wsKey, promiseRef, value, removeAfter) {
|
|
118
|
+
const promise = this.getDeferredPromise(wsKey, promiseRef);
|
|
119
|
+
if (promise?.reject) {
|
|
120
|
+
promise.reject(value);
|
|
121
|
+
}
|
|
122
|
+
if (removeAfter) {
|
|
123
|
+
this.removeDeferredPromise(wsKey, promiseRef);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
removeDeferredPromise(wsKey, promiseRef) {
|
|
127
|
+
const storeForKey = this.get(wsKey);
|
|
128
|
+
if (!storeForKey) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const deferredPromise = storeForKey.deferredPromiseStore[promiseRef];
|
|
132
|
+
if (deferredPromise) {
|
|
133
|
+
// Just in case it's pending
|
|
134
|
+
if (deferredPromise.resolve) {
|
|
135
|
+
deferredPromise.resolve('promiseRemoved');
|
|
136
|
+
}
|
|
137
|
+
delete storeForKey.deferredPromiseStore[promiseRef];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
rejectAllDeferredPromises(wsKey, reason) {
|
|
141
|
+
const storeForKey = this.get(wsKey);
|
|
142
|
+
const deferredPromiseStore = storeForKey.deferredPromiseStore;
|
|
143
|
+
if (!storeForKey || !deferredPromiseStore) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const reservedKeys = Object.values(DEFERRED_PROMISE_REF);
|
|
147
|
+
for (const promiseRef in deferredPromiseStore) {
|
|
148
|
+
// Skip reserved keys, such as the connection promise
|
|
149
|
+
if (reservedKeys.includes(promiseRef)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
this.rejectDeferredPromise(wsKey, promiseRef, reason, true);
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
this.logger.error(`rejectAllDeferredPromises(): Exception rejecting deferred promise`, { wsKey: wsKey, reason, promiseRef, exception: e });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** Get promise designed to track a connection attempt in progress. Resolves once connected. */
|
|
161
|
+
getConnectionInProgressPromise(wsKey) {
|
|
162
|
+
return this.getDeferredPromise(wsKey, DEFERRED_PROMISE_REF.CONNECTION_IN_PROGRESS);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Create a deferred promise designed to track a connection attempt in progress.
|
|
166
|
+
*
|
|
167
|
+
* Will throw if existing promise is found.
|
|
168
|
+
*/
|
|
169
|
+
createConnectionInProgressPromise(wsKey, throwIfExists) {
|
|
170
|
+
return this.createDeferredPromise(wsKey, DEFERRED_PROMISE_REF.CONNECTION_IN_PROGRESS, throwIfExists);
|
|
171
|
+
}
|
|
172
|
+
/** Remove promise designed to track a connection attempt in progress */
|
|
173
|
+
removeConnectingInProgressPromise(wsKey) {
|
|
174
|
+
return this.removeDeferredPromise(wsKey, DEFERRED_PROMISE_REF.CONNECTION_IN_PROGRESS);
|
|
175
|
+
}
|
|
176
|
+
/* connection state */
|
|
177
|
+
isWsOpen(key) {
|
|
178
|
+
const existingConnection = this.getWs(key);
|
|
179
|
+
return (!!existingConnection &&
|
|
180
|
+
existingConnection.readyState === existingConnection.OPEN);
|
|
181
|
+
}
|
|
182
|
+
getConnectionState(key) {
|
|
183
|
+
return this.get(key, true).connectionState;
|
|
184
|
+
}
|
|
185
|
+
setConnectionState(key, state) {
|
|
186
|
+
// console.log(new Date(), `setConnectionState(${key}, ${state})`);
|
|
187
|
+
this.get(key, true).connectionState = state;
|
|
188
|
+
}
|
|
189
|
+
isConnectionState(key, state) {
|
|
190
|
+
return this.getConnectionState(key) === state;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if we're currently in the process of opening a connection for any reason. Safer than only checking "CONNECTING" as the state
|
|
194
|
+
* @param key
|
|
195
|
+
* @returns
|
|
196
|
+
*/
|
|
197
|
+
isConnectionAttemptInProgress(key) {
|
|
198
|
+
const isConnectionInProgress = this.isConnectionState(key, WsConnectionStateEnum.CONNECTING) ||
|
|
199
|
+
this.isConnectionState(key, WsConnectionStateEnum.RECONNECTING);
|
|
200
|
+
return isConnectionInProgress;
|
|
201
|
+
}
|
|
202
|
+
/* subscribed topics */
|
|
203
|
+
getTopics(key) {
|
|
204
|
+
return this.get(key, true).subscribedTopics;
|
|
205
|
+
}
|
|
206
|
+
getTopicsByKey() {
|
|
207
|
+
const result = {};
|
|
208
|
+
for (const refKey in this.wsState) {
|
|
209
|
+
result[refKey] = this.getTopics(refKey);
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
// Since topics are objects we can't rely on the set to detect duplicates
|
|
214
|
+
/**
|
|
215
|
+
* Find matching "topic" request from the store
|
|
216
|
+
* @param key
|
|
217
|
+
* @param topic
|
|
218
|
+
* @returns
|
|
219
|
+
*/
|
|
220
|
+
getMatchingTopic(key, topic) {
|
|
221
|
+
// if (typeof topic === 'string') {
|
|
222
|
+
// return this.getMatchingTopic(key, { channel: topic });
|
|
223
|
+
// }
|
|
224
|
+
const allTopics = this.getTopics(key).values();
|
|
225
|
+
for (const storedTopic of allTopics) {
|
|
226
|
+
if (isDeepObjectMatch(topic, storedTopic)) {
|
|
227
|
+
return storedTopic;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
addTopic(key, topic) {
|
|
232
|
+
// console.log(`wsStore.addTopic(${key}, ${topic})`);
|
|
233
|
+
// Check for duplicate topic. If already tracked, don't store this one
|
|
234
|
+
const existingTopic = this.getMatchingTopic(key, topic);
|
|
235
|
+
if (existingTopic) {
|
|
236
|
+
return this.getTopics(key);
|
|
237
|
+
}
|
|
238
|
+
return this.getTopics(key).add(topic);
|
|
239
|
+
}
|
|
240
|
+
deleteTopic(key, topic) {
|
|
241
|
+
// Check if we're subscribed to a topic like this
|
|
242
|
+
const storedTopic = this.getMatchingTopic(key, topic);
|
|
243
|
+
if (storedTopic) {
|
|
244
|
+
this.getTopics(key).delete(storedTopic);
|
|
245
|
+
}
|
|
246
|
+
return this.getTopics(key);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=WsStore.js.map
|