diff --git a/.eslintignore b/.eslintignore index 0000ab1bf5..76816a18d2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,4 +7,3 @@ scripts/ stacks-blockchain/ src/schemas/* docs/ -client/ diff --git a/client/.eslintrc.js b/client/.eslintrc.js index c057511eda..4438bac70c 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -10,6 +10,7 @@ module.exports = { }, ignorePatterns: [ 'lib/*', + 'test/*' ], rules: { } diff --git a/client/package-lock.json b/client/package-lock.json index 6a04a991a3..c583befbc3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -83,16 +83,16 @@ } }, "@babel/core": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz", - "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz", + "integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.11.4", "@babel/helper-module-transforms": "^7.11.0", "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.1", + "@babel/parser": "^7.11.4", "@babel/template": "^7.10.4", "@babel/traverse": "^7.11.0", "@babel/types": "^7.11.0", @@ -111,32 +111,18 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true } } }, "@babel/generator": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", - "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", + "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", "dev": true, "requires": { "@babel/types": "^7.11.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/helper-annotate-as-pure": { @@ -237,12 +223,11 @@ } }, "@babel/helper-explode-assignable-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", - "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", + "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", "dev": true, "requires": { - "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" } }, @@ -333,15 +318,14 @@ } }, "@babel/helper-remap-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", - "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", + "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-wrap-function": "^7.10.4", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" } }, @@ -478,9 +462,9 @@ } }, "@babel/parser": { - "version": "7.11.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz", - "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==", + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", + "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -1928,6 +1912,14 @@ "dev": true, "requires": { "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "convert-source-map": { @@ -2075,12 +2067,6 @@ "supports-color": "^3.2.3" } }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -2137,6 +2123,14 @@ "requires": { "mdn-data": "2.0.4", "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "css-what": { @@ -2292,6 +2286,12 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==", "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -3962,6 +3962,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -4333,6 +4339,12 @@ "supports-color": "^5.4.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4406,6 +4418,12 @@ "supports-color": "^5.4.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4479,6 +4497,12 @@ "supports-color": "^5.4.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4552,6 +4576,12 @@ "supports-color": "^5.4.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -5361,9 +5391,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "source-map-support": { @@ -5374,6 +5404,14 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "sourcemap-codec": { @@ -5394,11 +5432,6 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, - "strict-event-emitter-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", - "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" - }, "string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", @@ -5624,6 +5657,14 @@ "commander": "^2.20.0", "source-map": "~0.6.1", "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "text-table": { diff --git a/client/package.json b/client/package.json index dd0ddeac1e..e7bb892d17 100644 --- a/client/package.json +++ b/client/package.json @@ -28,7 +28,6 @@ "cross-fetch": "^3.0.5", "eventemitter3": "^4.0.4", "jsonrpc-lite": "^2.1.1", - "strict-event-emitter-types": "^2.0.0", "ws": "^7.3.1" }, "devDependencies": { diff --git a/client/src/generated/runtime.ts b/client/src/generated/runtime.ts index d9a1d3c43f..466557fb3e 100644 --- a/client/src/generated/runtime.ts +++ b/client/src/generated/runtime.ts @@ -13,7 +13,7 @@ */ -export const BASE_PATH = "http://localhost:3999".replace(/\/+$/, ""); +export const BASE_PATH = "https://stacks-node-api-latest.argon.blockstack.xyz".replace(/\/+$/, ""); const isBlob = (value: any) => typeof Blob !== 'undefined' && value instanceof Blob; diff --git a/client/src/ws/index.ts b/client/src/ws/index.ts index c60de2e078..bc5b3e8cfe 100644 --- a/client/src/ws/index.ts +++ b/client/src/ws/index.ts @@ -1,6 +1,5 @@ import * as JsonRpcLite from 'jsonrpc-lite'; -import EventEmitter from 'eventemitter3'; -import StrictEventEmitter from 'strict-event-emitter-types'; +import { EventEmitter } from 'eventemitter3'; import { RpcTxUpdateSubscriptionParams, RpcTxUpdateNotificationParams, @@ -10,24 +9,15 @@ import { RpcAddressBalanceNotificationParams, RpcSubscriptionType, } from '@blockstack/stacks-blockchain-api-types'; +import { BASE_PATH } from '../generated/runtime'; type IWebSocket = import('ws') | WebSocket; -interface Events { - txUpdate: (event: RpcTxUpdateNotificationParams) => void; - addressTxUpdate: (event: RpcAddressTxNotificationParams) => void; - addressBalanceUpdate: (event: RpcAddressBalanceNotificationParams) => void; -} - interface Subscription { unsubscribe(): Promise; } -type StacksApiEventEmitter = StrictEventEmitter; - -export class StacksApiWebSocketClient extends (EventEmitter as { - new (): StacksApiEventEmitter; -}) { +export class StacksApiWebSocketClient { webSocket: IWebSocket; idCursor = 0; pendingRequests = new Map< @@ -35,8 +25,37 @@ export class StacksApiWebSocketClient extends (EventEmitter as { { resolve: (result: any) => void; reject: (error: any) => void } >(); + eventEmitter = new EventEmitter<{ + txUpdate: (event: RpcTxUpdateNotificationParams) => any; + addressTxUpdate: (event: RpcAddressTxNotificationParams) => void; + addressBalanceUpdate: (event: RpcAddressBalanceNotificationParams) => void; + }>(); + + public static async connect(url: string = BASE_PATH): Promise { + // `ws://${addr}/extended/v1/ws`; + let urlObj: URL; + try { + urlObj = new URL(url); + } catch (_error) { + urlObj = new URL(`ws://${url}`); + } + if (urlObj.protocol === 'https:') { + urlObj.protocol = 'wss:'; + } else if (urlObj.protocol === 'http:') { + urlObj.protocol = 'ws:'; + } + if (urlObj.pathname === '/') { + urlObj.pathname = '/extended/v1/ws'; + } + const webSocket = await new Promise((resolve, reject) => { + const webSocket = new (createWebSocket())(urlObj.toString()); + webSocket.onopen = () => resolve(webSocket); + webSocket.onerror = error => reject(error); + }); + return new StacksApiWebSocketClient(webSocket); + } + constructor(webSocket: IWebSocket) { - super(); this.webSocket = webSocket; (webSocket as WebSocket).addEventListener('message', event => { const parsed = JsonRpcLite.parse(event.data); @@ -65,13 +84,16 @@ export class StacksApiWebSocketClient extends (EventEmitter as { const method = data.method as RpcSubscriptionType; switch (method) { case 'tx_update': - this.emit('txUpdate', data.params as RpcTxUpdateNotificationParams); + this.eventEmitter.emit('txUpdate', data.params as RpcTxUpdateNotificationParams); break; case 'address_tx_update': - this.emit('addressTxUpdate', data.params as RpcAddressTxNotificationParams); + this.eventEmitter.emit('addressTxUpdate', data.params as RpcAddressTxNotificationParams); break; case 'address_balance_update': - this.emit('addressBalanceUpdate', data.params as RpcAddressBalanceNotificationParams); + this.eventEmitter.emit( + 'addressBalanceUpdate', + data.params as RpcAddressBalanceNotificationParams + ); break; } } @@ -95,10 +117,10 @@ export class StacksApiWebSocketClient extends (EventEmitter as { update(event); } }; - this.addListener('txUpdate', listener); + this.eventEmitter.addListener('txUpdate', listener); return { unsubscribe: () => { - this.removeListener('txUpdate', listener); + this.eventEmitter.removeListener('txUpdate', listener); return this.rpcCall('unsubscribe', params); }, }; @@ -115,10 +137,10 @@ export class StacksApiWebSocketClient extends (EventEmitter as { update(event); } }; - this.addListener('addressTxUpdate', listener); + this.eventEmitter.addListener('addressTxUpdate', listener); return { unsubscribe: () => { - this.removeListener('addressTxUpdate', listener); + this.eventEmitter.removeListener('addressTxUpdate', listener); return this.rpcCall('unsubscribe', params); }, }; @@ -138,23 +160,20 @@ export class StacksApiWebSocketClient extends (EventEmitter as { update(event); } }; - this.addListener('addressBalanceUpdate', listener); + this.eventEmitter.addListener('addressBalanceUpdate', listener); return { unsubscribe: () => { - this.removeListener('addressBalanceUpdate', listener); + this.eventEmitter.removeListener('addressBalanceUpdate', listener); return this.rpcCall('unsubscribe', params); }, }; } } -export async function connect(url: string): Promise { - const webSocket = await new Promise((resolve, reject) => { - const webSocket = new (createWebSocket())(url); - webSocket.onopen = () => resolve(webSocket); - webSocket.onerror = error => reject(error); - }); - return new StacksApiWebSocketClient(webSocket); +export async function connectWebSocketClient( + url: string = BASE_PATH +): Promise { + return StacksApiWebSocketClient.connect(url); } /** diff --git a/client/tsconfig.json b/client/tsconfig.json index 04c423d16c..f0604a72aa 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -7,7 +7,6 @@ "sourceMap": true, "strict": true, "outDir": "lib", - "esModuleInterop": true, "baseUrl": ".", "paths": { "@blockstack/stacks-blockchain-api-types": ["../docs"] diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 49dd61caff..07300592c8 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,9 +1,9 @@ openapi: 3.0.2 servers: - - url: http://localhost:3999/ - description: Local - url: https://stacks-node-api-latest.argon.blockstack.xyz/ description: Testnet (Argon) + - url: http://localhost:3999/ + description: Local info: title: Stacks 2.0 Blockchain API version: '1.0.0' diff --git a/package-lock.json b/package-lock.json index 9310b61499..78319cc0a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10182,7 +10182,6 @@ "cross-fetch": "^3.0.5", "eventemitter3": "^4.0.4", "jsonrpc-lite": "^2.1.1", - "strict-event-emitter-types": "^2.0.0", "ws": "^7.3.1" }, "dependencies": { @@ -14988,8 +14987,7 @@ "strict-event-emitter-types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", - "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==", - "dev": true + "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" }, "string-hash": { "version": "1.1.3", diff --git a/src/tests/websocket-tests.ts b/src/tests/websocket-tests.ts index d05991f983..2777b2a9d4 100644 --- a/src/tests/websocket-tests.ts +++ b/src/tests/websocket-tests.ts @@ -26,7 +26,7 @@ import { RpcAddressBalanceNotificationParams, TransactionStatus, } from '@blockstack/stacks-blockchain-api-types'; -import { connect as connectWebSocketClient } from '@stacks/blockchain-api-client'; +import { connectWebSocketClient } from '@stacks/blockchain-api-client'; describe('websocket notifications', () => { let apiServer: ApiServer; diff --git a/tsconfig.json b/tsconfig.json index 4045cba93a..39e3b7b8c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "allowSyntheticDefaultImports": false, "resolveJsonModule": true, "baseUrl": ".", - "skipLibCheck": true, + "skipLibCheck": false, "paths": { "*": ["src/@types/*"] }