signalr.js 136 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190
  1. (function webpackUniversalModuleDefinition(root, factory) {
  2. if(typeof exports === 'object' && typeof module === 'object')
  3. module.exports = factory();
  4. else if(typeof define === 'function' && define.amd)
  5. define([], factory);
  6. else if(typeof exports === 'object')
  7. exports["signalR"] = factory();
  8. else
  9. root["signalR"] = factory();
  10. })(self, () => {
  11. return /******/ (() => { // webpackBootstrap
  12. /******/ "use strict";
  13. /******/ // The require scope
  14. /******/ var __webpack_require__ = {};
  15. /******/
  16. /************************************************************************/
  17. /******/ /* webpack/runtime/define property getters */
  18. /******/ (() => {
  19. /******/ // define getter functions for harmony exports
  20. /******/ __webpack_require__.d = (exports, definition) => {
  21. /******/ for(var key in definition) {
  22. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  23. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  24. /******/ }
  25. /******/ }
  26. /******/ };
  27. /******/ })();
  28. /******/
  29. /******/ /* webpack/runtime/global */
  30. /******/ (() => {
  31. /******/ __webpack_require__.g = (function() {
  32. /******/ if (typeof globalThis === 'object') return globalThis;
  33. /******/ try {
  34. /******/ return this || new Function('return this')();
  35. /******/ } catch (e) {
  36. /******/ if (typeof window === 'object') return window;
  37. /******/ }
  38. /******/ })();
  39. /******/ })();
  40. /******/
  41. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  42. /******/ (() => {
  43. /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
  44. /******/ })();
  45. /******/
  46. /******/ /* webpack/runtime/make namespace object */
  47. /******/ (() => {
  48. /******/ // define __esModule on exports
  49. /******/ __webpack_require__.r = (exports) => {
  50. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  51. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  52. /******/ }
  53. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  54. /******/ };
  55. /******/ })();
  56. /******/
  57. /************************************************************************/
  58. var __webpack_exports__ = {};
  59. // ESM COMPAT FLAG
  60. __webpack_require__.r(__webpack_exports__);
  61. // EXPORTS
  62. __webpack_require__.d(__webpack_exports__, {
  63. "AbortError": () => (/* reexport */ AbortError),
  64. "DefaultHttpClient": () => (/* reexport */ DefaultHttpClient),
  65. "HttpClient": () => (/* reexport */ HttpClient),
  66. "HttpError": () => (/* reexport */ HttpError),
  67. "HttpResponse": () => (/* reexport */ HttpResponse),
  68. "HttpTransportType": () => (/* reexport */ HttpTransportType),
  69. "HubConnection": () => (/* reexport */ HubConnection),
  70. "HubConnectionBuilder": () => (/* reexport */ HubConnectionBuilder),
  71. "HubConnectionState": () => (/* reexport */ HubConnectionState),
  72. "JsonHubProtocol": () => (/* reexport */ JsonHubProtocol),
  73. "LogLevel": () => (/* reexport */ LogLevel),
  74. "MessageType": () => (/* reexport */ MessageType),
  75. "NullLogger": () => (/* reexport */ NullLogger),
  76. "Subject": () => (/* reexport */ Subject),
  77. "TimeoutError": () => (/* reexport */ TimeoutError),
  78. "TransferFormat": () => (/* reexport */ TransferFormat),
  79. "VERSION": () => (/* reexport */ VERSION)
  80. });
  81. ;// CONCATENATED MODULE: ./src/Errors.ts
  82. // Licensed to the .NET Foundation under one or more agreements.
  83. // The .NET Foundation licenses this file to you under the MIT license.
  84. /** Error thrown when an HTTP request fails. */
  85. class HttpError extends Error {
  86. /** Constructs a new instance of {@link @microsoft/signalr.HttpError}.
  87. *
  88. * @param {string} errorMessage A descriptive error message.
  89. * @param {number} statusCode The HTTP status code represented by this error.
  90. */
  91. constructor(errorMessage, statusCode) {
  92. const trueProto = new.target.prototype;
  93. super(`${errorMessage}: Status code '${statusCode}'`);
  94. this.statusCode = statusCode;
  95. // Workaround issue in Typescript compiler
  96. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  97. this.__proto__ = trueProto;
  98. }
  99. }
  100. /** Error thrown when a timeout elapses. */
  101. class TimeoutError extends Error {
  102. /** Constructs a new instance of {@link @microsoft/signalr.TimeoutError}.
  103. *
  104. * @param {string} errorMessage A descriptive error message.
  105. */
  106. constructor(errorMessage = "A timeout occurred.") {
  107. const trueProto = new.target.prototype;
  108. super(errorMessage);
  109. // Workaround issue in Typescript compiler
  110. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  111. this.__proto__ = trueProto;
  112. }
  113. }
  114. /** Error thrown when an action is aborted. */
  115. class AbortError extends Error {
  116. /** Constructs a new instance of {@link AbortError}.
  117. *
  118. * @param {string} errorMessage A descriptive error message.
  119. */
  120. constructor(errorMessage = "An abort occurred.") {
  121. const trueProto = new.target.prototype;
  122. super(errorMessage);
  123. // Workaround issue in Typescript compiler
  124. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  125. this.__proto__ = trueProto;
  126. }
  127. }
  128. /** Error thrown when the selected transport is unsupported by the browser. */
  129. /** @private */
  130. class UnsupportedTransportError extends Error {
  131. /** Constructs a new instance of {@link @microsoft/signalr.UnsupportedTransportError}.
  132. *
  133. * @param {string} message A descriptive error message.
  134. * @param {HttpTransportType} transport The {@link @microsoft/signalr.HttpTransportType} this error occurred on.
  135. */
  136. constructor(message, transport) {
  137. const trueProto = new.target.prototype;
  138. super(message);
  139. this.transport = transport;
  140. this.errorType = 'UnsupportedTransportError';
  141. // Workaround issue in Typescript compiler
  142. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  143. this.__proto__ = trueProto;
  144. }
  145. }
  146. /** Error thrown when the selected transport is disabled by the browser. */
  147. /** @private */
  148. class DisabledTransportError extends Error {
  149. /** Constructs a new instance of {@link @microsoft/signalr.DisabledTransportError}.
  150. *
  151. * @param {string} message A descriptive error message.
  152. * @param {HttpTransportType} transport The {@link @microsoft/signalr.HttpTransportType} this error occurred on.
  153. */
  154. constructor(message, transport) {
  155. const trueProto = new.target.prototype;
  156. super(message);
  157. this.transport = transport;
  158. this.errorType = 'DisabledTransportError';
  159. // Workaround issue in Typescript compiler
  160. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  161. this.__proto__ = trueProto;
  162. }
  163. }
  164. /** Error thrown when the selected transport cannot be started. */
  165. /** @private */
  166. class FailedToStartTransportError extends Error {
  167. /** Constructs a new instance of {@link @microsoft/signalr.FailedToStartTransportError}.
  168. *
  169. * @param {string} message A descriptive error message.
  170. * @param {HttpTransportType} transport The {@link @microsoft/signalr.HttpTransportType} this error occurred on.
  171. */
  172. constructor(message, transport) {
  173. const trueProto = new.target.prototype;
  174. super(message);
  175. this.transport = transport;
  176. this.errorType = 'FailedToStartTransportError';
  177. // Workaround issue in Typescript compiler
  178. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  179. this.__proto__ = trueProto;
  180. }
  181. }
  182. /** Error thrown when the negotiation with the server failed to complete. */
  183. /** @private */
  184. class FailedToNegotiateWithServerError extends Error {
  185. /** Constructs a new instance of {@link @microsoft/signalr.FailedToNegotiateWithServerError}.
  186. *
  187. * @param {string} message A descriptive error message.
  188. */
  189. constructor(message) {
  190. const trueProto = new.target.prototype;
  191. super(message);
  192. this.errorType = 'FailedToNegotiateWithServerError';
  193. // Workaround issue in Typescript compiler
  194. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  195. this.__proto__ = trueProto;
  196. }
  197. }
  198. /** Error thrown when multiple errors have occurred. */
  199. /** @private */
  200. class AggregateErrors extends Error {
  201. /** Constructs a new instance of {@link @microsoft/signalr.AggregateErrors}.
  202. *
  203. * @param {string} message A descriptive error message.
  204. * @param {Error[]} innerErrors The collection of errors this error is aggregating.
  205. */
  206. constructor(message, innerErrors) {
  207. const trueProto = new.target.prototype;
  208. super(message);
  209. this.innerErrors = innerErrors;
  210. // Workaround issue in Typescript compiler
  211. // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
  212. this.__proto__ = trueProto;
  213. }
  214. }
  215. ;// CONCATENATED MODULE: ./src/HttpClient.ts
  216. // Licensed to the .NET Foundation under one or more agreements.
  217. // The .NET Foundation licenses this file to you under the MIT license.
  218. /** Represents an HTTP response. */
  219. class HttpResponse {
  220. constructor(statusCode, statusText, content) {
  221. this.statusCode = statusCode;
  222. this.statusText = statusText;
  223. this.content = content;
  224. }
  225. }
  226. /** Abstraction over an HTTP client.
  227. *
  228. * This class provides an abstraction over an HTTP client so that a different implementation can be provided on different platforms.
  229. */
  230. class HttpClient {
  231. get(url, options) {
  232. return this.send({
  233. ...options,
  234. method: "GET",
  235. url,
  236. });
  237. }
  238. post(url, options) {
  239. return this.send({
  240. ...options,
  241. method: "POST",
  242. url,
  243. });
  244. }
  245. delete(url, options) {
  246. return this.send({
  247. ...options,
  248. method: "DELETE",
  249. url,
  250. });
  251. }
  252. /** Gets all cookies that apply to the specified URL.
  253. *
  254. * @param url The URL that the cookies are valid for.
  255. * @returns {string} A string containing all the key-value cookie pairs for the specified URL.
  256. */
  257. // @ts-ignore
  258. getCookieString(url) {
  259. return "";
  260. }
  261. }
  262. ;// CONCATENATED MODULE: ./src/ILogger.ts
  263. // Licensed to the .NET Foundation under one or more agreements.
  264. // The .NET Foundation licenses this file to you under the MIT license.
  265. // These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here.
  266. /** Indicates the severity of a log message.
  267. *
  268. * Log Levels are ordered in increasing severity. So `Debug` is more severe than `Trace`, etc.
  269. */
  270. var LogLevel;
  271. (function (LogLevel) {
  272. /** Log level for very low severity diagnostic messages. */
  273. LogLevel[LogLevel["Trace"] = 0] = "Trace";
  274. /** Log level for low severity diagnostic messages. */
  275. LogLevel[LogLevel["Debug"] = 1] = "Debug";
  276. /** Log level for informational diagnostic messages. */
  277. LogLevel[LogLevel["Information"] = 2] = "Information";
  278. /** Log level for diagnostic messages that indicate a non-fatal problem. */
  279. LogLevel[LogLevel["Warning"] = 3] = "Warning";
  280. /** Log level for diagnostic messages that indicate a failure in the current operation. */
  281. LogLevel[LogLevel["Error"] = 4] = "Error";
  282. /** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */
  283. LogLevel[LogLevel["Critical"] = 5] = "Critical";
  284. /** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */
  285. LogLevel[LogLevel["None"] = 6] = "None";
  286. })(LogLevel || (LogLevel = {}));
  287. ;// CONCATENATED MODULE: ./src/Loggers.ts
  288. // Licensed to the .NET Foundation under one or more agreements.
  289. // The .NET Foundation licenses this file to you under the MIT license.
  290. /** A logger that does nothing when log messages are sent to it. */
  291. class NullLogger {
  292. constructor() { }
  293. /** @inheritDoc */
  294. // eslint-disable-next-line
  295. log(_logLevel, _message) {
  296. }
  297. }
  298. /** The singleton instance of the {@link @microsoft/signalr.NullLogger}. */
  299. NullLogger.instance = new NullLogger();
  300. ;// CONCATENATED MODULE: ./src/Utils.ts
  301. // Licensed to the .NET Foundation under one or more agreements.
  302. // The .NET Foundation licenses this file to you under the MIT license.
  303. // Version token that will be replaced by the prepack command
  304. /** The version of the SignalR client. */
  305. const VERSION = "7.0.14";
  306. /** @private */
  307. class Arg {
  308. static isRequired(val, name) {
  309. if (val === null || val === undefined) {
  310. throw new Error(`The '${name}' argument is required.`);
  311. }
  312. }
  313. static isNotEmpty(val, name) {
  314. if (!val || val.match(/^\s*$/)) {
  315. throw new Error(`The '${name}' argument should not be empty.`);
  316. }
  317. }
  318. static isIn(val, values, name) {
  319. // TypeScript enums have keys for **both** the name and the value of each enum member on the type itself.
  320. if (!(val in values)) {
  321. throw new Error(`Unknown ${name} value: ${val}.`);
  322. }
  323. }
  324. }
  325. /** @private */
  326. class Platform {
  327. // react-native has a window but no document so we should check both
  328. static get isBrowser() {
  329. return typeof window === "object" && typeof window.document === "object";
  330. }
  331. // WebWorkers don't have a window object so the isBrowser check would fail
  332. static get isWebWorker() {
  333. return typeof self === "object" && "importScripts" in self;
  334. }
  335. // react-native has a window but no document
  336. static get isReactNative() {
  337. return typeof window === "object" && typeof window.document === "undefined";
  338. }
  339. // Node apps shouldn't have a window object, but WebWorkers don't either
  340. // so we need to check for both WebWorker and window
  341. static get isNode() {
  342. return !this.isBrowser && !this.isWebWorker && !this.isReactNative;
  343. }
  344. }
  345. /** @private */
  346. function getDataDetail(data, includeContent) {
  347. let detail = "";
  348. if (isArrayBuffer(data)) {
  349. detail = `Binary data of length ${data.byteLength}`;
  350. if (includeContent) {
  351. detail += `. Content: '${formatArrayBuffer(data)}'`;
  352. }
  353. }
  354. else if (typeof data === "string") {
  355. detail = `String data of length ${data.length}`;
  356. if (includeContent) {
  357. detail += `. Content: '${data}'`;
  358. }
  359. }
  360. return detail;
  361. }
  362. /** @private */
  363. function formatArrayBuffer(data) {
  364. const view = new Uint8Array(data);
  365. // Uint8Array.map only supports returning another Uint8Array?
  366. let str = "";
  367. view.forEach((num) => {
  368. const pad = num < 16 ? "0" : "";
  369. str += `0x${pad}${num.toString(16)} `;
  370. });
  371. // Trim of trailing space.
  372. return str.substr(0, str.length - 1);
  373. }
  374. // Also in signalr-protocol-msgpack/Utils.ts
  375. /** @private */
  376. function isArrayBuffer(val) {
  377. return val && typeof ArrayBuffer !== "undefined" &&
  378. (val instanceof ArrayBuffer ||
  379. // Sometimes we get an ArrayBuffer that doesn't satisfy instanceof
  380. (val.constructor && val.constructor.name === "ArrayBuffer"));
  381. }
  382. /** @private */
  383. async function sendMessage(logger, transportName, httpClient, url, content, options) {
  384. const headers = {};
  385. const [name, value] = getUserAgentHeader();
  386. headers[name] = value;
  387. logger.log(LogLevel.Trace, `(${transportName} transport) sending data. ${getDataDetail(content, options.logMessageContent)}.`);
  388. const responseType = isArrayBuffer(content) ? "arraybuffer" : "text";
  389. const response = await httpClient.post(url, {
  390. content,
  391. headers: { ...headers, ...options.headers },
  392. responseType,
  393. timeout: options.timeout,
  394. withCredentials: options.withCredentials,
  395. });
  396. logger.log(LogLevel.Trace, `(${transportName} transport) request complete. Response status: ${response.statusCode}.`);
  397. }
  398. /** @private */
  399. function createLogger(logger) {
  400. if (logger === undefined) {
  401. return new ConsoleLogger(LogLevel.Information);
  402. }
  403. if (logger === null) {
  404. return NullLogger.instance;
  405. }
  406. if (logger.log !== undefined) {
  407. return logger;
  408. }
  409. return new ConsoleLogger(logger);
  410. }
  411. /** @private */
  412. class SubjectSubscription {
  413. constructor(subject, observer) {
  414. this._subject = subject;
  415. this._observer = observer;
  416. }
  417. dispose() {
  418. const index = this._subject.observers.indexOf(this._observer);
  419. if (index > -1) {
  420. this._subject.observers.splice(index, 1);
  421. }
  422. if (this._subject.observers.length === 0 && this._subject.cancelCallback) {
  423. this._subject.cancelCallback().catch((_) => { });
  424. }
  425. }
  426. }
  427. /** @private */
  428. class ConsoleLogger {
  429. constructor(minimumLogLevel) {
  430. this._minLevel = minimumLogLevel;
  431. this.out = console;
  432. }
  433. log(logLevel, message) {
  434. if (logLevel >= this._minLevel) {
  435. const msg = `[${new Date().toISOString()}] ${LogLevel[logLevel]}: ${message}`;
  436. switch (logLevel) {
  437. case LogLevel.Critical:
  438. case LogLevel.Error:
  439. this.out.error(msg);
  440. break;
  441. case LogLevel.Warning:
  442. this.out.warn(msg);
  443. break;
  444. case LogLevel.Information:
  445. this.out.info(msg);
  446. break;
  447. default:
  448. // console.debug only goes to attached debuggers in Node, so we use console.log for Trace and Debug
  449. this.out.log(msg);
  450. break;
  451. }
  452. }
  453. }
  454. }
  455. /** @private */
  456. function getUserAgentHeader() {
  457. let userAgentHeaderName = "X-SignalR-User-Agent";
  458. if (Platform.isNode) {
  459. userAgentHeaderName = "User-Agent";
  460. }
  461. return [userAgentHeaderName, constructUserAgent(VERSION, getOsName(), getRuntime(), getRuntimeVersion())];
  462. }
  463. /** @private */
  464. function constructUserAgent(version, os, runtime, runtimeVersion) {
  465. // Microsoft SignalR/[Version] ([Detailed Version]; [Operating System]; [Runtime]; [Runtime Version])
  466. let userAgent = "Microsoft SignalR/";
  467. const majorAndMinor = version.split(".");
  468. userAgent += `${majorAndMinor[0]}.${majorAndMinor[1]}`;
  469. userAgent += ` (${version}; `;
  470. if (os && os !== "") {
  471. userAgent += `${os}; `;
  472. }
  473. else {
  474. userAgent += "Unknown OS; ";
  475. }
  476. userAgent += `${runtime}`;
  477. if (runtimeVersion) {
  478. userAgent += `; ${runtimeVersion}`;
  479. }
  480. else {
  481. userAgent += "; Unknown Runtime Version";
  482. }
  483. userAgent += ")";
  484. return userAgent;
  485. }
  486. // eslint-disable-next-line spaced-comment
  487. /*#__PURE__*/ function getOsName() {
  488. if (Platform.isNode) {
  489. switch (process.platform) {
  490. case "win32":
  491. return "Windows NT";
  492. case "darwin":
  493. return "macOS";
  494. case "linux":
  495. return "Linux";
  496. default:
  497. return process.platform;
  498. }
  499. }
  500. else {
  501. return "";
  502. }
  503. }
  504. // eslint-disable-next-line spaced-comment
  505. /*#__PURE__*/ function getRuntimeVersion() {
  506. if (Platform.isNode) {
  507. return process.versions.node;
  508. }
  509. return undefined;
  510. }
  511. function getRuntime() {
  512. if (Platform.isNode) {
  513. return "NodeJS";
  514. }
  515. else {
  516. return "Browser";
  517. }
  518. }
  519. /** @private */
  520. function getErrorString(e) {
  521. if (e.stack) {
  522. return e.stack;
  523. }
  524. else if (e.message) {
  525. return e.message;
  526. }
  527. return `${e}`;
  528. }
  529. /** @private */
  530. function getGlobalThis() {
  531. // globalThis is semi-new and not available in Node until v12
  532. if (typeof globalThis !== "undefined") {
  533. return globalThis;
  534. }
  535. if (typeof self !== "undefined") {
  536. return self;
  537. }
  538. if (typeof window !== "undefined") {
  539. return window;
  540. }
  541. if (typeof __webpack_require__.g !== "undefined") {
  542. return __webpack_require__.g;
  543. }
  544. throw new Error("could not find global");
  545. }
  546. ;// CONCATENATED MODULE: ./src/FetchHttpClient.ts
  547. // Licensed to the .NET Foundation under one or more agreements.
  548. // The .NET Foundation licenses this file to you under the MIT license.
  549. class FetchHttpClient extends HttpClient {
  550. constructor(logger) {
  551. super();
  552. this._logger = logger;
  553. if (typeof fetch === "undefined") {
  554. // In order to ignore the dynamic require in webpack builds we need to do this magic
  555. // @ts-ignore: TS doesn't know about these names
  556. const requireFunc = true ? require : 0;
  557. // Cookies aren't automatically handled in Node so we need to add a CookieJar to preserve cookies across requests
  558. this._jar = new (requireFunc("tough-cookie")).CookieJar();
  559. this._fetchType = requireFunc("node-fetch");
  560. // node-fetch doesn't have a nice API for getting and setting cookies
  561. // fetch-cookie will wrap a fetch implementation with a default CookieJar or a provided one
  562. this._fetchType = requireFunc("fetch-cookie")(this._fetchType, this._jar);
  563. }
  564. else {
  565. this._fetchType = fetch.bind(getGlobalThis());
  566. }
  567. if (typeof AbortController === "undefined") {
  568. // In order to ignore the dynamic require in webpack builds we need to do this magic
  569. // @ts-ignore: TS doesn't know about these names
  570. const requireFunc = true ? require : 0;
  571. // Node needs EventListener methods on AbortController which our custom polyfill doesn't provide
  572. this._abortControllerType = requireFunc("abort-controller");
  573. }
  574. else {
  575. this._abortControllerType = AbortController;
  576. }
  577. }
  578. /** @inheritDoc */
  579. async send(request) {
  580. // Check that abort was not signaled before calling send
  581. if (request.abortSignal && request.abortSignal.aborted) {
  582. throw new AbortError();
  583. }
  584. if (!request.method) {
  585. throw new Error("No method defined.");
  586. }
  587. if (!request.url) {
  588. throw new Error("No url defined.");
  589. }
  590. const abortController = new this._abortControllerType();
  591. let error;
  592. // Hook our abortSignal into the abort controller
  593. if (request.abortSignal) {
  594. request.abortSignal.onabort = () => {
  595. abortController.abort();
  596. error = new AbortError();
  597. };
  598. }
  599. // If a timeout has been passed in, setup a timeout to call abort
  600. // Type needs to be any to fit window.setTimeout and NodeJS.setTimeout
  601. let timeoutId = null;
  602. if (request.timeout) {
  603. const msTimeout = request.timeout;
  604. timeoutId = setTimeout(() => {
  605. abortController.abort();
  606. this._logger.log(LogLevel.Warning, `Timeout from HTTP request.`);
  607. error = new TimeoutError();
  608. }, msTimeout);
  609. }
  610. if (request.content === "") {
  611. request.content = undefined;
  612. }
  613. if (request.content) {
  614. // Explicitly setting the Content-Type header for React Native on Android platform.
  615. request.headers = request.headers || {};
  616. if (isArrayBuffer(request.content)) {
  617. request.headers["Content-Type"] = "application/octet-stream";
  618. }
  619. else {
  620. request.headers["Content-Type"] = "text/plain;charset=UTF-8";
  621. }
  622. }
  623. let response;
  624. try {
  625. response = await this._fetchType(request.url, {
  626. body: request.content,
  627. cache: "no-cache",
  628. credentials: request.withCredentials === true ? "include" : "same-origin",
  629. headers: {
  630. "X-Requested-With": "XMLHttpRequest",
  631. ...request.headers,
  632. },
  633. method: request.method,
  634. mode: "cors",
  635. redirect: "follow",
  636. signal: abortController.signal,
  637. });
  638. }
  639. catch (e) {
  640. if (error) {
  641. throw error;
  642. }
  643. this._logger.log(LogLevel.Warning, `Error from HTTP request. ${e}.`);
  644. throw e;
  645. }
  646. finally {
  647. if (timeoutId) {
  648. clearTimeout(timeoutId);
  649. }
  650. if (request.abortSignal) {
  651. request.abortSignal.onabort = null;
  652. }
  653. }
  654. if (!response.ok) {
  655. const errorMessage = await deserializeContent(response, "text");
  656. throw new HttpError(errorMessage || response.statusText, response.status);
  657. }
  658. const content = deserializeContent(response, request.responseType);
  659. const payload = await content;
  660. return new HttpResponse(response.status, response.statusText, payload);
  661. }
  662. getCookieString(url) {
  663. let cookies = "";
  664. if (Platform.isNode && this._jar) {
  665. // @ts-ignore: unused variable
  666. this._jar.getCookies(url, (e, c) => cookies = c.join("; "));
  667. }
  668. return cookies;
  669. }
  670. }
  671. function deserializeContent(response, responseType) {
  672. let content;
  673. switch (responseType) {
  674. case "arraybuffer":
  675. content = response.arrayBuffer();
  676. break;
  677. case "text":
  678. content = response.text();
  679. break;
  680. case "blob":
  681. case "document":
  682. case "json":
  683. throw new Error(`${responseType} is not supported.`);
  684. default:
  685. content = response.text();
  686. break;
  687. }
  688. return content;
  689. }
  690. ;// CONCATENATED MODULE: ./src/XhrHttpClient.ts
  691. // Licensed to the .NET Foundation under one or more agreements.
  692. // The .NET Foundation licenses this file to you under the MIT license.
  693. class XhrHttpClient extends HttpClient {
  694. constructor(logger) {
  695. super();
  696. this._logger = logger;
  697. }
  698. /** @inheritDoc */
  699. send(request) {
  700. // Check that abort was not signaled before calling send
  701. if (request.abortSignal && request.abortSignal.aborted) {
  702. return Promise.reject(new AbortError());
  703. }
  704. if (!request.method) {
  705. return Promise.reject(new Error("No method defined."));
  706. }
  707. if (!request.url) {
  708. return Promise.reject(new Error("No url defined."));
  709. }
  710. return new Promise((resolve, reject) => {
  711. const xhr = new XMLHttpRequest();
  712. xhr.open(request.method, request.url, true);
  713. xhr.withCredentials = request.withCredentials === undefined ? true : request.withCredentials;
  714. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  715. if (request.content === "") {
  716. request.content = undefined;
  717. }
  718. if (request.content) {
  719. // Explicitly setting the Content-Type header for React Native on Android platform.
  720. if (isArrayBuffer(request.content)) {
  721. xhr.setRequestHeader("Content-Type", "application/octet-stream");
  722. }
  723. else {
  724. xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
  725. }
  726. }
  727. const headers = request.headers;
  728. if (headers) {
  729. Object.keys(headers)
  730. .forEach((header) => {
  731. xhr.setRequestHeader(header, headers[header]);
  732. });
  733. }
  734. if (request.responseType) {
  735. xhr.responseType = request.responseType;
  736. }
  737. if (request.abortSignal) {
  738. request.abortSignal.onabort = () => {
  739. xhr.abort();
  740. reject(new AbortError());
  741. };
  742. }
  743. if (request.timeout) {
  744. xhr.timeout = request.timeout;
  745. }
  746. xhr.onload = () => {
  747. if (request.abortSignal) {
  748. request.abortSignal.onabort = null;
  749. }
  750. if (xhr.status >= 200 && xhr.status < 300) {
  751. resolve(new HttpResponse(xhr.status, xhr.statusText, xhr.response || xhr.responseText));
  752. }
  753. else {
  754. reject(new HttpError(xhr.response || xhr.responseText || xhr.statusText, xhr.status));
  755. }
  756. };
  757. xhr.onerror = () => {
  758. this._logger.log(LogLevel.Warning, `Error from HTTP request. ${xhr.status}: ${xhr.statusText}.`);
  759. reject(new HttpError(xhr.statusText, xhr.status));
  760. };
  761. xhr.ontimeout = () => {
  762. this._logger.log(LogLevel.Warning, `Timeout from HTTP request.`);
  763. reject(new TimeoutError());
  764. };
  765. xhr.send(request.content);
  766. });
  767. }
  768. }
  769. ;// CONCATENATED MODULE: ./src/DefaultHttpClient.ts
  770. // Licensed to the .NET Foundation under one or more agreements.
  771. // The .NET Foundation licenses this file to you under the MIT license.
  772. /** Default implementation of {@link @microsoft/signalr.HttpClient}. */
  773. class DefaultHttpClient extends HttpClient {
  774. /** Creates a new instance of the {@link @microsoft/signalr.DefaultHttpClient}, using the provided {@link @microsoft/signalr.ILogger} to log messages. */
  775. constructor(logger) {
  776. super();
  777. if (typeof fetch !== "undefined" || Platform.isNode) {
  778. this._httpClient = new FetchHttpClient(logger);
  779. }
  780. else if (typeof XMLHttpRequest !== "undefined") {
  781. this._httpClient = new XhrHttpClient(logger);
  782. }
  783. else {
  784. throw new Error("No usable HttpClient found.");
  785. }
  786. }
  787. /** @inheritDoc */
  788. send(request) {
  789. // Check that abort was not signaled before calling send
  790. if (request.abortSignal && request.abortSignal.aborted) {
  791. return Promise.reject(new AbortError());
  792. }
  793. if (!request.method) {
  794. return Promise.reject(new Error("No method defined."));
  795. }
  796. if (!request.url) {
  797. return Promise.reject(new Error("No url defined."));
  798. }
  799. return this._httpClient.send(request);
  800. }
  801. getCookieString(url) {
  802. return this._httpClient.getCookieString(url);
  803. }
  804. }
  805. ;// CONCATENATED MODULE: ./src/TextMessageFormat.ts
  806. // Licensed to the .NET Foundation under one or more agreements.
  807. // The .NET Foundation licenses this file to you under the MIT license.
  808. // Not exported from index
  809. /** @private */
  810. class TextMessageFormat {
  811. static write(output) {
  812. return `${output}${TextMessageFormat.RecordSeparator}`;
  813. }
  814. static parse(input) {
  815. if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) {
  816. throw new Error("Message is incomplete.");
  817. }
  818. const messages = input.split(TextMessageFormat.RecordSeparator);
  819. messages.pop();
  820. return messages;
  821. }
  822. }
  823. TextMessageFormat.RecordSeparatorCode = 0x1e;
  824. TextMessageFormat.RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);
  825. ;// CONCATENATED MODULE: ./src/HandshakeProtocol.ts
  826. // Licensed to the .NET Foundation under one or more agreements.
  827. // The .NET Foundation licenses this file to you under the MIT license.
  828. /** @private */
  829. class HandshakeProtocol {
  830. // Handshake request is always JSON
  831. writeHandshakeRequest(handshakeRequest) {
  832. return TextMessageFormat.write(JSON.stringify(handshakeRequest));
  833. }
  834. parseHandshakeResponse(data) {
  835. let messageData;
  836. let remainingData;
  837. if (isArrayBuffer(data)) {
  838. // Format is binary but still need to read JSON text from handshake response
  839. const binaryData = new Uint8Array(data);
  840. const separatorIndex = binaryData.indexOf(TextMessageFormat.RecordSeparatorCode);
  841. if (separatorIndex === -1) {
  842. throw new Error("Message is incomplete.");
  843. }
  844. // content before separator is handshake response
  845. // optional content after is additional messages
  846. const responseLength = separatorIndex + 1;
  847. messageData = String.fromCharCode.apply(null, Array.prototype.slice.call(binaryData.slice(0, responseLength)));
  848. remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null;
  849. }
  850. else {
  851. const textData = data;
  852. const separatorIndex = textData.indexOf(TextMessageFormat.RecordSeparator);
  853. if (separatorIndex === -1) {
  854. throw new Error("Message is incomplete.");
  855. }
  856. // content before separator is handshake response
  857. // optional content after is additional messages
  858. const responseLength = separatorIndex + 1;
  859. messageData = textData.substring(0, responseLength);
  860. remainingData = (textData.length > responseLength) ? textData.substring(responseLength) : null;
  861. }
  862. // At this point we should have just the single handshake message
  863. const messages = TextMessageFormat.parse(messageData);
  864. const response = JSON.parse(messages[0]);
  865. if (response.type) {
  866. throw new Error("Expected a handshake response from the server.");
  867. }
  868. const responseMessage = response;
  869. // multiple messages could have arrived with handshake
  870. // return additional data to be parsed as usual, or null if all parsed
  871. return [remainingData, responseMessage];
  872. }
  873. }
  874. ;// CONCATENATED MODULE: ./src/IHubProtocol.ts
  875. // Licensed to the .NET Foundation under one or more agreements.
  876. // The .NET Foundation licenses this file to you under the MIT license.
  877. /** Defines the type of a Hub Message. */
  878. var MessageType;
  879. (function (MessageType) {
  880. /** Indicates the message is an Invocation message and implements the {@link @microsoft/signalr.InvocationMessage} interface. */
  881. MessageType[MessageType["Invocation"] = 1] = "Invocation";
  882. /** Indicates the message is a StreamItem message and implements the {@link @microsoft/signalr.StreamItemMessage} interface. */
  883. MessageType[MessageType["StreamItem"] = 2] = "StreamItem";
  884. /** Indicates the message is a Completion message and implements the {@link @microsoft/signalr.CompletionMessage} interface. */
  885. MessageType[MessageType["Completion"] = 3] = "Completion";
  886. /** Indicates the message is a Stream Invocation message and implements the {@link @microsoft/signalr.StreamInvocationMessage} interface. */
  887. MessageType[MessageType["StreamInvocation"] = 4] = "StreamInvocation";
  888. /** Indicates the message is a Cancel Invocation message and implements the {@link @microsoft/signalr.CancelInvocationMessage} interface. */
  889. MessageType[MessageType["CancelInvocation"] = 5] = "CancelInvocation";
  890. /** Indicates the message is a Ping message and implements the {@link @microsoft/signalr.PingMessage} interface. */
  891. MessageType[MessageType["Ping"] = 6] = "Ping";
  892. /** Indicates the message is a Close message and implements the {@link @microsoft/signalr.CloseMessage} interface. */
  893. MessageType[MessageType["Close"] = 7] = "Close";
  894. })(MessageType || (MessageType = {}));
  895. ;// CONCATENATED MODULE: ./src/Subject.ts
  896. // Licensed to the .NET Foundation under one or more agreements.
  897. // The .NET Foundation licenses this file to you under the MIT license.
  898. /** Stream implementation to stream items to the server. */
  899. class Subject {
  900. constructor() {
  901. this.observers = [];
  902. }
  903. next(item) {
  904. for (const observer of this.observers) {
  905. observer.next(item);
  906. }
  907. }
  908. error(err) {
  909. for (const observer of this.observers) {
  910. if (observer.error) {
  911. observer.error(err);
  912. }
  913. }
  914. }
  915. complete() {
  916. for (const observer of this.observers) {
  917. if (observer.complete) {
  918. observer.complete();
  919. }
  920. }
  921. }
  922. subscribe(observer) {
  923. this.observers.push(observer);
  924. return new SubjectSubscription(this, observer);
  925. }
  926. }
  927. ;// CONCATENATED MODULE: ./src/HubConnection.ts
  928. // Licensed to the .NET Foundation under one or more agreements.
  929. // The .NET Foundation licenses this file to you under the MIT license.
  930. const DEFAULT_TIMEOUT_IN_MS = 30 * 1000;
  931. const DEFAULT_PING_INTERVAL_IN_MS = 15 * 1000;
  932. /** Describes the current state of the {@link HubConnection} to the server. */
  933. var HubConnectionState;
  934. (function (HubConnectionState) {
  935. /** The hub connection is disconnected. */
  936. HubConnectionState["Disconnected"] = "Disconnected";
  937. /** The hub connection is connecting. */
  938. HubConnectionState["Connecting"] = "Connecting";
  939. /** The hub connection is connected. */
  940. HubConnectionState["Connected"] = "Connected";
  941. /** The hub connection is disconnecting. */
  942. HubConnectionState["Disconnecting"] = "Disconnecting";
  943. /** The hub connection is reconnecting. */
  944. HubConnectionState["Reconnecting"] = "Reconnecting";
  945. })(HubConnectionState || (HubConnectionState = {}));
  946. /** Represents a connection to a SignalR Hub. */
  947. class HubConnection {
  948. constructor(connection, logger, protocol, reconnectPolicy) {
  949. this._nextKeepAlive = 0;
  950. this._freezeEventListener = () => {
  951. this._logger.log(LogLevel.Warning, "The page is being frozen, this will likely lead to the connection being closed and messages being lost. For more information see the docs at https://docs.microsoft.com/aspnet/core/signalr/javascript-client#bsleep");
  952. };
  953. Arg.isRequired(connection, "connection");
  954. Arg.isRequired(logger, "logger");
  955. Arg.isRequired(protocol, "protocol");
  956. this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS;
  957. this.keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS;
  958. this._logger = logger;
  959. this._protocol = protocol;
  960. this.connection = connection;
  961. this._reconnectPolicy = reconnectPolicy;
  962. this._handshakeProtocol = new HandshakeProtocol();
  963. this.connection.onreceive = (data) => this._processIncomingData(data);
  964. this.connection.onclose = (error) => this._connectionClosed(error);
  965. this._callbacks = {};
  966. this._methods = {};
  967. this._closedCallbacks = [];
  968. this._reconnectingCallbacks = [];
  969. this._reconnectedCallbacks = [];
  970. this._invocationId = 0;
  971. this._receivedHandshakeResponse = false;
  972. this._connectionState = HubConnectionState.Disconnected;
  973. this._connectionStarted = false;
  974. this._cachedPingMessage = this._protocol.writeMessage({ type: MessageType.Ping });
  975. }
  976. /** @internal */
  977. // Using a public static factory method means we can have a private constructor and an _internal_
  978. // create method that can be used by HubConnectionBuilder. An "internal" constructor would just
  979. // be stripped away and the '.d.ts' file would have no constructor, which is interpreted as a
  980. // public parameter-less constructor.
  981. static create(connection, logger, protocol, reconnectPolicy) {
  982. return new HubConnection(connection, logger, protocol, reconnectPolicy);
  983. }
  984. /** Indicates the state of the {@link HubConnection} to the server. */
  985. get state() {
  986. return this._connectionState;
  987. }
  988. /** Represents the connection id of the {@link HubConnection} on the server. The connection id will be null when the connection is either
  989. * in the disconnected state or if the negotiation step was skipped.
  990. */
  991. get connectionId() {
  992. return this.connection ? (this.connection.connectionId || null) : null;
  993. }
  994. /** Indicates the url of the {@link HubConnection} to the server. */
  995. get baseUrl() {
  996. return this.connection.baseUrl || "";
  997. }
  998. /**
  999. * Sets a new url for the HubConnection. Note that the url can only be changed when the connection is in either the Disconnected or
  1000. * Reconnecting states.
  1001. * @param {string} url The url to connect to.
  1002. */
  1003. set baseUrl(url) {
  1004. if (this._connectionState !== HubConnectionState.Disconnected && this._connectionState !== HubConnectionState.Reconnecting) {
  1005. throw new Error("The HubConnection must be in the Disconnected or Reconnecting state to change the url.");
  1006. }
  1007. if (!url) {
  1008. throw new Error("The HubConnection url must be a valid url.");
  1009. }
  1010. this.connection.baseUrl = url;
  1011. }
  1012. /** Starts the connection.
  1013. *
  1014. * @returns {Promise<void>} A Promise that resolves when the connection has been successfully established, or rejects with an error.
  1015. */
  1016. start() {
  1017. this._startPromise = this._startWithStateTransitions();
  1018. return this._startPromise;
  1019. }
  1020. async _startWithStateTransitions() {
  1021. if (this._connectionState !== HubConnectionState.Disconnected) {
  1022. return Promise.reject(new Error("Cannot start a HubConnection that is not in the 'Disconnected' state."));
  1023. }
  1024. this._connectionState = HubConnectionState.Connecting;
  1025. this._logger.log(LogLevel.Debug, "Starting HubConnection.");
  1026. try {
  1027. await this._startInternal();
  1028. if (Platform.isBrowser) {
  1029. // Log when the browser freezes the tab so users know why their connection unexpectedly stopped working
  1030. window.document.addEventListener("freeze", this._freezeEventListener);
  1031. }
  1032. this._connectionState = HubConnectionState.Connected;
  1033. this._connectionStarted = true;
  1034. this._logger.log(LogLevel.Debug, "HubConnection connected successfully.");
  1035. }
  1036. catch (e) {
  1037. this._connectionState = HubConnectionState.Disconnected;
  1038. this._logger.log(LogLevel.Debug, `HubConnection failed to start successfully because of error '${e}'.`);
  1039. return Promise.reject(e);
  1040. }
  1041. }
  1042. async _startInternal() {
  1043. this._stopDuringStartError = undefined;
  1044. this._receivedHandshakeResponse = false;
  1045. // Set up the promise before any connection is (re)started otherwise it could race with received messages
  1046. const handshakePromise = new Promise((resolve, reject) => {
  1047. this._handshakeResolver = resolve;
  1048. this._handshakeRejecter = reject;
  1049. });
  1050. await this.connection.start(this._protocol.transferFormat);
  1051. try {
  1052. const handshakeRequest = {
  1053. protocol: this._protocol.name,
  1054. version: this._protocol.version,
  1055. };
  1056. this._logger.log(LogLevel.Debug, "Sending handshake request.");
  1057. await this._sendMessage(this._handshakeProtocol.writeHandshakeRequest(handshakeRequest));
  1058. this._logger.log(LogLevel.Information, `Using HubProtocol '${this._protocol.name}'.`);
  1059. // defensively cleanup timeout in case we receive a message from the server before we finish start
  1060. this._cleanupTimeout();
  1061. this._resetTimeoutPeriod();
  1062. this._resetKeepAliveInterval();
  1063. await handshakePromise;
  1064. // It's important to check the stopDuringStartError instead of just relying on the handshakePromise
  1065. // being rejected on close, because this continuation can run after both the handshake completed successfully
  1066. // and the connection was closed.
  1067. if (this._stopDuringStartError) {
  1068. // It's important to throw instead of returning a rejected promise, because we don't want to allow any state
  1069. // transitions to occur between now and the calling code observing the exceptions. Returning a rejected promise
  1070. // will cause the calling continuation to get scheduled to run later.
  1071. // eslint-disable-next-line @typescript-eslint/no-throw-literal
  1072. throw this._stopDuringStartError;
  1073. }
  1074. if (!this.connection.features.inherentKeepAlive) {
  1075. await this._sendMessage(this._cachedPingMessage);
  1076. }
  1077. }
  1078. catch (e) {
  1079. this._logger.log(LogLevel.Debug, `Hub handshake failed with error '${e}' during start(). Stopping HubConnection.`);
  1080. this._cleanupTimeout();
  1081. this._cleanupPingTimer();
  1082. // HttpConnection.stop() should not complete until after the onclose callback is invoked.
  1083. // This will transition the HubConnection to the disconnected state before HttpConnection.stop() completes.
  1084. await this.connection.stop(e);
  1085. throw e;
  1086. }
  1087. }
  1088. /** Stops the connection.
  1089. *
  1090. * @returns {Promise<void>} A Promise that resolves when the connection has been successfully terminated, or rejects with an error.
  1091. */
  1092. async stop() {
  1093. // Capture the start promise before the connection might be restarted in an onclose callback.
  1094. const startPromise = this._startPromise;
  1095. this._stopPromise = this._stopInternal();
  1096. await this._stopPromise;
  1097. try {
  1098. // Awaiting undefined continues immediately
  1099. await startPromise;
  1100. }
  1101. catch (e) {
  1102. // This exception is returned to the user as a rejected Promise from the start method.
  1103. }
  1104. }
  1105. _stopInternal(error) {
  1106. if (this._connectionState === HubConnectionState.Disconnected) {
  1107. this._logger.log(LogLevel.Debug, `Call to HubConnection.stop(${error}) ignored because it is already in the disconnected state.`);
  1108. return Promise.resolve();
  1109. }
  1110. if (this._connectionState === HubConnectionState.Disconnecting) {
  1111. this._logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnecting state.`);
  1112. return this._stopPromise;
  1113. }
  1114. this._connectionState = HubConnectionState.Disconnecting;
  1115. this._logger.log(LogLevel.Debug, "Stopping HubConnection.");
  1116. if (this._reconnectDelayHandle) {
  1117. // We're in a reconnect delay which means the underlying connection is currently already stopped.
  1118. // Just clear the handle to stop the reconnect loop (which no one is waiting on thankfully) and
  1119. // fire the onclose callbacks.
  1120. this._logger.log(LogLevel.Debug, "Connection stopped during reconnect delay. Done reconnecting.");
  1121. clearTimeout(this._reconnectDelayHandle);
  1122. this._reconnectDelayHandle = undefined;
  1123. this._completeClose();
  1124. return Promise.resolve();
  1125. }
  1126. this._cleanupTimeout();
  1127. this._cleanupPingTimer();
  1128. this._stopDuringStartError = error || new AbortError("The connection was stopped before the hub handshake could complete.");
  1129. // HttpConnection.stop() should not complete until after either HttpConnection.start() fails
  1130. // or the onclose callback is invoked. The onclose callback will transition the HubConnection
  1131. // to the disconnected state if need be before HttpConnection.stop() completes.
  1132. return this.connection.stop(error);
  1133. }
  1134. /** Invokes a streaming hub method on the server using the specified name and arguments.
  1135. *
  1136. * @typeparam T The type of the items returned by the server.
  1137. * @param {string} methodName The name of the server method to invoke.
  1138. * @param {any[]} args The arguments used to invoke the server method.
  1139. * @returns {IStreamResult<T>} An object that yields results from the server as they are received.
  1140. */
  1141. stream(methodName, ...args) {
  1142. const [streams, streamIds] = this._replaceStreamingParams(args);
  1143. const invocationDescriptor = this._createStreamInvocation(methodName, args, streamIds);
  1144. // eslint-disable-next-line prefer-const
  1145. let promiseQueue;
  1146. const subject = new Subject();
  1147. subject.cancelCallback = () => {
  1148. const cancelInvocation = this._createCancelInvocation(invocationDescriptor.invocationId);
  1149. delete this._callbacks[invocationDescriptor.invocationId];
  1150. return promiseQueue.then(() => {
  1151. return this._sendWithProtocol(cancelInvocation);
  1152. });
  1153. };
  1154. this._callbacks[invocationDescriptor.invocationId] = (invocationEvent, error) => {
  1155. if (error) {
  1156. subject.error(error);
  1157. return;
  1158. }
  1159. else if (invocationEvent) {
  1160. // invocationEvent will not be null when an error is not passed to the callback
  1161. if (invocationEvent.type === MessageType.Completion) {
  1162. if (invocationEvent.error) {
  1163. subject.error(new Error(invocationEvent.error));
  1164. }
  1165. else {
  1166. subject.complete();
  1167. }
  1168. }
  1169. else {
  1170. subject.next((invocationEvent.item));
  1171. }
  1172. }
  1173. };
  1174. promiseQueue = this._sendWithProtocol(invocationDescriptor)
  1175. .catch((e) => {
  1176. subject.error(e);
  1177. delete this._callbacks[invocationDescriptor.invocationId];
  1178. });
  1179. this._launchStreams(streams, promiseQueue);
  1180. return subject;
  1181. }
  1182. _sendMessage(message) {
  1183. this._resetKeepAliveInterval();
  1184. return this.connection.send(message);
  1185. }
  1186. /**
  1187. * Sends a js object to the server.
  1188. * @param message The js object to serialize and send.
  1189. */
  1190. _sendWithProtocol(message) {
  1191. return this._sendMessage(this._protocol.writeMessage(message));
  1192. }
  1193. /** Invokes a hub method on the server using the specified name and arguments. Does not wait for a response from the receiver.
  1194. *
  1195. * The Promise returned by this method resolves when the client has sent the invocation to the server. The server may still
  1196. * be processing the invocation.
  1197. *
  1198. * @param {string} methodName The name of the server method to invoke.
  1199. * @param {any[]} args The arguments used to invoke the server method.
  1200. * @returns {Promise<void>} A Promise that resolves when the invocation has been successfully sent, or rejects with an error.
  1201. */
  1202. send(methodName, ...args) {
  1203. const [streams, streamIds] = this._replaceStreamingParams(args);
  1204. const sendPromise = this._sendWithProtocol(this._createInvocation(methodName, args, true, streamIds));
  1205. this._launchStreams(streams, sendPromise);
  1206. return sendPromise;
  1207. }
  1208. /** Invokes a hub method on the server using the specified name and arguments.
  1209. *
  1210. * The Promise returned by this method resolves when the server indicates it has finished invoking the method. When the promise
  1211. * resolves, the server has finished invoking the method. If the server method returns a result, it is produced as the result of
  1212. * resolving the Promise.
  1213. *
  1214. * @typeparam T The expected return type.
  1215. * @param {string} methodName The name of the server method to invoke.
  1216. * @param {any[]} args The arguments used to invoke the server method.
  1217. * @returns {Promise<T>} A Promise that resolves with the result of the server method (if any), or rejects with an error.
  1218. */
  1219. invoke(methodName, ...args) {
  1220. const [streams, streamIds] = this._replaceStreamingParams(args);
  1221. const invocationDescriptor = this._createInvocation(methodName, args, false, streamIds);
  1222. const p = new Promise((resolve, reject) => {
  1223. // invocationId will always have a value for a non-blocking invocation
  1224. this._callbacks[invocationDescriptor.invocationId] = (invocationEvent, error) => {
  1225. if (error) {
  1226. reject(error);
  1227. return;
  1228. }
  1229. else if (invocationEvent) {
  1230. // invocationEvent will not be null when an error is not passed to the callback
  1231. if (invocationEvent.type === MessageType.Completion) {
  1232. if (invocationEvent.error) {
  1233. reject(new Error(invocationEvent.error));
  1234. }
  1235. else {
  1236. resolve(invocationEvent.result);
  1237. }
  1238. }
  1239. else {
  1240. reject(new Error(`Unexpected message type: ${invocationEvent.type}`));
  1241. }
  1242. }
  1243. };
  1244. const promiseQueue = this._sendWithProtocol(invocationDescriptor)
  1245. .catch((e) => {
  1246. reject(e);
  1247. // invocationId will always have a value for a non-blocking invocation
  1248. delete this._callbacks[invocationDescriptor.invocationId];
  1249. });
  1250. this._launchStreams(streams, promiseQueue);
  1251. });
  1252. return p;
  1253. }
  1254. on(methodName, newMethod) {
  1255. if (!methodName || !newMethod) {
  1256. return;
  1257. }
  1258. methodName = methodName.toLowerCase();
  1259. if (!this._methods[methodName]) {
  1260. this._methods[methodName] = [];
  1261. }
  1262. // Preventing adding the same handler multiple times.
  1263. if (this._methods[methodName].indexOf(newMethod) !== -1) {
  1264. return;
  1265. }
  1266. this._methods[methodName].push(newMethod);
  1267. }
  1268. off(methodName, method) {
  1269. if (!methodName) {
  1270. return;
  1271. }
  1272. methodName = methodName.toLowerCase();
  1273. const handlers = this._methods[methodName];
  1274. if (!handlers) {
  1275. return;
  1276. }
  1277. if (method) {
  1278. const removeIdx = handlers.indexOf(method);
  1279. if (removeIdx !== -1) {
  1280. handlers.splice(removeIdx, 1);
  1281. if (handlers.length === 0) {
  1282. delete this._methods[methodName];
  1283. }
  1284. }
  1285. }
  1286. else {
  1287. delete this._methods[methodName];
  1288. }
  1289. }
  1290. /** Registers a handler that will be invoked when the connection is closed.
  1291. *
  1292. * @param {Function} callback The handler that will be invoked when the connection is closed. Optionally receives a single argument containing the error that caused the connection to close (if any).
  1293. */
  1294. onclose(callback) {
  1295. if (callback) {
  1296. this._closedCallbacks.push(callback);
  1297. }
  1298. }
  1299. /** Registers a handler that will be invoked when the connection starts reconnecting.
  1300. *
  1301. * @param {Function} callback The handler that will be invoked when the connection starts reconnecting. Optionally receives a single argument containing the error that caused the connection to start reconnecting (if any).
  1302. */
  1303. onreconnecting(callback) {
  1304. if (callback) {
  1305. this._reconnectingCallbacks.push(callback);
  1306. }
  1307. }
  1308. /** Registers a handler that will be invoked when the connection successfully reconnects.
  1309. *
  1310. * @param {Function} callback The handler that will be invoked when the connection successfully reconnects.
  1311. */
  1312. onreconnected(callback) {
  1313. if (callback) {
  1314. this._reconnectedCallbacks.push(callback);
  1315. }
  1316. }
  1317. _processIncomingData(data) {
  1318. this._cleanupTimeout();
  1319. if (!this._receivedHandshakeResponse) {
  1320. data = this._processHandshakeResponse(data);
  1321. this._receivedHandshakeResponse = true;
  1322. }
  1323. // Data may have all been read when processing handshake response
  1324. if (data) {
  1325. // Parse the messages
  1326. const messages = this._protocol.parseMessages(data, this._logger);
  1327. for (const message of messages) {
  1328. switch (message.type) {
  1329. case MessageType.Invocation:
  1330. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  1331. this._invokeClientMethod(message);
  1332. break;
  1333. case MessageType.StreamItem:
  1334. case MessageType.Completion: {
  1335. const callback = this._callbacks[message.invocationId];
  1336. if (callback) {
  1337. if (message.type === MessageType.Completion) {
  1338. delete this._callbacks[message.invocationId];
  1339. }
  1340. try {
  1341. callback(message);
  1342. }
  1343. catch (e) {
  1344. this._logger.log(LogLevel.Error, `Stream callback threw error: ${getErrorString(e)}`);
  1345. }
  1346. }
  1347. break;
  1348. }
  1349. case MessageType.Ping:
  1350. // Don't care about pings
  1351. break;
  1352. case MessageType.Close: {
  1353. this._logger.log(LogLevel.Information, "Close message received from server.");
  1354. const error = message.error ? new Error("Server returned an error on close: " + message.error) : undefined;
  1355. if (message.allowReconnect === true) {
  1356. // It feels wrong not to await connection.stop() here, but processIncomingData is called as part of an onreceive callback which is not async,
  1357. // this is already the behavior for serverTimeout(), and HttpConnection.Stop() should catch and log all possible exceptions.
  1358. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  1359. this.connection.stop(error);
  1360. }
  1361. else {
  1362. // We cannot await stopInternal() here, but subsequent calls to stop() will await this if stopInternal() is still ongoing.
  1363. this._stopPromise = this._stopInternal(error);
  1364. }
  1365. break;
  1366. }
  1367. default:
  1368. this._logger.log(LogLevel.Warning, `Invalid message type: ${message.type}.`);
  1369. break;
  1370. }
  1371. }
  1372. }
  1373. this._resetTimeoutPeriod();
  1374. }
  1375. _processHandshakeResponse(data) {
  1376. let responseMessage;
  1377. let remainingData;
  1378. try {
  1379. [remainingData, responseMessage] = this._handshakeProtocol.parseHandshakeResponse(data);
  1380. }
  1381. catch (e) {
  1382. const message = "Error parsing handshake response: " + e;
  1383. this._logger.log(LogLevel.Error, message);
  1384. const error = new Error(message);
  1385. this._handshakeRejecter(error);
  1386. throw error;
  1387. }
  1388. if (responseMessage.error) {
  1389. const message = "Server returned handshake error: " + responseMessage.error;
  1390. this._logger.log(LogLevel.Error, message);
  1391. const error = new Error(message);
  1392. this._handshakeRejecter(error);
  1393. throw error;
  1394. }
  1395. else {
  1396. this._logger.log(LogLevel.Debug, "Server handshake complete.");
  1397. }
  1398. this._handshakeResolver();
  1399. return remainingData;
  1400. }
  1401. _resetKeepAliveInterval() {
  1402. if (this.connection.features.inherentKeepAlive) {
  1403. return;
  1404. }
  1405. // Set the time we want the next keep alive to be sent
  1406. // Timer will be setup on next message receive
  1407. this._nextKeepAlive = new Date().getTime() + this.keepAliveIntervalInMilliseconds;
  1408. this._cleanupPingTimer();
  1409. }
  1410. _resetTimeoutPeriod() {
  1411. if (!this.connection.features || !this.connection.features.inherentKeepAlive) {
  1412. // Set the timeout timer
  1413. this._timeoutHandle = setTimeout(() => this.serverTimeout(), this.serverTimeoutInMilliseconds);
  1414. // Set keepAlive timer if there isn't one
  1415. if (this._pingServerHandle === undefined) {
  1416. let nextPing = this._nextKeepAlive - new Date().getTime();
  1417. if (nextPing < 0) {
  1418. nextPing = 0;
  1419. }
  1420. // The timer needs to be set from a networking callback to avoid Chrome timer throttling from causing timers to run once a minute
  1421. this._pingServerHandle = setTimeout(async () => {
  1422. if (this._connectionState === HubConnectionState.Connected) {
  1423. try {
  1424. await this._sendMessage(this._cachedPingMessage);
  1425. }
  1426. catch {
  1427. // We don't care about the error. It should be seen elsewhere in the client.
  1428. // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering
  1429. this._cleanupPingTimer();
  1430. }
  1431. }
  1432. }, nextPing);
  1433. }
  1434. }
  1435. }
  1436. // eslint-disable-next-line @typescript-eslint/naming-convention
  1437. serverTimeout() {
  1438. // The server hasn't talked to us in a while. It doesn't like us anymore ... :(
  1439. // Terminate the connection, but we don't need to wait on the promise. This could trigger reconnecting.
  1440. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  1441. this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."));
  1442. }
  1443. async _invokeClientMethod(invocationMessage) {
  1444. const methodName = invocationMessage.target.toLowerCase();
  1445. const methods = this._methods[methodName];
  1446. if (!methods) {
  1447. this._logger.log(LogLevel.Warning, `No client method with the name '${methodName}' found.`);
  1448. // No handlers provided by client but the server is expecting a response still, so we send an error
  1449. if (invocationMessage.invocationId) {
  1450. this._logger.log(LogLevel.Warning, `No result given for '${methodName}' method and invocation ID '${invocationMessage.invocationId}'.`);
  1451. await this._sendWithProtocol(this._createCompletionMessage(invocationMessage.invocationId, "Client didn't provide a result.", null));
  1452. }
  1453. return;
  1454. }
  1455. // Avoid issues with handlers removing themselves thus modifying the list while iterating through it
  1456. const methodsCopy = methods.slice();
  1457. // Server expects a response
  1458. const expectsResponse = invocationMessage.invocationId ? true : false;
  1459. // We preserve the last result or exception but still call all handlers
  1460. let res;
  1461. let exception;
  1462. let completionMessage;
  1463. for (const m of methodsCopy) {
  1464. try {
  1465. const prevRes = res;
  1466. res = await m.apply(this, invocationMessage.arguments);
  1467. if (expectsResponse && res && prevRes) {
  1468. this._logger.log(LogLevel.Error, `Multiple results provided for '${methodName}'. Sending error to server.`);
  1469. completionMessage = this._createCompletionMessage(invocationMessage.invocationId, `Client provided multiple results.`, null);
  1470. }
  1471. // Ignore exception if we got a result after, the exception will be logged
  1472. exception = undefined;
  1473. }
  1474. catch (e) {
  1475. exception = e;
  1476. this._logger.log(LogLevel.Error, `A callback for the method '${methodName}' threw error '${e}'.`);
  1477. }
  1478. }
  1479. if (completionMessage) {
  1480. await this._sendWithProtocol(completionMessage);
  1481. }
  1482. else if (expectsResponse) {
  1483. // If there is an exception that means either no result was given or a handler after a result threw
  1484. if (exception) {
  1485. completionMessage = this._createCompletionMessage(invocationMessage.invocationId, `${exception}`, null);
  1486. }
  1487. else if (res !== undefined) {
  1488. completionMessage = this._createCompletionMessage(invocationMessage.invocationId, null, res);
  1489. }
  1490. else {
  1491. this._logger.log(LogLevel.Warning, `No result given for '${methodName}' method and invocation ID '${invocationMessage.invocationId}'.`);
  1492. // Client didn't provide a result or throw from a handler, server expects a response so we send an error
  1493. completionMessage = this._createCompletionMessage(invocationMessage.invocationId, "Client didn't provide a result.", null);
  1494. }
  1495. await this._sendWithProtocol(completionMessage);
  1496. }
  1497. else {
  1498. if (res) {
  1499. this._logger.log(LogLevel.Error, `Result given for '${methodName}' method but server is not expecting a result.`);
  1500. }
  1501. }
  1502. }
  1503. _connectionClosed(error) {
  1504. this._logger.log(LogLevel.Debug, `HubConnection.connectionClosed(${error}) called while in state ${this._connectionState}.`);
  1505. // Triggering this.handshakeRejecter is insufficient because it could already be resolved without the continuation having run yet.
  1506. this._stopDuringStartError = this._stopDuringStartError || error || new AbortError("The underlying connection was closed before the hub handshake could complete.");
  1507. // If the handshake is in progress, start will be waiting for the handshake promise, so we complete it.
  1508. // If it has already completed, this should just noop.
  1509. if (this._handshakeResolver) {
  1510. this._handshakeResolver();
  1511. }
  1512. this._cancelCallbacksWithError(error || new Error("Invocation canceled due to the underlying connection being closed."));
  1513. this._cleanupTimeout();
  1514. this._cleanupPingTimer();
  1515. if (this._connectionState === HubConnectionState.Disconnecting) {
  1516. this._completeClose(error);
  1517. }
  1518. else if (this._connectionState === HubConnectionState.Connected && this._reconnectPolicy) {
  1519. // eslint-disable-next-line @typescript-eslint/no-floating-promises
  1520. this._reconnect(error);
  1521. }
  1522. else if (this._connectionState === HubConnectionState.Connected) {
  1523. this._completeClose(error);
  1524. }
  1525. // If none of the above if conditions were true were called the HubConnection must be in either:
  1526. // 1. The Connecting state in which case the handshakeResolver will complete it and stopDuringStartError will fail it.
  1527. // 2. The Reconnecting state in which case the handshakeResolver will complete it and stopDuringStartError will fail the current reconnect attempt
  1528. // and potentially continue the reconnect() loop.
  1529. // 3. The Disconnected state in which case we're already done.
  1530. }
  1531. _completeClose(error) {
  1532. if (this._connectionStarted) {
  1533. this._connectionState = HubConnectionState.Disconnected;
  1534. this._connectionStarted = false;
  1535. if (Platform.isBrowser) {
  1536. window.document.removeEventListener("freeze", this._freezeEventListener);
  1537. }
  1538. try {
  1539. this._closedCallbacks.forEach((c) => c.apply(this, [error]));
  1540. }
  1541. catch (e) {
  1542. this._logger.log(LogLevel.Error, `An onclose callback called with error '${error}' threw error '${e}'.`);
  1543. }
  1544. }
  1545. }
  1546. async _reconnect(error) {
  1547. const reconnectStartTime = Date.now();
  1548. let previousReconnectAttempts = 0;
  1549. let retryError = error !== undefined ? error : new Error("Attempting to reconnect due to a unknown error.");
  1550. let nextRetryDelay = this._getNextRetryDelay(previousReconnectAttempts++, 0, retryError);
  1551. if (nextRetryDelay === null) {
  1552. this._logger.log(LogLevel.Debug, "Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt.");
  1553. this._completeClose(error);
  1554. return;
  1555. }
  1556. this._connectionState = HubConnectionState.Reconnecting;
  1557. if (error) {
  1558. this._logger.log(LogLevel.Information, `Connection reconnecting because of error '${error}'.`);
  1559. }
  1560. else {
  1561. this._logger.log(LogLevel.Information, "Connection reconnecting.");
  1562. }
  1563. if (this._reconnectingCallbacks.length !== 0) {
  1564. try {
  1565. this._reconnectingCallbacks.forEach((c) => c.apply(this, [error]));
  1566. }
  1567. catch (e) {
  1568. this._logger.log(LogLevel.Error, `An onreconnecting callback called with error '${error}' threw error '${e}'.`);
  1569. }
  1570. // Exit early if an onreconnecting callback called connection.stop().
  1571. if (this._connectionState !== HubConnectionState.Reconnecting) {
  1572. this._logger.log(LogLevel.Debug, "Connection left the reconnecting state in onreconnecting callback. Done reconnecting.");
  1573. return;
  1574. }
  1575. }
  1576. while (nextRetryDelay !== null) {
  1577. this._logger.log(LogLevel.Information, `Reconnect attempt number ${previousReconnectAttempts} will start in ${nextRetryDelay} ms.`);
  1578. await new Promise((resolve) => {
  1579. this._reconnectDelayHandle = setTimeout(resolve, nextRetryDelay);
  1580. });
  1581. this._reconnectDelayHandle = undefined;
  1582. if (this._connectionState !== HubConnectionState.Reconnecting) {
  1583. this._logger.log(LogLevel.Debug, "Connection left the reconnecting state during reconnect delay. Done reconnecting.");
  1584. return;
  1585. }
  1586. try {
  1587. await this._startInternal();
  1588. this._connectionState = HubConnectionState.Connected;
  1589. this._logger.log(LogLevel.Information, "HubConnection reconnected successfully.");
  1590. if (this._reconnectedCallbacks.length !== 0) {
  1591. try {
  1592. this._reconnectedCallbacks.forEach((c) => c.apply(this, [this.connection.connectionId]));
  1593. }
  1594. catch (e) {
  1595. this._logger.log(LogLevel.Error, `An onreconnected callback called with connectionId '${this.connection.connectionId}; threw error '${e}'.`);
  1596. }
  1597. }
  1598. return;
  1599. }
  1600. catch (e) {
  1601. this._logger.log(LogLevel.Information, `Reconnect attempt failed because of error '${e}'.`);
  1602. if (this._connectionState !== HubConnectionState.Reconnecting) {
  1603. this._logger.log(LogLevel.Debug, `Connection moved to the '${this._connectionState}' from the reconnecting state during reconnect attempt. Done reconnecting.`);
  1604. // The TypeScript compiler thinks that connectionState must be Connected here. The TypeScript compiler is wrong.
  1605. if (this._connectionState === HubConnectionState.Disconnecting) {
  1606. this._completeClose();
  1607. }
  1608. return;
  1609. }
  1610. retryError = e instanceof Error ? e : new Error(e.toString());
  1611. nextRetryDelay = this._getNextRetryDelay(previousReconnectAttempts++, Date.now() - reconnectStartTime, retryError);
  1612. }
  1613. }
  1614. this._logger.log(LogLevel.Information, `Reconnect retries have been exhausted after ${Date.now() - reconnectStartTime} ms and ${previousReconnectAttempts} failed attempts. Connection disconnecting.`);
  1615. this._completeClose();
  1616. }
  1617. _getNextRetryDelay(previousRetryCount, elapsedMilliseconds, retryReason) {
  1618. try {
  1619. return this._reconnectPolicy.nextRetryDelayInMilliseconds({
  1620. elapsedMilliseconds,
  1621. previousRetryCount,
  1622. retryReason,
  1623. });
  1624. }
  1625. catch (e) {
  1626. this._logger.log(LogLevel.Error, `IRetryPolicy.nextRetryDelayInMilliseconds(${previousRetryCount}, ${elapsedMilliseconds}) threw error '${e}'.`);
  1627. return null;
  1628. }
  1629. }
  1630. _cancelCallbacksWithError(error) {
  1631. const callbacks = this._callbacks;
  1632. this._callbacks = {};
  1633. Object.keys(callbacks)
  1634. .forEach((key) => {
  1635. const callback = callbacks[key];
  1636. try {
  1637. callback(null, error);
  1638. }
  1639. catch (e) {
  1640. this._logger.log(LogLevel.Error, `Stream 'error' callback called with '${error}' threw error: ${getErrorString(e)}`);
  1641. }
  1642. });
  1643. }
  1644. _cleanupPingTimer() {
  1645. if (this._pingServerHandle) {
  1646. clearTimeout(this._pingServerHandle);
  1647. this._pingServerHandle = undefined;
  1648. }
  1649. }
  1650. _cleanupTimeout() {
  1651. if (this._timeoutHandle) {
  1652. clearTimeout(this._timeoutHandle);
  1653. }
  1654. }
  1655. _createInvocation(methodName, args, nonblocking, streamIds) {
  1656. if (nonblocking) {
  1657. if (streamIds.length !== 0) {
  1658. return {
  1659. arguments: args,
  1660. streamIds,
  1661. target: methodName,
  1662. type: MessageType.Invocation,
  1663. };
  1664. }
  1665. else {
  1666. return {
  1667. arguments: args,
  1668. target: methodName,
  1669. type: MessageType.Invocation,
  1670. };
  1671. }
  1672. }
  1673. else {
  1674. const invocationId = this._invocationId;
  1675. this._invocationId++;
  1676. if (streamIds.length !== 0) {
  1677. return {
  1678. arguments: args,
  1679. invocationId: invocationId.toString(),
  1680. streamIds,
  1681. target: methodName,
  1682. type: MessageType.Invocation,
  1683. };
  1684. }
  1685. else {
  1686. return {
  1687. arguments: args,
  1688. invocationId: invocationId.toString(),
  1689. target: methodName,
  1690. type: MessageType.Invocation,
  1691. };
  1692. }
  1693. }
  1694. }
  1695. _launchStreams(streams, promiseQueue) {
  1696. if (streams.length === 0) {
  1697. return;
  1698. }
  1699. // Synchronize stream data so they arrive in-order on the server
  1700. if (!promiseQueue) {
  1701. promiseQueue = Promise.resolve();
  1702. }
  1703. // We want to iterate over the keys, since the keys are the stream ids
  1704. // eslint-disable-next-line guard-for-in
  1705. for (const streamId in streams) {
  1706. streams[streamId].subscribe({
  1707. complete: () => {
  1708. promiseQueue = promiseQueue.then(() => this._sendWithProtocol(this._createCompletionMessage(streamId)));
  1709. },
  1710. error: (err) => {
  1711. let message;
  1712. if (err instanceof Error) {
  1713. message = err.message;
  1714. }
  1715. else if (err && err.toString) {
  1716. message = err.toString();
  1717. }
  1718. else {
  1719. message = "Unknown error";
  1720. }
  1721. promiseQueue = promiseQueue.then(() => this._sendWithProtocol(this._createCompletionMessage(streamId, message)));
  1722. },
  1723. next: (item) => {
  1724. promiseQueue = promiseQueue.then(() => this._sendWithProtocol(this._createStreamItemMessage(streamId, item)));
  1725. },
  1726. });
  1727. }
  1728. }
  1729. _replaceStreamingParams(args) {
  1730. const streams = [];
  1731. const streamIds = [];
  1732. for (let i = 0; i < args.length; i++) {
  1733. const argument = args[i];
  1734. if (this._isObservable(argument)) {
  1735. const streamId = this._invocationId;
  1736. this._invocationId++;
  1737. // Store the stream for later use
  1738. streams[streamId] = argument;
  1739. streamIds.push(streamId.toString());
  1740. // remove stream from args
  1741. args.splice(i, 1);
  1742. }
  1743. }
  1744. return [streams, streamIds];
  1745. }
  1746. _isObservable(arg) {
  1747. // This allows other stream implementations to just work (like rxjs)
  1748. return arg && arg.subscribe && typeof arg.subscribe === "function";
  1749. }
  1750. _createStreamInvocation(methodName, args, streamIds) {
  1751. const invocationId = this._invocationId;
  1752. this._invocationId++;
  1753. if (streamIds.length !== 0) {
  1754. return {
  1755. arguments: args,
  1756. invocationId: invocationId.toString(),
  1757. streamIds,
  1758. target: methodName,
  1759. type: MessageType.StreamInvocation,
  1760. };
  1761. }
  1762. else {
  1763. return {
  1764. arguments: args,
  1765. invocationId: invocationId.toString(),
  1766. target: methodName,
  1767. type: MessageType.StreamInvocation,
  1768. };
  1769. }
  1770. }
  1771. _createCancelInvocation(id) {
  1772. return {
  1773. invocationId: id,
  1774. type: MessageType.CancelInvocation,
  1775. };
  1776. }
  1777. _createStreamItemMessage(id, item) {
  1778. return {
  1779. invocationId: id,
  1780. item,
  1781. type: MessageType.StreamItem,
  1782. };
  1783. }
  1784. _createCompletionMessage(id, error, result) {
  1785. if (error) {
  1786. return {
  1787. error,
  1788. invocationId: id,
  1789. type: MessageType.Completion,
  1790. };
  1791. }
  1792. return {
  1793. invocationId: id,
  1794. result,
  1795. type: MessageType.Completion,
  1796. };
  1797. }
  1798. }
  1799. ;// CONCATENATED MODULE: ./src/DefaultReconnectPolicy.ts
  1800. // Licensed to the .NET Foundation under one or more agreements.
  1801. // The .NET Foundation licenses this file to you under the MIT license.
  1802. // 0, 2, 10, 30 second delays before reconnect attempts.
  1803. const DEFAULT_RETRY_DELAYS_IN_MILLISECONDS = [0, 2000, 10000, 30000, null];
  1804. /** @private */
  1805. class DefaultReconnectPolicy {
  1806. constructor(retryDelays) {
  1807. this._retryDelays = retryDelays !== undefined ? [...retryDelays, null] : DEFAULT_RETRY_DELAYS_IN_MILLISECONDS;
  1808. }
  1809. nextRetryDelayInMilliseconds(retryContext) {
  1810. return this._retryDelays[retryContext.previousRetryCount];
  1811. }
  1812. }
  1813. ;// CONCATENATED MODULE: ./src/HeaderNames.ts
  1814. // Licensed to the .NET Foundation under one or more agreements.
  1815. // The .NET Foundation licenses this file to you under the MIT license.
  1816. class HeaderNames {
  1817. }
  1818. HeaderNames.Authorization = "Authorization";
  1819. HeaderNames.Cookie = "Cookie";
  1820. ;// CONCATENATED MODULE: ./src/AccessTokenHttpClient.ts
  1821. // Licensed to the .NET Foundation under one or more agreements.
  1822. // The .NET Foundation licenses this file to you under the MIT license.
  1823. /** @private */
  1824. class AccessTokenHttpClient extends HttpClient {
  1825. constructor(innerClient, accessTokenFactory) {
  1826. super();
  1827. this._innerClient = innerClient;
  1828. this._accessTokenFactory = accessTokenFactory;
  1829. }
  1830. async send(request) {
  1831. let allowRetry = true;
  1832. if (this._accessTokenFactory && (!this._accessToken || (request.url && request.url.indexOf("/negotiate?") > 0))) {
  1833. // don't retry if the request is a negotiate or if we just got a potentially new token from the access token factory
  1834. allowRetry = false;
  1835. this._accessToken = await this._accessTokenFactory();
  1836. }
  1837. this._setAuthorizationHeader(request);
  1838. const response = await this._innerClient.send(request);
  1839. if (allowRetry && response.statusCode === 401 && this._accessTokenFactory) {
  1840. this._accessToken = await this._accessTokenFactory();
  1841. this._setAuthorizationHeader(request);
  1842. return await this._innerClient.send(request);
  1843. }
  1844. return response;
  1845. }
  1846. _setAuthorizationHeader(request) {
  1847. if (!request.headers) {
  1848. request.headers = {};
  1849. }
  1850. if (this._accessToken) {
  1851. request.headers[HeaderNames.Authorization] = `Bearer ${this._accessToken}`;
  1852. }
  1853. // don't remove the header if there isn't an access token factory, the user manually added the header in this case
  1854. else if (this._accessTokenFactory) {
  1855. if (request.headers[HeaderNames.Authorization]) {
  1856. delete request.headers[HeaderNames.Authorization];
  1857. }
  1858. }
  1859. }
  1860. getCookieString(url) {
  1861. return this._innerClient.getCookieString(url);
  1862. }
  1863. }
  1864. ;// CONCATENATED MODULE: ./src/ITransport.ts
  1865. // Licensed to the .NET Foundation under one or more agreements.
  1866. // The .NET Foundation licenses this file to you under the MIT license.
  1867. // This will be treated as a bit flag in the future, so we keep it using power-of-two values.
  1868. /** Specifies a specific HTTP transport type. */
  1869. var HttpTransportType;
  1870. (function (HttpTransportType) {
  1871. /** Specifies no transport preference. */
  1872. HttpTransportType[HttpTransportType["None"] = 0] = "None";
  1873. /** Specifies the WebSockets transport. */
  1874. HttpTransportType[HttpTransportType["WebSockets"] = 1] = "WebSockets";
  1875. /** Specifies the Server-Sent Events transport. */
  1876. HttpTransportType[HttpTransportType["ServerSentEvents"] = 2] = "ServerSentEvents";
  1877. /** Specifies the Long Polling transport. */
  1878. HttpTransportType[HttpTransportType["LongPolling"] = 4] = "LongPolling";
  1879. })(HttpTransportType || (HttpTransportType = {}));
  1880. /** Specifies the transfer format for a connection. */
  1881. var TransferFormat;
  1882. (function (TransferFormat) {
  1883. /** Specifies that only text data will be transmitted over the connection. */
  1884. TransferFormat[TransferFormat["Text"] = 1] = "Text";
  1885. /** Specifies that binary data will be transmitted over the connection. */
  1886. TransferFormat[TransferFormat["Binary"] = 2] = "Binary";
  1887. })(TransferFormat || (TransferFormat = {}));
  1888. ;// CONCATENATED MODULE: ./src/AbortController.ts
  1889. // Licensed to the .NET Foundation under one or more agreements.
  1890. // The .NET Foundation licenses this file to you under the MIT license.
  1891. // Rough polyfill of https://developer.mozilla.org/en-US/docs/Web/API/AbortController
  1892. // We don't actually ever use the API being polyfilled, we always use the polyfill because
  1893. // it's a very new API right now.
  1894. // Not exported from index.
  1895. /** @private */
  1896. class AbortController_AbortController {
  1897. constructor() {
  1898. this._isAborted = false;
  1899. this.onabort = null;
  1900. }
  1901. abort() {
  1902. if (!this._isAborted) {
  1903. this._isAborted = true;
  1904. if (this.onabort) {
  1905. this.onabort();
  1906. }
  1907. }
  1908. }
  1909. get signal() {
  1910. return this;
  1911. }
  1912. get aborted() {
  1913. return this._isAborted;
  1914. }
  1915. }
  1916. ;// CONCATENATED MODULE: ./src/LongPollingTransport.ts
  1917. // Licensed to the .NET Foundation under one or more agreements.
  1918. // The .NET Foundation licenses this file to you under the MIT license.
  1919. // Not exported from 'index', this type is internal.
  1920. /** @private */
  1921. class LongPollingTransport {
  1922. constructor(httpClient, logger, options) {
  1923. this._httpClient = httpClient;
  1924. this._logger = logger;
  1925. this._pollAbort = new AbortController_AbortController();
  1926. this._options = options;
  1927. this._running = false;
  1928. this.onreceive = null;
  1929. this.onclose = null;
  1930. }
  1931. // This is an internal type, not exported from 'index' so this is really just internal.
  1932. get pollAborted() {
  1933. return this._pollAbort.aborted;
  1934. }
  1935. async connect(url, transferFormat) {
  1936. Arg.isRequired(url, "url");
  1937. Arg.isRequired(transferFormat, "transferFormat");
  1938. Arg.isIn(transferFormat, TransferFormat, "transferFormat");
  1939. this._url = url;
  1940. this._logger.log(LogLevel.Trace, "(LongPolling transport) Connecting.");
  1941. // Allow binary format on Node and Browsers that support binary content (indicated by the presence of responseType property)
  1942. if (transferFormat === TransferFormat.Binary &&
  1943. (typeof XMLHttpRequest !== "undefined" && typeof new XMLHttpRequest().responseType !== "string")) {
  1944. throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");
  1945. }
  1946. const [name, value] = getUserAgentHeader();
  1947. const headers = { [name]: value, ...this._options.headers };
  1948. const pollOptions = {
  1949. abortSignal: this._pollAbort.signal,
  1950. headers,
  1951. timeout: 100000,
  1952. withCredentials: this._options.withCredentials,
  1953. };
  1954. if (transferFormat === TransferFormat.Binary) {
  1955. pollOptions.responseType = "arraybuffer";
  1956. }
  1957. // Make initial long polling request
  1958. // Server uses first long polling request to finish initializing connection and it returns without data
  1959. const pollUrl = `${url}&_=${Date.now()}`;
  1960. this._logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}.`);
  1961. const response = await this._httpClient.get(pollUrl, pollOptions);
  1962. if (response.statusCode !== 200) {
  1963. this._logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}.`);
  1964. // Mark running as false so that the poll immediately ends and runs the close logic
  1965. this._closeError = new HttpError(response.statusText || "", response.statusCode);
  1966. this._running = false;
  1967. }
  1968. else {
  1969. this._running = true;
  1970. }
  1971. this._receiving = this._poll(this._url, pollOptions);
  1972. }
  1973. async _poll(url, pollOptions) {
  1974. try {
  1975. while (this._running) {
  1976. try {
  1977. const pollUrl = `${url}&_=${Date.now()}`;
  1978. this._logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}.`);
  1979. const response = await this._httpClient.get(pollUrl, pollOptions);
  1980. if (response.statusCode === 204) {
  1981. this._logger.log(LogLevel.Information, "(LongPolling transport) Poll terminated by server.");
  1982. this._running = false;
  1983. }
  1984. else if (response.statusCode !== 200) {
  1985. this._logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}.`);
  1986. // Unexpected status code
  1987. this._closeError = new HttpError(response.statusText || "", response.statusCode);
  1988. this._running = false;
  1989. }
  1990. else {
  1991. // Process the response
  1992. if (response.content) {
  1993. this._logger.log(LogLevel.Trace, `(LongPolling transport) data received. ${getDataDetail(response.content, this._options.logMessageContent)}.`);
  1994. if (this.onreceive) {
  1995. this.onreceive(response.content);
  1996. }
  1997. }
  1998. else {
  1999. // This is another way timeout manifest.
  2000. this._logger.log(LogLevel.Trace, "(LongPolling transport) Poll timed out, reissuing.");
  2001. }
  2002. }
  2003. }
  2004. catch (e) {
  2005. if (!this._running) {
  2006. // Log but disregard errors that occur after stopping
  2007. this._logger.log(LogLevel.Trace, `(LongPolling transport) Poll errored after shutdown: ${e.message}`);
  2008. }
  2009. else {
  2010. if (e instanceof TimeoutError) {
  2011. // Ignore timeouts and reissue the poll.
  2012. this._logger.log(LogLevel.Trace, "(LongPolling transport) Poll timed out, reissuing.");
  2013. }
  2014. else {
  2015. // Close the connection with the error as the result.
  2016. this._closeError = e;
  2017. this._running = false;
  2018. }
  2019. }
  2020. }
  2021. }
  2022. }
  2023. finally {
  2024. this._logger.log(LogLevel.Trace, "(LongPolling transport) Polling complete.");
  2025. // We will reach here with pollAborted==false when the server returned a response causing the transport to stop.
  2026. // If pollAborted==true then client initiated the stop and the stop method will raise the close event after DELETE is sent.
  2027. if (!this.pollAborted) {
  2028. this._raiseOnClose();
  2029. }
  2030. }
  2031. }
  2032. async send(data) {
  2033. if (!this._running) {
  2034. return Promise.reject(new Error("Cannot send until the transport is connected"));
  2035. }
  2036. return sendMessage(this._logger, "LongPolling", this._httpClient, this._url, data, this._options);
  2037. }
  2038. async stop() {
  2039. this._logger.log(LogLevel.Trace, "(LongPolling transport) Stopping polling.");
  2040. // Tell receiving loop to stop, abort any current request, and then wait for it to finish
  2041. this._running = false;
  2042. this._pollAbort.abort();
  2043. try {
  2044. await this._receiving;
  2045. // Send DELETE to clean up long polling on the server
  2046. this._logger.log(LogLevel.Trace, `(LongPolling transport) sending DELETE request to ${this._url}.`);
  2047. const headers = {};
  2048. const [name, value] = getUserAgentHeader();
  2049. headers[name] = value;
  2050. const deleteOptions = {
  2051. headers: { ...headers, ...this._options.headers },
  2052. timeout: this._options.timeout,
  2053. withCredentials: this._options.withCredentials,
  2054. };
  2055. await this._httpClient.delete(this._url, deleteOptions);
  2056. this._logger.log(LogLevel.Trace, "(LongPolling transport) DELETE request sent.");
  2057. }
  2058. finally {
  2059. this._logger.log(LogLevel.Trace, "(LongPolling transport) Stop finished.");
  2060. // Raise close event here instead of in polling
  2061. // It needs to happen after the DELETE request is sent
  2062. this._raiseOnClose();
  2063. }
  2064. }
  2065. _raiseOnClose() {
  2066. if (this.onclose) {
  2067. let logMessage = "(LongPolling transport) Firing onclose event.";
  2068. if (this._closeError) {
  2069. logMessage += " Error: " + this._closeError;
  2070. }
  2071. this._logger.log(LogLevel.Trace, logMessage);
  2072. this.onclose(this._closeError);
  2073. }
  2074. }
  2075. }
  2076. ;// CONCATENATED MODULE: ./src/ServerSentEventsTransport.ts
  2077. // Licensed to the .NET Foundation under one or more agreements.
  2078. // The .NET Foundation licenses this file to you under the MIT license.
  2079. /** @private */
  2080. class ServerSentEventsTransport {
  2081. constructor(httpClient, accessToken, logger, options) {
  2082. this._httpClient = httpClient;
  2083. this._accessToken = accessToken;
  2084. this._logger = logger;
  2085. this._options = options;
  2086. this.onreceive = null;
  2087. this.onclose = null;
  2088. }
  2089. async connect(url, transferFormat) {
  2090. Arg.isRequired(url, "url");
  2091. Arg.isRequired(transferFormat, "transferFormat");
  2092. Arg.isIn(transferFormat, TransferFormat, "transferFormat");
  2093. this._logger.log(LogLevel.Trace, "(SSE transport) Connecting.");
  2094. // set url before accessTokenFactory because this._url is only for send and we set the auth header instead of the query string for send
  2095. this._url = url;
  2096. if (this._accessToken) {
  2097. url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(this._accessToken)}`;
  2098. }
  2099. return new Promise((resolve, reject) => {
  2100. let opened = false;
  2101. if (transferFormat !== TransferFormat.Text) {
  2102. reject(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"));
  2103. return;
  2104. }
  2105. let eventSource;
  2106. if (Platform.isBrowser || Platform.isWebWorker) {
  2107. eventSource = new this._options.EventSource(url, { withCredentials: this._options.withCredentials });
  2108. }
  2109. else {
  2110. // Non-browser passes cookies via the dictionary
  2111. const cookies = this._httpClient.getCookieString(url);
  2112. const headers = {};
  2113. headers.Cookie = cookies;
  2114. const [name, value] = getUserAgentHeader();
  2115. headers[name] = value;
  2116. eventSource = new this._options.EventSource(url, { withCredentials: this._options.withCredentials, headers: { ...headers, ...this._options.headers } });
  2117. }
  2118. try {
  2119. eventSource.onmessage = (e) => {
  2120. if (this.onreceive) {
  2121. try {
  2122. this._logger.log(LogLevel.Trace, `(SSE transport) data received. ${getDataDetail(e.data, this._options.logMessageContent)}.`);
  2123. this.onreceive(e.data);
  2124. }
  2125. catch (error) {
  2126. this._close(error);
  2127. return;
  2128. }
  2129. }
  2130. };
  2131. // @ts-ignore: not using event on purpose
  2132. eventSource.onerror = (e) => {
  2133. // EventSource doesn't give any useful information about server side closes.
  2134. if (opened) {
  2135. this._close();
  2136. }
  2137. else {
  2138. reject(new Error("EventSource failed to connect. The connection could not be found on the server,"
  2139. + " either the connection ID is not present on the server, or a proxy is refusing/buffering the connection."
  2140. + " If you have multiple servers check that sticky sessions are enabled."));
  2141. }
  2142. };
  2143. eventSource.onopen = () => {
  2144. this._logger.log(LogLevel.Information, `SSE connected to ${this._url}`);
  2145. this._eventSource = eventSource;
  2146. opened = true;
  2147. resolve();
  2148. };
  2149. }
  2150. catch (e) {
  2151. reject(e);
  2152. return;
  2153. }
  2154. });
  2155. }
  2156. async send(data) {
  2157. if (!this._eventSource) {
  2158. return Promise.reject(new Error("Cannot send until the transport is connected"));
  2159. }
  2160. return sendMessage(this._logger, "SSE", this._httpClient, this._url, data, this._options);
  2161. }
  2162. stop() {
  2163. this._close();
  2164. return Promise.resolve();
  2165. }
  2166. _close(e) {
  2167. if (this._eventSource) {
  2168. this._eventSource.close();
  2169. this._eventSource = undefined;
  2170. if (this.onclose) {
  2171. this.onclose(e);
  2172. }
  2173. }
  2174. }
  2175. }
  2176. ;// CONCATENATED MODULE: ./src/WebSocketTransport.ts
  2177. // Licensed to the .NET Foundation under one or more agreements.
  2178. // The .NET Foundation licenses this file to you under the MIT license.
  2179. /** @private */
  2180. class WebSocketTransport {
  2181. constructor(httpClient, accessTokenFactory, logger, logMessageContent, webSocketConstructor, headers) {
  2182. this._logger = logger;
  2183. this._accessTokenFactory = accessTokenFactory;
  2184. this._logMessageContent = logMessageContent;
  2185. this._webSocketConstructor = webSocketConstructor;
  2186. this._httpClient = httpClient;
  2187. this.onreceive = null;
  2188. this.onclose = null;
  2189. this._headers = headers;
  2190. }
  2191. async connect(url, transferFormat) {
  2192. Arg.isRequired(url, "url");
  2193. Arg.isRequired(transferFormat, "transferFormat");
  2194. Arg.isIn(transferFormat, TransferFormat, "transferFormat");
  2195. this._logger.log(LogLevel.Trace, "(WebSockets transport) Connecting.");
  2196. let token;
  2197. if (this._accessTokenFactory) {
  2198. token = await this._accessTokenFactory();
  2199. }
  2200. return new Promise((resolve, reject) => {
  2201. url = url.replace(/^http/, "ws");
  2202. let webSocket;
  2203. const cookies = this._httpClient.getCookieString(url);
  2204. let opened = false;
  2205. if (Platform.isNode || Platform.isReactNative) {
  2206. const headers = {};
  2207. const [name, value] = getUserAgentHeader();
  2208. headers[name] = value;
  2209. if (token) {
  2210. headers[HeaderNames.Authorization] = `Bearer ${token}`;
  2211. }
  2212. if (cookies) {
  2213. headers[HeaderNames.Cookie] = cookies;
  2214. }
  2215. // Only pass headers when in non-browser environments
  2216. webSocket = new this._webSocketConstructor(url, undefined, {
  2217. headers: { ...headers, ...this._headers },
  2218. });
  2219. }
  2220. else {
  2221. if (token) {
  2222. url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
  2223. }
  2224. }
  2225. if (!webSocket) {
  2226. // Chrome is not happy with passing 'undefined' as protocol
  2227. webSocket = new this._webSocketConstructor(url);
  2228. }
  2229. if (transferFormat === TransferFormat.Binary) {
  2230. webSocket.binaryType = "arraybuffer";
  2231. }
  2232. webSocket.onopen = (_event) => {
  2233. this._logger.log(LogLevel.Information, `WebSocket connected to ${url}.`);
  2234. this._webSocket = webSocket;
  2235. opened = true;
  2236. resolve();
  2237. };
  2238. webSocket.onerror = (event) => {
  2239. let error = null;
  2240. // ErrorEvent is a browser only type we need to check if the type exists before using it
  2241. if (typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent) {
  2242. error = event.error;
  2243. }
  2244. else {
  2245. error = "There was an error with the transport";
  2246. }
  2247. this._logger.log(LogLevel.Information, `(WebSockets transport) ${error}.`);
  2248. };
  2249. webSocket.onmessage = (message) => {
  2250. this._logger.log(LogLevel.Trace, `(WebSockets transport) data received. ${getDataDetail(message.data, this._logMessageContent)}.`);
  2251. if (this.onreceive) {
  2252. try {
  2253. this.onreceive(message.data);
  2254. }
  2255. catch (error) {
  2256. this._close(error);
  2257. return;
  2258. }
  2259. }
  2260. };
  2261. webSocket.onclose = (event) => {
  2262. // Don't call close handler if connection was never established
  2263. // We'll reject the connect call instead
  2264. if (opened) {
  2265. this._close(event);
  2266. }
  2267. else {
  2268. let error = null;
  2269. // ErrorEvent is a browser only type we need to check if the type exists before using it
  2270. if (typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent) {
  2271. error = event.error;
  2272. }
  2273. else {
  2274. error = "WebSocket failed to connect. The connection could not be found on the server,"
  2275. + " either the endpoint may not be a SignalR endpoint,"
  2276. + " the connection ID is not present on the server, or there is a proxy blocking WebSockets."
  2277. + " If you have multiple servers check that sticky sessions are enabled.";
  2278. }
  2279. reject(new Error(error));
  2280. }
  2281. };
  2282. });
  2283. }
  2284. send(data) {
  2285. if (this._webSocket && this._webSocket.readyState === this._webSocketConstructor.OPEN) {
  2286. this._logger.log(LogLevel.Trace, `(WebSockets transport) sending data. ${getDataDetail(data, this._logMessageContent)}.`);
  2287. this._webSocket.send(data);
  2288. return Promise.resolve();
  2289. }
  2290. return Promise.reject("WebSocket is not in the OPEN state");
  2291. }
  2292. stop() {
  2293. if (this._webSocket) {
  2294. // Manually invoke onclose callback inline so we know the HttpConnection was closed properly before returning
  2295. // This also solves an issue where websocket.onclose could take 18+ seconds to trigger during network disconnects
  2296. this._close(undefined);
  2297. }
  2298. return Promise.resolve();
  2299. }
  2300. _close(event) {
  2301. // webSocket will be null if the transport did not start successfully
  2302. if (this._webSocket) {
  2303. // Clear websocket handlers because we are considering the socket closed now
  2304. this._webSocket.onclose = () => { };
  2305. this._webSocket.onmessage = () => { };
  2306. this._webSocket.onerror = () => { };
  2307. this._webSocket.close();
  2308. this._webSocket = undefined;
  2309. }
  2310. this._logger.log(LogLevel.Trace, "(WebSockets transport) socket closed.");
  2311. if (this.onclose) {
  2312. if (this._isCloseEvent(event) && (event.wasClean === false || event.code !== 1000)) {
  2313. this.onclose(new Error(`WebSocket closed with status code: ${event.code} (${event.reason || "no reason given"}).`));
  2314. }
  2315. else if (event instanceof Error) {
  2316. this.onclose(event);
  2317. }
  2318. else {
  2319. this.onclose();
  2320. }
  2321. }
  2322. }
  2323. _isCloseEvent(event) {
  2324. return event && typeof event.wasClean === "boolean" && typeof event.code === "number";
  2325. }
  2326. }
  2327. ;// CONCATENATED MODULE: ./src/HttpConnection.ts
  2328. // Licensed to the .NET Foundation under one or more agreements.
  2329. // The .NET Foundation licenses this file to you under the MIT license.
  2330. const MAX_REDIRECTS = 100;
  2331. /** @private */
  2332. class HttpConnection {
  2333. constructor(url, options = {}) {
  2334. this._stopPromiseResolver = () => { };
  2335. this.features = {};
  2336. this._negotiateVersion = 1;
  2337. Arg.isRequired(url, "url");
  2338. this._logger = createLogger(options.logger);
  2339. this.baseUrl = this._resolveUrl(url);
  2340. options = options || {};
  2341. options.logMessageContent = options.logMessageContent === undefined ? false : options.logMessageContent;
  2342. if (typeof options.withCredentials === "boolean" || options.withCredentials === undefined) {
  2343. options.withCredentials = options.withCredentials === undefined ? true : options.withCredentials;
  2344. }
  2345. else {
  2346. throw new Error("withCredentials option was not a 'boolean' or 'undefined' value");
  2347. }
  2348. options.timeout = options.timeout === undefined ? 100 * 1000 : options.timeout;
  2349. let webSocketModule = null;
  2350. let eventSourceModule = null;
  2351. if (Platform.isNode && "function" !== "undefined") {
  2352. // In order to ignore the dynamic require in webpack builds we need to do this magic
  2353. // @ts-ignore: TS doesn't know about these names
  2354. const requireFunc = true ? require : 0;
  2355. webSocketModule = requireFunc("ws");
  2356. eventSourceModule = requireFunc("eventsource");
  2357. }
  2358. if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) {
  2359. options.WebSocket = WebSocket;
  2360. }
  2361. else if (Platform.isNode && !options.WebSocket) {
  2362. if (webSocketModule) {
  2363. options.WebSocket = webSocketModule;
  2364. }
  2365. }
  2366. if (!Platform.isNode && typeof EventSource !== "undefined" && !options.EventSource) {
  2367. options.EventSource = EventSource;
  2368. }
  2369. else if (Platform.isNode && !options.EventSource) {
  2370. if (typeof eventSourceModule !== "undefined") {
  2371. options.EventSource = eventSourceModule;
  2372. }
  2373. }
  2374. this._httpClient = new AccessTokenHttpClient(options.httpClient || new DefaultHttpClient(this._logger), options.accessTokenFactory);
  2375. this._connectionState = "Disconnected" /* Disconnected */;
  2376. this._connectionStarted = false;
  2377. this._options = options;
  2378. this.onreceive = null;
  2379. this.onclose = null;
  2380. }
  2381. async start(transferFormat) {
  2382. transferFormat = transferFormat || TransferFormat.Binary;
  2383. Arg.isIn(transferFormat, TransferFormat, "transferFormat");
  2384. this._logger.log(LogLevel.Debug, `Starting connection with transfer format '${TransferFormat[transferFormat]}'.`);
  2385. if (this._connectionState !== "Disconnected" /* Disconnected */) {
  2386. return Promise.reject(new Error("Cannot start an HttpConnection that is not in the 'Disconnected' state."));
  2387. }
  2388. this._connectionState = "Connecting" /* Connecting */;
  2389. this._startInternalPromise = this._startInternal(transferFormat);
  2390. await this._startInternalPromise;
  2391. // The TypeScript compiler thinks that connectionState must be Connecting here. The TypeScript compiler is wrong.
  2392. if (this._connectionState === "Disconnecting" /* Disconnecting */) {
  2393. // stop() was called and transitioned the client into the Disconnecting state.
  2394. const message = "Failed to start the HttpConnection before stop() was called.";
  2395. this._logger.log(LogLevel.Error, message);
  2396. // We cannot await stopPromise inside startInternal since stopInternal awaits the startInternalPromise.
  2397. await this._stopPromise;
  2398. return Promise.reject(new AbortError(message));
  2399. }
  2400. else if (this._connectionState !== "Connected" /* Connected */) {
  2401. // stop() was called and transitioned the client into the Disconnecting state.
  2402. const message = "HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!";
  2403. this._logger.log(LogLevel.Error, message);
  2404. return Promise.reject(new AbortError(message));
  2405. }
  2406. this._connectionStarted = true;
  2407. }
  2408. send(data) {
  2409. if (this._connectionState !== "Connected" /* Connected */) {
  2410. return Promise.reject(new Error("Cannot send data if the connection is not in the 'Connected' State."));
  2411. }
  2412. if (!this._sendQueue) {
  2413. this._sendQueue = new TransportSendQueue(this.transport);
  2414. }
  2415. // Transport will not be null if state is connected
  2416. return this._sendQueue.send(data);
  2417. }
  2418. async stop(error) {
  2419. if (this._connectionState === "Disconnected" /* Disconnected */) {
  2420. this._logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnected state.`);
  2421. return Promise.resolve();
  2422. }
  2423. if (this._connectionState === "Disconnecting" /* Disconnecting */) {
  2424. this._logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnecting state.`);
  2425. return this._stopPromise;
  2426. }
  2427. this._connectionState = "Disconnecting" /* Disconnecting */;
  2428. this._stopPromise = new Promise((resolve) => {
  2429. // Don't complete stop() until stopConnection() completes.
  2430. this._stopPromiseResolver = resolve;
  2431. });
  2432. // stopInternal should never throw so just observe it.
  2433. await this._stopInternal(error);
  2434. await this._stopPromise;
  2435. }
  2436. async _stopInternal(error) {
  2437. // Set error as soon as possible otherwise there is a race between
  2438. // the transport closing and providing an error and the error from a close message
  2439. // We would prefer the close message error.
  2440. this._stopError = error;
  2441. try {
  2442. await this._startInternalPromise;
  2443. }
  2444. catch (e) {
  2445. // This exception is returned to the user as a rejected Promise from the start method.
  2446. }
  2447. // The transport's onclose will trigger stopConnection which will run our onclose event.
  2448. // The transport should always be set if currently connected. If it wasn't set, it's likely because
  2449. // stop was called during start() and start() failed.
  2450. if (this.transport) {
  2451. try {
  2452. await this.transport.stop();
  2453. }
  2454. catch (e) {
  2455. this._logger.log(LogLevel.Error, `HttpConnection.transport.stop() threw error '${e}'.`);
  2456. this._stopConnection();
  2457. }
  2458. this.transport = undefined;
  2459. }
  2460. else {
  2461. this._logger.log(LogLevel.Debug, "HttpConnection.transport is undefined in HttpConnection.stop() because start() failed.");
  2462. }
  2463. }
  2464. async _startInternal(transferFormat) {
  2465. // Store the original base url and the access token factory since they may change
  2466. // as part of negotiating
  2467. let url = this.baseUrl;
  2468. this._accessTokenFactory = this._options.accessTokenFactory;
  2469. this._httpClient._accessTokenFactory = this._accessTokenFactory;
  2470. try {
  2471. if (this._options.skipNegotiation) {
  2472. if (this._options.transport === HttpTransportType.WebSockets) {
  2473. // No need to add a connection ID in this case
  2474. this.transport = this._constructTransport(HttpTransportType.WebSockets);
  2475. // We should just call connect directly in this case.
  2476. // No fallback or negotiate in this case.
  2477. await this._startTransport(url, transferFormat);
  2478. }
  2479. else {
  2480. throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");
  2481. }
  2482. }
  2483. else {
  2484. let negotiateResponse = null;
  2485. let redirects = 0;
  2486. do {
  2487. negotiateResponse = await this._getNegotiationResponse(url);
  2488. // the user tries to stop the connection when it is being started
  2489. if (this._connectionState === "Disconnecting" /* Disconnecting */ || this._connectionState === "Disconnected" /* Disconnected */) {
  2490. throw new AbortError("The connection was stopped during negotiation.");
  2491. }
  2492. if (negotiateResponse.error) {
  2493. throw new Error(negotiateResponse.error);
  2494. }
  2495. if (negotiateResponse.ProtocolVersion) {
  2496. throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");
  2497. }
  2498. if (negotiateResponse.url) {
  2499. url = negotiateResponse.url;
  2500. }
  2501. if (negotiateResponse.accessToken) {
  2502. // Replace the current access token factory with one that uses
  2503. // the returned access token
  2504. const accessToken = negotiateResponse.accessToken;
  2505. this._accessTokenFactory = () => accessToken;
  2506. // set the factory to undefined so the AccessTokenHttpClient won't retry with the same token, since we know it won't change until a connection restart
  2507. this._httpClient._accessToken = accessToken;
  2508. this._httpClient._accessTokenFactory = undefined;
  2509. }
  2510. redirects++;
  2511. } while (negotiateResponse.url && redirects < MAX_REDIRECTS);
  2512. if (redirects === MAX_REDIRECTS && negotiateResponse.url) {
  2513. throw new Error("Negotiate redirection limit exceeded.");
  2514. }
  2515. await this._createTransport(url, this._options.transport, negotiateResponse, transferFormat);
  2516. }
  2517. if (this.transport instanceof LongPollingTransport) {
  2518. this.features.inherentKeepAlive = true;
  2519. }
  2520. if (this._connectionState === "Connecting" /* Connecting */) {
  2521. // Ensure the connection transitions to the connected state prior to completing this.startInternalPromise.
  2522. // start() will handle the case when stop was called and startInternal exits still in the disconnecting state.
  2523. this._logger.log(LogLevel.Debug, "The HttpConnection connected successfully.");
  2524. this._connectionState = "Connected" /* Connected */;
  2525. }
  2526. // stop() is waiting on us via this.startInternalPromise so keep this.transport around so it can clean up.
  2527. // This is the only case startInternal can exit in neither the connected nor disconnected state because stopConnection()
  2528. // will transition to the disconnected state. start() will wait for the transition using the stopPromise.
  2529. }
  2530. catch (e) {
  2531. this._logger.log(LogLevel.Error, "Failed to start the connection: " + e);
  2532. this._connectionState = "Disconnected" /* Disconnected */;
  2533. this.transport = undefined;
  2534. // if start fails, any active calls to stop assume that start will complete the stop promise
  2535. this._stopPromiseResolver();
  2536. return Promise.reject(e);
  2537. }
  2538. }
  2539. async _getNegotiationResponse(url) {
  2540. const headers = {};
  2541. const [name, value] = getUserAgentHeader();
  2542. headers[name] = value;
  2543. const negotiateUrl = this._resolveNegotiateUrl(url);
  2544. this._logger.log(LogLevel.Debug, `Sending negotiation request: ${negotiateUrl}.`);
  2545. try {
  2546. const response = await this._httpClient.post(negotiateUrl, {
  2547. content: "",
  2548. headers: { ...headers, ...this._options.headers },
  2549. timeout: this._options.timeout,
  2550. withCredentials: this._options.withCredentials,
  2551. });
  2552. if (response.statusCode !== 200) {
  2553. return Promise.reject(new Error(`Unexpected status code returned from negotiate '${response.statusCode}'`));
  2554. }
  2555. const negotiateResponse = JSON.parse(response.content);
  2556. if (!negotiateResponse.negotiateVersion || negotiateResponse.negotiateVersion < 1) {
  2557. // Negotiate version 0 doesn't use connectionToken
  2558. // So we set it equal to connectionId so all our logic can use connectionToken without being aware of the negotiate version
  2559. negotiateResponse.connectionToken = negotiateResponse.connectionId;
  2560. }
  2561. return negotiateResponse;
  2562. }
  2563. catch (e) {
  2564. let errorMessage = "Failed to complete negotiation with the server: " + e;
  2565. if (e instanceof HttpError) {
  2566. if (e.statusCode === 404) {
  2567. errorMessage = errorMessage + " Either this is not a SignalR endpoint or there is a proxy blocking the connection.";
  2568. }
  2569. }
  2570. this._logger.log(LogLevel.Error, errorMessage);
  2571. return Promise.reject(new FailedToNegotiateWithServerError(errorMessage));
  2572. }
  2573. }
  2574. _createConnectUrl(url, connectionToken) {
  2575. if (!connectionToken) {
  2576. return url;
  2577. }
  2578. return url + (url.indexOf("?") === -1 ? "?" : "&") + `id=${connectionToken}`;
  2579. }
  2580. async _createTransport(url, requestedTransport, negotiateResponse, requestedTransferFormat) {
  2581. let connectUrl = this._createConnectUrl(url, negotiateResponse.connectionToken);
  2582. if (this._isITransport(requestedTransport)) {
  2583. this._logger.log(LogLevel.Debug, "Connection was provided an instance of ITransport, using that directly.");
  2584. this.transport = requestedTransport;
  2585. await this._startTransport(connectUrl, requestedTransferFormat);
  2586. this.connectionId = negotiateResponse.connectionId;
  2587. return;
  2588. }
  2589. const transportExceptions = [];
  2590. const transports = negotiateResponse.availableTransports || [];
  2591. let negotiate = negotiateResponse;
  2592. for (const endpoint of transports) {
  2593. const transportOrError = this._resolveTransportOrError(endpoint, requestedTransport, requestedTransferFormat);
  2594. if (transportOrError instanceof Error) {
  2595. // Store the error and continue, we don't want to cause a re-negotiate in these cases
  2596. transportExceptions.push(`${endpoint.transport} failed:`);
  2597. transportExceptions.push(transportOrError);
  2598. }
  2599. else if (this._isITransport(transportOrError)) {
  2600. this.transport = transportOrError;
  2601. if (!negotiate) {
  2602. try {
  2603. negotiate = await this._getNegotiationResponse(url);
  2604. }
  2605. catch (ex) {
  2606. return Promise.reject(ex);
  2607. }
  2608. connectUrl = this._createConnectUrl(url, negotiate.connectionToken);
  2609. }
  2610. try {
  2611. await this._startTransport(connectUrl, requestedTransferFormat);
  2612. this.connectionId = negotiate.connectionId;
  2613. return;
  2614. }
  2615. catch (ex) {
  2616. this._logger.log(LogLevel.Error, `Failed to start the transport '${endpoint.transport}': ${ex}`);
  2617. negotiate = undefined;
  2618. transportExceptions.push(new FailedToStartTransportError(`${endpoint.transport} failed: ${ex}`, HttpTransportType[endpoint.transport]));
  2619. if (this._connectionState !== "Connecting" /* Connecting */) {
  2620. const message = "Failed to select transport before stop() was called.";
  2621. this._logger.log(LogLevel.Debug, message);
  2622. return Promise.reject(new AbortError(message));
  2623. }
  2624. }
  2625. }
  2626. }
  2627. if (transportExceptions.length > 0) {
  2628. return Promise.reject(new AggregateErrors(`Unable to connect to the server with any of the available transports. ${transportExceptions.join(" ")}`, transportExceptions));
  2629. }
  2630. return Promise.reject(new Error("None of the transports supported by the client are supported by the server."));
  2631. }
  2632. _constructTransport(transport) {
  2633. switch (transport) {
  2634. case HttpTransportType.WebSockets:
  2635. if (!this._options.WebSocket) {
  2636. throw new Error("'WebSocket' is not supported in your environment.");
  2637. }
  2638. return new WebSocketTransport(this._httpClient, this._accessTokenFactory, this._logger, this._options.logMessageContent, this._options.WebSocket, this._options.headers || {});
  2639. case HttpTransportType.ServerSentEvents:
  2640. if (!this._options.EventSource) {
  2641. throw new Error("'EventSource' is not supported in your environment.");
  2642. }
  2643. return new ServerSentEventsTransport(this._httpClient, this._httpClient._accessToken, this._logger, this._options);
  2644. case HttpTransportType.LongPolling:
  2645. return new LongPollingTransport(this._httpClient, this._logger, this._options);
  2646. default:
  2647. throw new Error(`Unknown transport: ${transport}.`);
  2648. }
  2649. }
  2650. _startTransport(url, transferFormat) {
  2651. this.transport.onreceive = this.onreceive;
  2652. this.transport.onclose = (e) => this._stopConnection(e);
  2653. return this.transport.connect(url, transferFormat);
  2654. }
  2655. _resolveTransportOrError(endpoint, requestedTransport, requestedTransferFormat) {
  2656. const transport = HttpTransportType[endpoint.transport];
  2657. if (transport === null || transport === undefined) {
  2658. this._logger.log(LogLevel.Debug, `Skipping transport '${endpoint.transport}' because it is not supported by this client.`);
  2659. return new Error(`Skipping transport '${endpoint.transport}' because it is not supported by this client.`);
  2660. }
  2661. else {
  2662. if (transportMatches(requestedTransport, transport)) {
  2663. const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]);
  2664. if (transferFormats.indexOf(requestedTransferFormat) >= 0) {
  2665. if ((transport === HttpTransportType.WebSockets && !this._options.WebSocket) ||
  2666. (transport === HttpTransportType.ServerSentEvents && !this._options.EventSource)) {
  2667. this._logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it is not supported in your environment.'`);
  2668. return new UnsupportedTransportError(`'${HttpTransportType[transport]}' is not supported in your environment.`, transport);
  2669. }
  2670. else {
  2671. this._logger.log(LogLevel.Debug, `Selecting transport '${HttpTransportType[transport]}'.`);
  2672. try {
  2673. return this._constructTransport(transport);
  2674. }
  2675. catch (ex) {
  2676. return ex;
  2677. }
  2678. }
  2679. }
  2680. else {
  2681. this._logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it does not support the requested transfer format '${TransferFormat[requestedTransferFormat]}'.`);
  2682. return new Error(`'${HttpTransportType[transport]}' does not support ${TransferFormat[requestedTransferFormat]}.`);
  2683. }
  2684. }
  2685. else {
  2686. this._logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it was disabled by the client.`);
  2687. return new DisabledTransportError(`'${HttpTransportType[transport]}' is disabled by the client.`, transport);
  2688. }
  2689. }
  2690. }
  2691. _isITransport(transport) {
  2692. return transport && typeof (transport) === "object" && "connect" in transport;
  2693. }
  2694. _stopConnection(error) {
  2695. this._logger.log(LogLevel.Debug, `HttpConnection.stopConnection(${error}) called while in state ${this._connectionState}.`);
  2696. this.transport = undefined;
  2697. // If we have a stopError, it takes precedence over the error from the transport
  2698. error = this._stopError || error;
  2699. this._stopError = undefined;
  2700. if (this._connectionState === "Disconnected" /* Disconnected */) {
  2701. this._logger.log(LogLevel.Debug, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is already in the disconnected state.`);
  2702. return;
  2703. }
  2704. if (this._connectionState === "Connecting" /* Connecting */) {
  2705. this._logger.log(LogLevel.Warning, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is still in the connecting state.`);
  2706. throw new Error(`HttpConnection.stopConnection(${error}) was called while the connection is still in the connecting state.`);
  2707. }
  2708. if (this._connectionState === "Disconnecting" /* Disconnecting */) {
  2709. // A call to stop() induced this call to stopConnection and needs to be completed.
  2710. // Any stop() awaiters will be scheduled to continue after the onclose callback fires.
  2711. this._stopPromiseResolver();
  2712. }
  2713. if (error) {
  2714. this._logger.log(LogLevel.Error, `Connection disconnected with error '${error}'.`);
  2715. }
  2716. else {
  2717. this._logger.log(LogLevel.Information, "Connection disconnected.");
  2718. }
  2719. if (this._sendQueue) {
  2720. this._sendQueue.stop().catch((e) => {
  2721. this._logger.log(LogLevel.Error, `TransportSendQueue.stop() threw error '${e}'.`);
  2722. });
  2723. this._sendQueue = undefined;
  2724. }
  2725. this.connectionId = undefined;
  2726. this._connectionState = "Disconnected" /* Disconnected */;
  2727. if (this._connectionStarted) {
  2728. this._connectionStarted = false;
  2729. try {
  2730. if (this.onclose) {
  2731. this.onclose(error);
  2732. }
  2733. }
  2734. catch (e) {
  2735. this._logger.log(LogLevel.Error, `HttpConnection.onclose(${error}) threw error '${e}'.`);
  2736. }
  2737. }
  2738. }
  2739. _resolveUrl(url) {
  2740. // startsWith is not supported in IE
  2741. if (url.lastIndexOf("https://", 0) === 0 || url.lastIndexOf("http://", 0) === 0) {
  2742. return url;
  2743. }
  2744. if (!Platform.isBrowser) {
  2745. throw new Error(`Cannot resolve '${url}'.`);
  2746. }
  2747. // Setting the url to the href propery of an anchor tag handles normalization
  2748. // for us. There are 3 main cases.
  2749. // 1. Relative path normalization e.g "b" -> "http://localhost:5000/a/b"
  2750. // 2. Absolute path normalization e.g "/a/b" -> "http://localhost:5000/a/b"
  2751. // 3. Networkpath reference normalization e.g "//localhost:5000/a/b" -> "http://localhost:5000/a/b"
  2752. const aTag = window.document.createElement("a");
  2753. aTag.href = url;
  2754. this._logger.log(LogLevel.Information, `Normalizing '${url}' to '${aTag.href}'.`);
  2755. return aTag.href;
  2756. }
  2757. _resolveNegotiateUrl(url) {
  2758. const index = url.indexOf("?");
  2759. let negotiateUrl = url.substring(0, index === -1 ? url.length : index);
  2760. if (negotiateUrl[negotiateUrl.length - 1] !== "/") {
  2761. negotiateUrl += "/";
  2762. }
  2763. negotiateUrl += "negotiate";
  2764. negotiateUrl += index === -1 ? "" : url.substring(index);
  2765. if (negotiateUrl.indexOf("negotiateVersion") === -1) {
  2766. negotiateUrl += index === -1 ? "?" : "&";
  2767. negotiateUrl += "negotiateVersion=" + this._negotiateVersion;
  2768. }
  2769. return negotiateUrl;
  2770. }
  2771. }
  2772. function transportMatches(requestedTransport, actualTransport) {
  2773. return !requestedTransport || ((actualTransport & requestedTransport) !== 0);
  2774. }
  2775. /** @private */
  2776. class TransportSendQueue {
  2777. constructor(_transport) {
  2778. this._transport = _transport;
  2779. this._buffer = [];
  2780. this._executing = true;
  2781. this._sendBufferedData = new PromiseSource();
  2782. this._transportResult = new PromiseSource();
  2783. this._sendLoopPromise = this._sendLoop();
  2784. }
  2785. send(data) {
  2786. this._bufferData(data);
  2787. if (!this._transportResult) {
  2788. this._transportResult = new PromiseSource();
  2789. }
  2790. return this._transportResult.promise;
  2791. }
  2792. stop() {
  2793. this._executing = false;
  2794. this._sendBufferedData.resolve();
  2795. return this._sendLoopPromise;
  2796. }
  2797. _bufferData(data) {
  2798. if (this._buffer.length && typeof (this._buffer[0]) !== typeof (data)) {
  2799. throw new Error(`Expected data to be of type ${typeof (this._buffer)} but was of type ${typeof (data)}`);
  2800. }
  2801. this._buffer.push(data);
  2802. this._sendBufferedData.resolve();
  2803. }
  2804. async _sendLoop() {
  2805. while (true) {
  2806. await this._sendBufferedData.promise;
  2807. if (!this._executing) {
  2808. if (this._transportResult) {
  2809. this._transportResult.reject("Connection stopped.");
  2810. }
  2811. break;
  2812. }
  2813. this._sendBufferedData = new PromiseSource();
  2814. const transportResult = this._transportResult;
  2815. this._transportResult = undefined;
  2816. const data = typeof (this._buffer[0]) === "string" ?
  2817. this._buffer.join("") :
  2818. TransportSendQueue._concatBuffers(this._buffer);
  2819. this._buffer.length = 0;
  2820. try {
  2821. await this._transport.send(data);
  2822. transportResult.resolve();
  2823. }
  2824. catch (error) {
  2825. transportResult.reject(error);
  2826. }
  2827. }
  2828. }
  2829. static _concatBuffers(arrayBuffers) {
  2830. const totalLength = arrayBuffers.map((b) => b.byteLength).reduce((a, b) => a + b);
  2831. const result = new Uint8Array(totalLength);
  2832. let offset = 0;
  2833. for (const item of arrayBuffers) {
  2834. result.set(new Uint8Array(item), offset);
  2835. offset += item.byteLength;
  2836. }
  2837. return result.buffer;
  2838. }
  2839. }
  2840. class PromiseSource {
  2841. constructor() {
  2842. this.promise = new Promise((resolve, reject) => [this._resolver, this._rejecter] = [resolve, reject]);
  2843. }
  2844. resolve() {
  2845. this._resolver();
  2846. }
  2847. reject(reason) {
  2848. this._rejecter(reason);
  2849. }
  2850. }
  2851. ;// CONCATENATED MODULE: ./src/JsonHubProtocol.ts
  2852. // Licensed to the .NET Foundation under one or more agreements.
  2853. // The .NET Foundation licenses this file to you under the MIT license.
  2854. const JSON_HUB_PROTOCOL_NAME = "json";
  2855. /** Implements the JSON Hub Protocol. */
  2856. class JsonHubProtocol {
  2857. constructor() {
  2858. /** @inheritDoc */
  2859. this.name = JSON_HUB_PROTOCOL_NAME;
  2860. /** @inheritDoc */
  2861. this.version = 1;
  2862. /** @inheritDoc */
  2863. this.transferFormat = TransferFormat.Text;
  2864. }
  2865. /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation.
  2866. *
  2867. * @param {string} input A string containing the serialized representation.
  2868. * @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
  2869. */
  2870. parseMessages(input, logger) {
  2871. // The interface does allow "ArrayBuffer" to be passed in, but this implementation does not. So let's throw a useful error.
  2872. if (typeof input !== "string") {
  2873. throw new Error("Invalid input for JSON hub protocol. Expected a string.");
  2874. }
  2875. if (!input) {
  2876. return [];
  2877. }
  2878. if (logger === null) {
  2879. logger = NullLogger.instance;
  2880. }
  2881. // Parse the messages
  2882. const messages = TextMessageFormat.parse(input);
  2883. const hubMessages = [];
  2884. for (const message of messages) {
  2885. const parsedMessage = JSON.parse(message);
  2886. if (typeof parsedMessage.type !== "number") {
  2887. throw new Error("Invalid payload.");
  2888. }
  2889. switch (parsedMessage.type) {
  2890. case MessageType.Invocation:
  2891. this._isInvocationMessage(parsedMessage);
  2892. break;
  2893. case MessageType.StreamItem:
  2894. this._isStreamItemMessage(parsedMessage);
  2895. break;
  2896. case MessageType.Completion:
  2897. this._isCompletionMessage(parsedMessage);
  2898. break;
  2899. case MessageType.Ping:
  2900. // Single value, no need to validate
  2901. break;
  2902. case MessageType.Close:
  2903. // All optional values, no need to validate
  2904. break;
  2905. default:
  2906. // Future protocol changes can add message types, old clients can ignore them
  2907. logger.log(LogLevel.Information, "Unknown message type '" + parsedMessage.type + "' ignored.");
  2908. continue;
  2909. }
  2910. hubMessages.push(parsedMessage);
  2911. }
  2912. return hubMessages;
  2913. }
  2914. /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string and returns it.
  2915. *
  2916. * @param {HubMessage} message The message to write.
  2917. * @returns {string} A string containing the serialized representation of the message.
  2918. */
  2919. writeMessage(message) {
  2920. return TextMessageFormat.write(JSON.stringify(message));
  2921. }
  2922. _isInvocationMessage(message) {
  2923. this._assertNotEmptyString(message.target, "Invalid payload for Invocation message.");
  2924. if (message.invocationId !== undefined) {
  2925. this._assertNotEmptyString(message.invocationId, "Invalid payload for Invocation message.");
  2926. }
  2927. }
  2928. _isStreamItemMessage(message) {
  2929. this._assertNotEmptyString(message.invocationId, "Invalid payload for StreamItem message.");
  2930. if (message.item === undefined) {
  2931. throw new Error("Invalid payload for StreamItem message.");
  2932. }
  2933. }
  2934. _isCompletionMessage(message) {
  2935. if (message.result && message.error) {
  2936. throw new Error("Invalid payload for Completion message.");
  2937. }
  2938. if (!message.result && message.error) {
  2939. this._assertNotEmptyString(message.error, "Invalid payload for Completion message.");
  2940. }
  2941. this._assertNotEmptyString(message.invocationId, "Invalid payload for Completion message.");
  2942. }
  2943. _assertNotEmptyString(value, errorMessage) {
  2944. if (typeof value !== "string" || value === "") {
  2945. throw new Error(errorMessage);
  2946. }
  2947. }
  2948. }
  2949. ;// CONCATENATED MODULE: ./src/HubConnectionBuilder.ts
  2950. // Licensed to the .NET Foundation under one or more agreements.
  2951. // The .NET Foundation licenses this file to you under the MIT license.
  2952. const LogLevelNameMapping = {
  2953. trace: LogLevel.Trace,
  2954. debug: LogLevel.Debug,
  2955. info: LogLevel.Information,
  2956. information: LogLevel.Information,
  2957. warn: LogLevel.Warning,
  2958. warning: LogLevel.Warning,
  2959. error: LogLevel.Error,
  2960. critical: LogLevel.Critical,
  2961. none: LogLevel.None,
  2962. };
  2963. function parseLogLevel(name) {
  2964. // Case-insensitive matching via lower-casing
  2965. // Yes, I know case-folding is a complicated problem in Unicode, but we only support
  2966. // the ASCII strings defined in LogLevelNameMapping anyway, so it's fine -anurse.
  2967. const mapping = LogLevelNameMapping[name.toLowerCase()];
  2968. if (typeof mapping !== "undefined") {
  2969. return mapping;
  2970. }
  2971. else {
  2972. throw new Error(`Unknown log level: ${name}`);
  2973. }
  2974. }
  2975. /** A builder for configuring {@link @microsoft/signalr.HubConnection} instances. */
  2976. class HubConnectionBuilder {
  2977. configureLogging(logging) {
  2978. Arg.isRequired(logging, "logging");
  2979. if (isLogger(logging)) {
  2980. this.logger = logging;
  2981. }
  2982. else if (typeof logging === "string") {
  2983. const logLevel = parseLogLevel(logging);
  2984. this.logger = new ConsoleLogger(logLevel);
  2985. }
  2986. else {
  2987. this.logger = new ConsoleLogger(logging);
  2988. }
  2989. return this;
  2990. }
  2991. withUrl(url, transportTypeOrOptions) {
  2992. Arg.isRequired(url, "url");
  2993. Arg.isNotEmpty(url, "url");
  2994. this.url = url;
  2995. // Flow-typing knows where it's at. Since HttpTransportType is a number and IHttpConnectionOptions is guaranteed
  2996. // to be an object, we know (as does TypeScript) this comparison is all we need to figure out which overload was called.
  2997. if (typeof transportTypeOrOptions === "object") {
  2998. this.httpConnectionOptions = { ...this.httpConnectionOptions, ...transportTypeOrOptions };
  2999. }
  3000. else {
  3001. this.httpConnectionOptions = {
  3002. ...this.httpConnectionOptions,
  3003. transport: transportTypeOrOptions,
  3004. };
  3005. }
  3006. return this;
  3007. }
  3008. /** Configures the {@link @microsoft/signalr.HubConnection} to use the specified Hub Protocol.
  3009. *
  3010. * @param {IHubProtocol} protocol The {@link @microsoft/signalr.IHubProtocol} implementation to use.
  3011. */
  3012. withHubProtocol(protocol) {
  3013. Arg.isRequired(protocol, "protocol");
  3014. this.protocol = protocol;
  3015. return this;
  3016. }
  3017. withAutomaticReconnect(retryDelaysOrReconnectPolicy) {
  3018. if (this.reconnectPolicy) {
  3019. throw new Error("A reconnectPolicy has already been set.");
  3020. }
  3021. if (!retryDelaysOrReconnectPolicy) {
  3022. this.reconnectPolicy = new DefaultReconnectPolicy();
  3023. }
  3024. else if (Array.isArray(retryDelaysOrReconnectPolicy)) {
  3025. this.reconnectPolicy = new DefaultReconnectPolicy(retryDelaysOrReconnectPolicy);
  3026. }
  3027. else {
  3028. this.reconnectPolicy = retryDelaysOrReconnectPolicy;
  3029. }
  3030. return this;
  3031. }
  3032. /** Creates a {@link @microsoft/signalr.HubConnection} from the configuration options specified in this builder.
  3033. *
  3034. * @returns {HubConnection} The configured {@link @microsoft/signalr.HubConnection}.
  3035. */
  3036. build() {
  3037. // If httpConnectionOptions has a logger, use it. Otherwise, override it with the one
  3038. // provided to configureLogger
  3039. const httpConnectionOptions = this.httpConnectionOptions || {};
  3040. // If it's 'null', the user **explicitly** asked for null, don't mess with it.
  3041. if (httpConnectionOptions.logger === undefined) {
  3042. // If our logger is undefined or null, that's OK, the HttpConnection constructor will handle it.
  3043. httpConnectionOptions.logger = this.logger;
  3044. }
  3045. // Now create the connection
  3046. if (!this.url) {
  3047. throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");
  3048. }
  3049. const connection = new HttpConnection(this.url, httpConnectionOptions);
  3050. return HubConnection.create(connection, this.logger || NullLogger.instance, this.protocol || new JsonHubProtocol(), this.reconnectPolicy);
  3051. }
  3052. }
  3053. function isLogger(logger) {
  3054. return logger.log !== undefined;
  3055. }
  3056. ;// CONCATENATED MODULE: ./src/index.ts
  3057. // Licensed to the .NET Foundation under one or more agreements.
  3058. // The .NET Foundation licenses this file to you under the MIT license.
  3059. ;// CONCATENATED MODULE: ./src/browser-index.ts
  3060. // Licensed to the .NET Foundation under one or more agreements.
  3061. // The .NET Foundation licenses this file to you under the MIT license.
  3062. // This is where we add any polyfills we'll need for the browser. It is the entry module for browser-specific builds.
  3063. // Copy from Array.prototype into Uint8Array to polyfill on IE. It's OK because the implementations of indexOf and slice use properties
  3064. // that exist on Uint8Array with the same name, and JavaScript is magic.
  3065. // We make them 'writable' because the Buffer polyfill messes with it as well.
  3066. if (!Uint8Array.prototype.indexOf) {
  3067. Object.defineProperty(Uint8Array.prototype, "indexOf", {
  3068. value: Array.prototype.indexOf,
  3069. writable: true,
  3070. });
  3071. }
  3072. if (!Uint8Array.prototype.slice) {
  3073. Object.defineProperty(Uint8Array.prototype, "slice", {
  3074. // wrap the slice in Uint8Array so it looks like a Uint8Array.slice call
  3075. // eslint-disable-next-line object-shorthand
  3076. value: function (start, end) { return new Uint8Array(Array.prototype.slice.call(this, start, end)); },
  3077. writable: true,
  3078. });
  3079. }
  3080. if (!Uint8Array.prototype.forEach) {
  3081. Object.defineProperty(Uint8Array.prototype, "forEach", {
  3082. value: Array.prototype.forEach,
  3083. writable: true,
  3084. });
  3085. }
  3086. /******/ return __webpack_exports__;
  3087. /******/ })()
  3088. ;
  3089. });
  3090. //# sourceMappingURL=signalr.js.map