@@ -9,7 +9,7 @@ let WSClient = require("ws");
99/**
1010 * @typedef {Object } WsOpts
1111 * @prop {String } [baseUrl] - (deprecated by dashsocketBaseUrl) ex: https://insight.dash.org
12- * @prop {CookieStore } cookieStore - only needed for insight APIs hosted behind an AWS load balancer
12+ * @prop {import('../'). CookieStore } cookieStore - only needed for insight APIs hosted behind an AWS load balancer
1313 * @prop {Boolean } debug
1414 * @prop {Function } onClose
1515 * @prop {Function } onError
@@ -48,22 +48,19 @@ Ws.create = function ({
4848 let now = Date . now ( ) ;
4949 let sidUrl = `${ dashsocketBaseUrl } /?EIO=3&transport=polling&t=${ now } ` ;
5050
51- let cookies = await cookieStore . get ( sidUrl ) ;
52- let sidResp = await fetch ( sidUrl , {
51+ let cookiesStr = await cookieStore . get ( sidUrl ) ;
52+ let sidResp = await Ws . fetch ( sidUrl , {
5353 //agent: httpAgent,
5454 //@ts -ignore - request function is not typed correctly
5555 headers : {
56- Cookie : cookies ,
56+ Cookie : cookiesStr ,
5757 } ,
5858 } ) ;
59- if ( ! sidResp . ok ) {
60- console . error ( await sidResp . json ( ) ) ;
61- throw new Error ( "bad response" ) ;
62- }
59+
6360 await cookieStore . set ( sidUrl , sidResp ) ;
6461
6562 // ex: `97:0{"sid":"xxxx",...}`
66- let msg = await sidResp . json ( ) ;
63+ let msg = sidResp . body || "" ;
6764 let colonIndex = msg . indexOf ( ":" ) ;
6865 // 0 is CONNECT, which will always follow our first message
6966 let start = colonIndex + ":0" . length ;
@@ -95,24 +92,21 @@ Ws.create = function ({
9592 let len = msg . length ;
9693 let body = `${ len } :${ msg } ` ;
9794
98- let cookies = await cookieStore . get ( subUrl ) ;
99- let subResp = await fetch ( subUrl , {
95+ let cookiesStr = await cookieStore . get ( subUrl ) ;
96+ let subResp = await Ws . fetch ( subUrl , {
10097 //agent: httpAgent,
10198 method : "POST" ,
10299 headers : {
103100 "Content-Type" : "text/plain;charset=UTF-8" ,
104- Cookie : cookies ,
101+ Cookie : cookiesStr ,
105102 } ,
106103 body : body ,
107104 } ) ;
108- if ( ! subResp . ok ) {
109- console . error ( await subResp . json ( ) ) ;
110- throw new Error ( "bad response" ) ;
111- }
105+
112106 await cookieStore . set ( subUrl , subResp ) ;
113107
114108 // "ok"
115- return await subResp . json ( ) ;
109+ return subResp . body ;
116110 } ;
117111
118112 /*
@@ -147,14 +141,15 @@ Ws.create = function ({
147141 Eio3 . connectWs = async function ( sid ) {
148142 let dashsocketBaseUrlPart = dashsocketBaseUrl . slice ( 4 ) ; // trim leading 'http'
149143 let url = `ws${ dashsocketBaseUrlPart } /?EIO=3&transport=websocket&sid=${ sid } ` ;
144+ let sidUrl = `${ dashsocketBaseUrl } /` ;
150145
151- let cookies = await cookieStore . get ( ` ${ dashsocketBaseUrl } /` ) ;
146+ let cookiesStr = await cookieStore . get ( sidUrl ) ;
152147 let ws = new WSClient ( url , {
153148 //agent: httpAgent,
154149 //perMessageDeflate: false,
155150 //@ts -ignore - see above
156151 headers : {
157- Cookie : cookies ,
152+ Cookie : cookiesStr ,
158153 } ,
159154 } ) ;
160155
@@ -169,10 +164,25 @@ Ws.create = function ({
169164 ws . once ( "error" , function ( err ) {
170165 if ( onError ) {
171166 onError ( err ) ;
172- } else {
173- console . error ( "WebSocket Error:" ) ;
174- console . error ( err ) ;
167+ return ;
175168 }
169+
170+ console . error ( "WebSocket Error:" ) ;
171+ console . error ( err ) ;
172+ } ) ;
173+
174+ ws . once ( "unexpected-response" , function ( req , res ) {
175+ let err = new Error ( "unexpected-response" ) ;
176+ //@ts -ignore
177+ err . response = res ;
178+
179+ if ( onError ) {
180+ onError ( err ) ;
181+ return ;
182+ }
183+
184+ console . error ( "WebSocket Unexpected Response:" ) ;
185+ console . error ( err ) ;
176186 } ) ;
177187
178188 ws . once ( "message" , function message ( data ) {
@@ -261,7 +271,7 @@ Ws.create = function ({
261271 return ;
262272 }
263273
264- /** @type {InsightPush } */
274+ /** @type {import('../'). InsightPush } */
265275 let [ evname , data ] = JSON . parse ( msg . slice ( 2 ) ) ;
266276 if ( onMessage ) {
267277 onMessage ( evname , data ) ;
@@ -303,7 +313,7 @@ Ws.create = function ({
303313/**
304314 * @callback Finder
305315 * @param {String } evname
306- * @param {InsightSocketEventData } data
316+ * @param {import('../'). InsightSocketEventData } data
307317 */
308318
309319/**
@@ -359,8 +369,8 @@ Ws.listen = async function (dashsocketBaseUrl, find, opts) {
359369 * @param {String } addr
360370 * @param {Number } [amount]
361371 * @param {Number } [maxTxLockWait]
362- * @param {WsOpts } [opts]
363- * @returns {Promise<SocketPayment> }
372+ * @param {Partial< WsOpts> } [opts]
373+ * @returns {Promise<import('../'). SocketPayment> }
364374 */
365375Ws . waitForVout = async function (
366376 dashsocketBaseUrl ,
@@ -374,13 +384,13 @@ Ws.waitForVout = async function (
374384 }
375385
376386 // Listen for Response
377- /** @type SocketPayment */
387+ /** @type { import('../'). SocketPayment} */
378388 let mempoolTx ;
379389 return await Ws . listen ( dashsocketBaseUrl , findResponse , opts ) ;
380390
381391 /**
382392 * @param {String } evname
383- * @param {InsightSocketEventData } data
393+ * @param {import('../'). InsightSocketEventData } data
384394 */
385395 function findResponse ( evname , data ) {
386396 if ( ! [ "tx" , "txlock" ] . includes ( evname ) ) {
@@ -397,39 +407,116 @@ Ws.waitForVout = async function (
397407
398408 let result ;
399409 // TODO should fetch tx and match hotwallet as vin
400- data . vout . some ( function ( vout ) {
401- if ( ! ( addr in vout ) ) {
402- return false ;
403- }
404-
405- let duffs = vout [ addr ] ;
406- if ( amount && duffs !== amount ) {
407- return false ;
408- }
410+ data . vout . some (
411+ /** @param {Record<String,Number> } vout */
412+ function ( vout ) {
413+ if ( ! ( addr in vout ) ) {
414+ return false ;
415+ }
409416
410- let newTx = {
411- address : addr ,
412- timestamp : now ,
413- txid : data . txid ,
414- satoshis : duffs ,
415- txlock : data . txlock ,
416- } ;
417+ let duffs = vout [ addr ] ;
418+ if ( amount && duffs !== amount ) {
419+ return false ;
420+ }
417421
418- if ( "txlock" !== evname ) {
419- if ( ! mempoolTx ) {
420- mempoolTx = newTx ;
422+ let newTx = {
423+ address : addr ,
424+ timestamp : now ,
425+ txid : data . txid ,
426+ satoshis : duffs ,
427+ txlock : data . txlock ,
428+ } ;
429+
430+ if ( "txlock" !== evname ) {
431+ if ( ! mempoolTx ) {
432+ mempoolTx = newTx ;
433+ }
434+ return false ;
421435 }
422- return false ;
423- }
424436
425- result = newTx ;
426- return true ;
427- } ) ;
437+ result = newTx ;
438+ return true ;
439+ } ,
440+ ) ;
428441
429442 return result ;
430443 }
431444} ;
432445
446+ /** @type {RequestInit } */
447+ let defaultRequest = {
448+ mode : "cors" ,
449+ credentials : "include" ,
450+ } ;
451+
452+ /**
453+ * @param {String | URL | Request } url
454+ * @param {RequestInit } [_opts]
455+ */
456+ Ws . fetch = async function dashfetch ( url , _opts ) {
457+ let opts = Object . assign ( defaultRequest , _opts ) ;
458+
459+ let resp = await fetch ( url , opts ) ;
460+ /** @type {Record<String,String|Array<String>> } */
461+ let headers = { } ;
462+
463+ // for the Set-Cookie headers through AWS load balancer
464+ let headerEntries = resp . headers . entries ( ) ;
465+ for ( let [ key , value ] of headerEntries ) {
466+ if ( ! headers [ key ] ) {
467+ headers [ key ] = value ;
468+ continue ;
469+ }
470+
471+ let isArray = Array . isArray ( headers [ key ] ) ;
472+ if ( ! isArray ) {
473+ //@ts -ignore
474+ headers [ key ] = [ headers [ key ] ] ;
475+ }
476+ //@ts -ignore
477+ headers [ key ] . push ( value ) ;
478+ }
479+
480+ let body = await resp . text ( ) ;
481+
482+ let response = {
483+ ok : resp . ok ,
484+ statusCode : resp . status , // backwards compat
485+ statusText : resp . statusText ,
486+ headers : headers ,
487+ body : body ,
488+ toJSON : function ( ) {
489+ return {
490+ ok : response . ok ,
491+ statusCode : response . statusCode ,
492+ statusText : response . statusText ,
493+ headers : response . headers ,
494+ body : response . body ,
495+ } ;
496+ } ,
497+ get status ( ) {
498+ console . warn (
499+ "deprecated: please use either 'statusText' or 'statusCode' (node.js and browser both have 'status', but flipped)" ,
500+ ) ;
501+ return resp . statusText ;
502+ } ,
503+ _request : opts ,
504+ _response : resp ,
505+ } ;
506+
507+ if ( resp . ok ) {
508+ return response ;
509+ }
510+
511+ let err = new Error (
512+ `http request was ${ resp . status } , not ok. See err.response for details.` ,
513+ ) ;
514+
515+ // @ts -ignore
516+ err . response = response ;
517+ throw err ;
518+ } ;
519+
433520/*
434521async function sleep(ms) {
435522 return await new Promise(function (resolve) {
0 commit comments