module-z3bvLlVg.mjs 122 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928
  1. /**
  2. * @license Angular v19.2.13
  3. * (c) 2010-2025 Google LLC. https://angular.io/
  4. * License: MIT
  5. */
  6. import * as i0 from '@angular/core';
  7. import { ɵRuntimeError as _RuntimeError, Injectable, inject, NgZone, DestroyRef, InjectionToken, ɵPendingTasksInternal as _PendingTasksInternal, PLATFORM_ID, ɵConsole as _Console, ɵformatRuntimeError as _formatRuntimeError, runInInjectionContext, Inject, makeEnvironmentProviders, NgModule } from '@angular/core';
  8. import { concatMap, filter, map, finalize, switchMap } from 'rxjs/operators';
  9. import { of, Observable, from } from 'rxjs';
  10. import { isPlatformServer, XhrFactory, parseCookieValue } from './xhr-BfNfxNDv.mjs';
  11. import { DOCUMENT } from './dom_tokens-rA0ACyx7.mjs';
  12. /**
  13. * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a
  14. * `HttpResponse`.
  15. *
  16. * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the
  17. * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the
  18. * `HttpBackend`.
  19. *
  20. * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain.
  21. *
  22. * @publicApi
  23. */
  24. class HttpHandler {
  25. }
  26. /**
  27. * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend.
  28. *
  29. * Interceptors sit between the `HttpClient` interface and the `HttpBackend`.
  30. *
  31. * When injected, `HttpBackend` dispatches requests directly to the backend, without going
  32. * through the interceptor chain.
  33. *
  34. * @publicApi
  35. */
  36. class HttpBackend {
  37. }
  38. /**
  39. * Represents the header configuration options for an HTTP request.
  40. * Instances are immutable. Modifying methods return a cloned
  41. * instance with the change. The original object is never changed.
  42. *
  43. * @publicApi
  44. */
  45. class HttpHeaders {
  46. /**
  47. * Internal map of lowercase header names to values.
  48. */
  49. headers;
  50. /**
  51. * Internal map of lowercased header names to the normalized
  52. * form of the name (the form seen first).
  53. */
  54. normalizedNames = new Map();
  55. /**
  56. * Complete the lazy initialization of this object (needed before reading).
  57. */
  58. lazyInit;
  59. /**
  60. * Queued updates to be materialized the next initialization.
  61. */
  62. lazyUpdate = null;
  63. /** Constructs a new HTTP header object with the given values.*/
  64. constructor(headers) {
  65. if (!headers) {
  66. this.headers = new Map();
  67. }
  68. else if (typeof headers === 'string') {
  69. this.lazyInit = () => {
  70. this.headers = new Map();
  71. headers.split('\n').forEach((line) => {
  72. const index = line.indexOf(':');
  73. if (index > 0) {
  74. const name = line.slice(0, index);
  75. const value = line.slice(index + 1).trim();
  76. this.addHeaderEntry(name, value);
  77. }
  78. });
  79. };
  80. }
  81. else if (typeof Headers !== 'undefined' && headers instanceof Headers) {
  82. this.headers = new Map();
  83. headers.forEach((value, name) => {
  84. this.addHeaderEntry(name, value);
  85. });
  86. }
  87. else {
  88. this.lazyInit = () => {
  89. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  90. assertValidHeaders(headers);
  91. }
  92. this.headers = new Map();
  93. Object.entries(headers).forEach(([name, values]) => {
  94. this.setHeaderEntries(name, values);
  95. });
  96. };
  97. }
  98. }
  99. /**
  100. * Checks for existence of a given header.
  101. *
  102. * @param name The header name to check for existence.
  103. *
  104. * @returns True if the header exists, false otherwise.
  105. */
  106. has(name) {
  107. this.init();
  108. return this.headers.has(name.toLowerCase());
  109. }
  110. /**
  111. * Retrieves the first value of a given header.
  112. *
  113. * @param name The header name.
  114. *
  115. * @returns The value string if the header exists, null otherwise
  116. */
  117. get(name) {
  118. this.init();
  119. const values = this.headers.get(name.toLowerCase());
  120. return values && values.length > 0 ? values[0] : null;
  121. }
  122. /**
  123. * Retrieves the names of the headers.
  124. *
  125. * @returns A list of header names.
  126. */
  127. keys() {
  128. this.init();
  129. return Array.from(this.normalizedNames.values());
  130. }
  131. /**
  132. * Retrieves a list of values for a given header.
  133. *
  134. * @param name The header name from which to retrieve values.
  135. *
  136. * @returns A string of values if the header exists, null otherwise.
  137. */
  138. getAll(name) {
  139. this.init();
  140. return this.headers.get(name.toLowerCase()) || null;
  141. }
  142. /**
  143. * Appends a new value to the existing set of values for a header
  144. * and returns them in a clone of the original instance.
  145. *
  146. * @param name The header name for which to append the values.
  147. * @param value The value to append.
  148. *
  149. * @returns A clone of the HTTP headers object with the value appended to the given header.
  150. */
  151. append(name, value) {
  152. return this.clone({ name, value, op: 'a' });
  153. }
  154. /**
  155. * Sets or modifies a value for a given header in a clone of the original instance.
  156. * If the header already exists, its value is replaced with the given value
  157. * in the returned object.
  158. *
  159. * @param name The header name.
  160. * @param value The value or values to set or override for the given header.
  161. *
  162. * @returns A clone of the HTTP headers object with the newly set header value.
  163. */
  164. set(name, value) {
  165. return this.clone({ name, value, op: 's' });
  166. }
  167. /**
  168. * Deletes values for a given header in a clone of the original instance.
  169. *
  170. * @param name The header name.
  171. * @param value The value or values to delete for the given header.
  172. *
  173. * @returns A clone of the HTTP headers object with the given value deleted.
  174. */
  175. delete(name, value) {
  176. return this.clone({ name, value, op: 'd' });
  177. }
  178. maybeSetNormalizedName(name, lcName) {
  179. if (!this.normalizedNames.has(lcName)) {
  180. this.normalizedNames.set(lcName, name);
  181. }
  182. }
  183. init() {
  184. if (!!this.lazyInit) {
  185. if (this.lazyInit instanceof HttpHeaders) {
  186. this.copyFrom(this.lazyInit);
  187. }
  188. else {
  189. this.lazyInit();
  190. }
  191. this.lazyInit = null;
  192. if (!!this.lazyUpdate) {
  193. this.lazyUpdate.forEach((update) => this.applyUpdate(update));
  194. this.lazyUpdate = null;
  195. }
  196. }
  197. }
  198. copyFrom(other) {
  199. other.init();
  200. Array.from(other.headers.keys()).forEach((key) => {
  201. this.headers.set(key, other.headers.get(key));
  202. this.normalizedNames.set(key, other.normalizedNames.get(key));
  203. });
  204. }
  205. clone(update) {
  206. const clone = new HttpHeaders();
  207. clone.lazyInit = !!this.lazyInit && this.lazyInit instanceof HttpHeaders ? this.lazyInit : this;
  208. clone.lazyUpdate = (this.lazyUpdate || []).concat([update]);
  209. return clone;
  210. }
  211. applyUpdate(update) {
  212. const key = update.name.toLowerCase();
  213. switch (update.op) {
  214. case 'a':
  215. case 's':
  216. let value = update.value;
  217. if (typeof value === 'string') {
  218. value = [value];
  219. }
  220. if (value.length === 0) {
  221. return;
  222. }
  223. this.maybeSetNormalizedName(update.name, key);
  224. const base = (update.op === 'a' ? this.headers.get(key) : undefined) || [];
  225. base.push(...value);
  226. this.headers.set(key, base);
  227. break;
  228. case 'd':
  229. const toDelete = update.value;
  230. if (!toDelete) {
  231. this.headers.delete(key);
  232. this.normalizedNames.delete(key);
  233. }
  234. else {
  235. let existing = this.headers.get(key);
  236. if (!existing) {
  237. return;
  238. }
  239. existing = existing.filter((value) => toDelete.indexOf(value) === -1);
  240. if (existing.length === 0) {
  241. this.headers.delete(key);
  242. this.normalizedNames.delete(key);
  243. }
  244. else {
  245. this.headers.set(key, existing);
  246. }
  247. }
  248. break;
  249. }
  250. }
  251. addHeaderEntry(name, value) {
  252. const key = name.toLowerCase();
  253. this.maybeSetNormalizedName(name, key);
  254. if (this.headers.has(key)) {
  255. this.headers.get(key).push(value);
  256. }
  257. else {
  258. this.headers.set(key, [value]);
  259. }
  260. }
  261. setHeaderEntries(name, values) {
  262. const headerValues = (Array.isArray(values) ? values : [values]).map((value) => value.toString());
  263. const key = name.toLowerCase();
  264. this.headers.set(key, headerValues);
  265. this.maybeSetNormalizedName(name, key);
  266. }
  267. /**
  268. * @internal
  269. */
  270. forEach(fn) {
  271. this.init();
  272. Array.from(this.normalizedNames.keys()).forEach((key) => fn(this.normalizedNames.get(key), this.headers.get(key)));
  273. }
  274. }
  275. /**
  276. * Verifies that the headers object has the right shape: the values
  277. * must be either strings, numbers or arrays. Throws an error if an invalid
  278. * header value is present.
  279. */
  280. function assertValidHeaders(headers) {
  281. for (const [key, value] of Object.entries(headers)) {
  282. if (!(typeof value === 'string' || typeof value === 'number') && !Array.isArray(value)) {
  283. throw new Error(`Unexpected value of the \`${key}\` header provided. ` +
  284. `Expecting either a string, a number or an array, but got: \`${value}\`.`);
  285. }
  286. }
  287. }
  288. /**
  289. * Provides encoding and decoding of URL parameter and query-string values.
  290. *
  291. * Serializes and parses URL parameter keys and values to encode and decode them.
  292. * If you pass URL query parameters without encoding,
  293. * the query parameters can be misinterpreted at the receiving end.
  294. *
  295. *
  296. * @publicApi
  297. */
  298. class HttpUrlEncodingCodec {
  299. /**
  300. * Encodes a key name for a URL parameter or query-string.
  301. * @param key The key name.
  302. * @returns The encoded key name.
  303. */
  304. encodeKey(key) {
  305. return standardEncoding(key);
  306. }
  307. /**
  308. * Encodes the value of a URL parameter or query-string.
  309. * @param value The value.
  310. * @returns The encoded value.
  311. */
  312. encodeValue(value) {
  313. return standardEncoding(value);
  314. }
  315. /**
  316. * Decodes an encoded URL parameter or query-string key.
  317. * @param key The encoded key name.
  318. * @returns The decoded key name.
  319. */
  320. decodeKey(key) {
  321. return decodeURIComponent(key);
  322. }
  323. /**
  324. * Decodes an encoded URL parameter or query-string value.
  325. * @param value The encoded value.
  326. * @returns The decoded value.
  327. */
  328. decodeValue(value) {
  329. return decodeURIComponent(value);
  330. }
  331. }
  332. function paramParser(rawParams, codec) {
  333. const map = new Map();
  334. if (rawParams.length > 0) {
  335. // The `window.location.search` can be used while creating an instance of the `HttpParams` class
  336. // (e.g. `new HttpParams({ fromString: window.location.search })`). The `window.location.search`
  337. // may start with the `?` char, so we strip it if it's present.
  338. const params = rawParams.replace(/^\?/, '').split('&');
  339. params.forEach((param) => {
  340. const eqIdx = param.indexOf('=');
  341. const [key, val] = eqIdx == -1
  342. ? [codec.decodeKey(param), '']
  343. : [codec.decodeKey(param.slice(0, eqIdx)), codec.decodeValue(param.slice(eqIdx + 1))];
  344. const list = map.get(key) || [];
  345. list.push(val);
  346. map.set(key, list);
  347. });
  348. }
  349. return map;
  350. }
  351. /**
  352. * Encode input string with standard encodeURIComponent and then un-encode specific characters.
  353. */
  354. const STANDARD_ENCODING_REGEX = /%(\d[a-f0-9])/gi;
  355. const STANDARD_ENCODING_REPLACEMENTS = {
  356. '40': '@',
  357. '3A': ':',
  358. '24': '$',
  359. '2C': ',',
  360. '3B': ';',
  361. '3D': '=',
  362. '3F': '?',
  363. '2F': '/',
  364. };
  365. function standardEncoding(v) {
  366. return encodeURIComponent(v).replace(STANDARD_ENCODING_REGEX, (s, t) => STANDARD_ENCODING_REPLACEMENTS[t] ?? s);
  367. }
  368. function valueToString(value) {
  369. return `${value}`;
  370. }
  371. /**
  372. * An HTTP request/response body that represents serialized parameters,
  373. * per the MIME type `application/x-www-form-urlencoded`.
  374. *
  375. * This class is immutable; all mutation operations return a new instance.
  376. *
  377. * @publicApi
  378. */
  379. class HttpParams {
  380. map;
  381. encoder;
  382. updates = null;
  383. cloneFrom = null;
  384. constructor(options = {}) {
  385. this.encoder = options.encoder || new HttpUrlEncodingCodec();
  386. if (options.fromString) {
  387. if (options.fromObject) {
  388. throw new _RuntimeError(2805 /* RuntimeErrorCode.CANNOT_SPECIFY_BOTH_FROM_STRING_AND_FROM_OBJECT */, ngDevMode && 'Cannot specify both fromString and fromObject.');
  389. }
  390. this.map = paramParser(options.fromString, this.encoder);
  391. }
  392. else if (!!options.fromObject) {
  393. this.map = new Map();
  394. Object.keys(options.fromObject).forEach((key) => {
  395. const value = options.fromObject[key];
  396. // convert the values to strings
  397. const values = Array.isArray(value) ? value.map(valueToString) : [valueToString(value)];
  398. this.map.set(key, values);
  399. });
  400. }
  401. else {
  402. this.map = null;
  403. }
  404. }
  405. /**
  406. * Reports whether the body includes one or more values for a given parameter.
  407. * @param param The parameter name.
  408. * @returns True if the parameter has one or more values,
  409. * false if it has no value or is not present.
  410. */
  411. has(param) {
  412. this.init();
  413. return this.map.has(param);
  414. }
  415. /**
  416. * Retrieves the first value for a parameter.
  417. * @param param The parameter name.
  418. * @returns The first value of the given parameter,
  419. * or `null` if the parameter is not present.
  420. */
  421. get(param) {
  422. this.init();
  423. const res = this.map.get(param);
  424. return !!res ? res[0] : null;
  425. }
  426. /**
  427. * Retrieves all values for a parameter.
  428. * @param param The parameter name.
  429. * @returns All values in a string array,
  430. * or `null` if the parameter not present.
  431. */
  432. getAll(param) {
  433. this.init();
  434. return this.map.get(param) || null;
  435. }
  436. /**
  437. * Retrieves all the parameters for this body.
  438. * @returns The parameter names in a string array.
  439. */
  440. keys() {
  441. this.init();
  442. return Array.from(this.map.keys());
  443. }
  444. /**
  445. * Appends a new value to existing values for a parameter.
  446. * @param param The parameter name.
  447. * @param value The new value to add.
  448. * @return A new body with the appended value.
  449. */
  450. append(param, value) {
  451. return this.clone({ param, value, op: 'a' });
  452. }
  453. /**
  454. * Constructs a new body with appended values for the given parameter name.
  455. * @param params parameters and values
  456. * @return A new body with the new value.
  457. */
  458. appendAll(params) {
  459. const updates = [];
  460. Object.keys(params).forEach((param) => {
  461. const value = params[param];
  462. if (Array.isArray(value)) {
  463. value.forEach((_value) => {
  464. updates.push({ param, value: _value, op: 'a' });
  465. });
  466. }
  467. else {
  468. updates.push({ param, value: value, op: 'a' });
  469. }
  470. });
  471. return this.clone(updates);
  472. }
  473. /**
  474. * Replaces the value for a parameter.
  475. * @param param The parameter name.
  476. * @param value The new value.
  477. * @return A new body with the new value.
  478. */
  479. set(param, value) {
  480. return this.clone({ param, value, op: 's' });
  481. }
  482. /**
  483. * Removes a given value or all values from a parameter.
  484. * @param param The parameter name.
  485. * @param value The value to remove, if provided.
  486. * @return A new body with the given value removed, or with all values
  487. * removed if no value is specified.
  488. */
  489. delete(param, value) {
  490. return this.clone({ param, value, op: 'd' });
  491. }
  492. /**
  493. * Serializes the body to an encoded string, where key-value pairs (separated by `=`) are
  494. * separated by `&`s.
  495. */
  496. toString() {
  497. this.init();
  498. return (this.keys()
  499. .map((key) => {
  500. const eKey = this.encoder.encodeKey(key);
  501. // `a: ['1']` produces `'a=1'`
  502. // `b: []` produces `''`
  503. // `c: ['1', '2']` produces `'c=1&c=2'`
  504. return this.map.get(key)
  505. .map((value) => eKey + '=' + this.encoder.encodeValue(value))
  506. .join('&');
  507. })
  508. // filter out empty values because `b: []` produces `''`
  509. // which results in `a=1&&c=1&c=2` instead of `a=1&c=1&c=2` if we don't
  510. .filter((param) => param !== '')
  511. .join('&'));
  512. }
  513. clone(update) {
  514. const clone = new HttpParams({ encoder: this.encoder });
  515. clone.cloneFrom = this.cloneFrom || this;
  516. clone.updates = (this.updates || []).concat(update);
  517. return clone;
  518. }
  519. init() {
  520. if (this.map === null) {
  521. this.map = new Map();
  522. }
  523. if (this.cloneFrom !== null) {
  524. this.cloneFrom.init();
  525. this.cloneFrom.keys().forEach((key) => this.map.set(key, this.cloneFrom.map.get(key)));
  526. this.updates.forEach((update) => {
  527. switch (update.op) {
  528. case 'a':
  529. case 's':
  530. const base = (update.op === 'a' ? this.map.get(update.param) : undefined) || [];
  531. base.push(valueToString(update.value));
  532. this.map.set(update.param, base);
  533. break;
  534. case 'd':
  535. if (update.value !== undefined) {
  536. let base = this.map.get(update.param) || [];
  537. const idx = base.indexOf(valueToString(update.value));
  538. if (idx !== -1) {
  539. base.splice(idx, 1);
  540. }
  541. if (base.length > 0) {
  542. this.map.set(update.param, base);
  543. }
  544. else {
  545. this.map.delete(update.param);
  546. }
  547. }
  548. else {
  549. this.map.delete(update.param);
  550. break;
  551. }
  552. }
  553. });
  554. this.cloneFrom = this.updates = null;
  555. }
  556. }
  557. }
  558. /**
  559. * A token used to manipulate and access values stored in `HttpContext`.
  560. *
  561. * @publicApi
  562. */
  563. class HttpContextToken {
  564. defaultValue;
  565. constructor(defaultValue) {
  566. this.defaultValue = defaultValue;
  567. }
  568. }
  569. /**
  570. * Http context stores arbitrary user defined values and ensures type safety without
  571. * actually knowing the types. It is backed by a `Map` and guarantees that keys do not clash.
  572. *
  573. * This context is mutable and is shared between cloned requests unless explicitly specified.
  574. *
  575. * @usageNotes
  576. *
  577. * ### Usage Example
  578. *
  579. * ```ts
  580. * // inside cache.interceptors.ts
  581. * export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false);
  582. *
  583. * export class CacheInterceptor implements HttpInterceptor {
  584. *
  585. * intercept(req: HttpRequest<any>, delegate: HttpHandler): Observable<HttpEvent<any>> {
  586. * if (req.context.get(IS_CACHE_ENABLED) === true) {
  587. * return ...;
  588. * }
  589. * return delegate.handle(req);
  590. * }
  591. * }
  592. *
  593. * // inside a service
  594. *
  595. * this.httpClient.get('/api/weather', {
  596. * context: new HttpContext().set(IS_CACHE_ENABLED, true)
  597. * }).subscribe(...);
  598. * ```
  599. *
  600. * @publicApi
  601. */
  602. class HttpContext {
  603. map = new Map();
  604. /**
  605. * Store a value in the context. If a value is already present it will be overwritten.
  606. *
  607. * @param token The reference to an instance of `HttpContextToken`.
  608. * @param value The value to store.
  609. *
  610. * @returns A reference to itself for easy chaining.
  611. */
  612. set(token, value) {
  613. this.map.set(token, value);
  614. return this;
  615. }
  616. /**
  617. * Retrieve the value associated with the given token.
  618. *
  619. * @param token The reference to an instance of `HttpContextToken`.
  620. *
  621. * @returns The stored value or default if one is defined.
  622. */
  623. get(token) {
  624. if (!this.map.has(token)) {
  625. this.map.set(token, token.defaultValue());
  626. }
  627. return this.map.get(token);
  628. }
  629. /**
  630. * Delete the value associated with the given token.
  631. *
  632. * @param token The reference to an instance of `HttpContextToken`.
  633. *
  634. * @returns A reference to itself for easy chaining.
  635. */
  636. delete(token) {
  637. this.map.delete(token);
  638. return this;
  639. }
  640. /**
  641. * Checks for existence of a given token.
  642. *
  643. * @param token The reference to an instance of `HttpContextToken`.
  644. *
  645. * @returns True if the token exists, false otherwise.
  646. */
  647. has(token) {
  648. return this.map.has(token);
  649. }
  650. /**
  651. * @returns a list of tokens currently stored in the context.
  652. */
  653. keys() {
  654. return this.map.keys();
  655. }
  656. }
  657. /**
  658. * Determine whether the given HTTP method may include a body.
  659. */
  660. function mightHaveBody(method) {
  661. switch (method) {
  662. case 'DELETE':
  663. case 'GET':
  664. case 'HEAD':
  665. case 'OPTIONS':
  666. case 'JSONP':
  667. return false;
  668. default:
  669. return true;
  670. }
  671. }
  672. /**
  673. * Safely assert whether the given value is an ArrayBuffer.
  674. *
  675. * In some execution environments ArrayBuffer is not defined.
  676. */
  677. function isArrayBuffer(value) {
  678. return typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer;
  679. }
  680. /**
  681. * Safely assert whether the given value is a Blob.
  682. *
  683. * In some execution environments Blob is not defined.
  684. */
  685. function isBlob(value) {
  686. return typeof Blob !== 'undefined' && value instanceof Blob;
  687. }
  688. /**
  689. * Safely assert whether the given value is a FormData instance.
  690. *
  691. * In some execution environments FormData is not defined.
  692. */
  693. function isFormData(value) {
  694. return typeof FormData !== 'undefined' && value instanceof FormData;
  695. }
  696. /**
  697. * Safely assert whether the given value is a URLSearchParams instance.
  698. *
  699. * In some execution environments URLSearchParams is not defined.
  700. */
  701. function isUrlSearchParams(value) {
  702. return typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams;
  703. }
  704. /**
  705. * `Content-Type` is an HTTP header used to indicate the media type
  706. * (also known as MIME type) of the resource being sent to the client
  707. * or received from the server.
  708. */
  709. const CONTENT_TYPE_HEADER = 'Content-Type';
  710. /**
  711. * The `Accept` header is an HTTP request header that indicates the media types
  712. * (or content types) the client is willing to receive from the server.
  713. */
  714. const ACCEPT_HEADER = 'Accept';
  715. /**
  716. * `X-Request-URL` is a custom HTTP header used in older browser versions,
  717. * including Firefox (< 32), Chrome (< 37), Safari (< 8), and Internet Explorer,
  718. * to include the full URL of the request in cross-origin requests.
  719. */
  720. const X_REQUEST_URL_HEADER = 'X-Request-URL';
  721. /**
  722. * `text/plain` is a content type used to indicate that the content being
  723. * sent is plain text with no special formatting or structured data
  724. * like HTML, XML, or JSON.
  725. */
  726. const TEXT_CONTENT_TYPE = 'text/plain';
  727. /**
  728. * `application/json` is a content type used to indicate that the content
  729. * being sent is in the JSON format.
  730. */
  731. const JSON_CONTENT_TYPE = 'application/json';
  732. /**
  733. * `application/json, text/plain, *\/*` is a content negotiation string often seen in the
  734. * Accept header of HTTP requests. It indicates the types of content the client is willing
  735. * to accept from the server, with a preference for `application/json` and `text/plain`,
  736. * but also accepting any other type (*\/*).
  737. */
  738. const ACCEPT_HEADER_VALUE = `${JSON_CONTENT_TYPE}, ${TEXT_CONTENT_TYPE}, */*`;
  739. /**
  740. * An outgoing HTTP request with an optional typed body.
  741. *
  742. * `HttpRequest` represents an outgoing request, including URL, method,
  743. * headers, body, and other request configuration options. Instances should be
  744. * assumed to be immutable. To modify a `HttpRequest`, the `clone`
  745. * method should be used.
  746. *
  747. * @publicApi
  748. */
  749. class HttpRequest {
  750. url;
  751. /**
  752. * The request body, or `null` if one isn't set.
  753. *
  754. * Bodies are not enforced to be immutable, as they can include a reference to any
  755. * user-defined data type. However, interceptors should take care to preserve
  756. * idempotence by treating them as such.
  757. */
  758. body = null;
  759. /**
  760. * Outgoing headers for this request.
  761. */
  762. headers;
  763. /**
  764. * Shared and mutable context that can be used by interceptors
  765. */
  766. context;
  767. /**
  768. * Whether this request should be made in a way that exposes progress events.
  769. *
  770. * Progress events are expensive (change detection runs on each event) and so
  771. * they should only be requested if the consumer intends to monitor them.
  772. *
  773. * Note: The `FetchBackend` doesn't support progress report on uploads.
  774. */
  775. reportProgress = false;
  776. /**
  777. * Whether this request should be sent with outgoing credentials (cookies).
  778. */
  779. withCredentials = false;
  780. /**
  781. * The expected response type of the server.
  782. *
  783. * This is used to parse the response appropriately before returning it to
  784. * the requestee.
  785. */
  786. responseType = 'json';
  787. /**
  788. * The outgoing HTTP request method.
  789. */
  790. method;
  791. /**
  792. * Outgoing URL parameters.
  793. *
  794. * To pass a string representation of HTTP parameters in the URL-query-string format,
  795. * the `HttpParamsOptions`' `fromString` may be used. For example:
  796. *
  797. * ```ts
  798. * new HttpParams({fromString: 'angular=awesome'})
  799. * ```
  800. */
  801. params;
  802. /**
  803. * The outgoing URL with all URL parameters set.
  804. */
  805. urlWithParams;
  806. /**
  807. * The HttpTransferCache option for the request
  808. */
  809. transferCache;
  810. constructor(method, url, third, fourth) {
  811. this.url = url;
  812. this.method = method.toUpperCase();
  813. // Next, need to figure out which argument holds the HttpRequestInit
  814. // options, if any.
  815. let options;
  816. // Check whether a body argument is expected. The only valid way to omit
  817. // the body argument is to use a known no-body method like GET.
  818. if (mightHaveBody(this.method) || !!fourth) {
  819. // Body is the third argument, options are the fourth.
  820. this.body = third !== undefined ? third : null;
  821. options = fourth;
  822. }
  823. else {
  824. // No body required, options are the third argument. The body stays null.
  825. options = third;
  826. }
  827. // If options have been passed, interpret them.
  828. if (options) {
  829. // Normalize reportProgress and withCredentials.
  830. this.reportProgress = !!options.reportProgress;
  831. this.withCredentials = !!options.withCredentials;
  832. // Override default response type of 'json' if one is provided.
  833. if (!!options.responseType) {
  834. this.responseType = options.responseType;
  835. }
  836. // Override headers if they're provided.
  837. if (!!options.headers) {
  838. this.headers = options.headers;
  839. }
  840. if (!!options.context) {
  841. this.context = options.context;
  842. }
  843. if (!!options.params) {
  844. this.params = options.params;
  845. }
  846. // We do want to assign transferCache even if it's falsy (false is valid value)
  847. this.transferCache = options.transferCache;
  848. }
  849. // If no headers have been passed in, construct a new HttpHeaders instance.
  850. this.headers ??= new HttpHeaders();
  851. // If no context have been passed in, construct a new HttpContext instance.
  852. this.context ??= new HttpContext();
  853. // If no parameters have been passed in, construct a new HttpUrlEncodedParams instance.
  854. if (!this.params) {
  855. this.params = new HttpParams();
  856. this.urlWithParams = url;
  857. }
  858. else {
  859. // Encode the parameters to a string in preparation for inclusion in the URL.
  860. const params = this.params.toString();
  861. if (params.length === 0) {
  862. // No parameters, the visible URL is just the URL given at creation time.
  863. this.urlWithParams = url;
  864. }
  865. else {
  866. // Does the URL already have query parameters? Look for '?'.
  867. const qIdx = url.indexOf('?');
  868. // There are 3 cases to handle:
  869. // 1) No existing parameters -> append '?' followed by params.
  870. // 2) '?' exists and is followed by existing query string ->
  871. // append '&' followed by params.
  872. // 3) '?' exists at the end of the url -> append params directly.
  873. // This basically amounts to determining the character, if any, with
  874. // which to join the URL and parameters.
  875. const sep = qIdx === -1 ? '?' : qIdx < url.length - 1 ? '&' : '';
  876. this.urlWithParams = url + sep + params;
  877. }
  878. }
  879. }
  880. /**
  881. * Transform the free-form body into a serialized format suitable for
  882. * transmission to the server.
  883. */
  884. serializeBody() {
  885. // If no body is present, no need to serialize it.
  886. if (this.body === null) {
  887. return null;
  888. }
  889. // Check whether the body is already in a serialized form. If so,
  890. // it can just be returned directly.
  891. if (typeof this.body === 'string' ||
  892. isArrayBuffer(this.body) ||
  893. isBlob(this.body) ||
  894. isFormData(this.body) ||
  895. isUrlSearchParams(this.body)) {
  896. return this.body;
  897. }
  898. // Check whether the body is an instance of HttpUrlEncodedParams.
  899. if (this.body instanceof HttpParams) {
  900. return this.body.toString();
  901. }
  902. // Check whether the body is an object or array, and serialize with JSON if so.
  903. if (typeof this.body === 'object' ||
  904. typeof this.body === 'boolean' ||
  905. Array.isArray(this.body)) {
  906. return JSON.stringify(this.body);
  907. }
  908. // Fall back on toString() for everything else.
  909. return this.body.toString();
  910. }
  911. /**
  912. * Examine the body and attempt to infer an appropriate MIME type
  913. * for it.
  914. *
  915. * If no such type can be inferred, this method will return `null`.
  916. */
  917. detectContentTypeHeader() {
  918. // An empty body has no content type.
  919. if (this.body === null) {
  920. return null;
  921. }
  922. // FormData bodies rely on the browser's content type assignment.
  923. if (isFormData(this.body)) {
  924. return null;
  925. }
  926. // Blobs usually have their own content type. If it doesn't, then
  927. // no type can be inferred.
  928. if (isBlob(this.body)) {
  929. return this.body.type || null;
  930. }
  931. // Array buffers have unknown contents and thus no type can be inferred.
  932. if (isArrayBuffer(this.body)) {
  933. return null;
  934. }
  935. // Technically, strings could be a form of JSON data, but it's safe enough
  936. // to assume they're plain strings.
  937. if (typeof this.body === 'string') {
  938. return TEXT_CONTENT_TYPE;
  939. }
  940. // `HttpUrlEncodedParams` has its own content-type.
  941. if (this.body instanceof HttpParams) {
  942. return 'application/x-www-form-urlencoded;charset=UTF-8';
  943. }
  944. // Arrays, objects, boolean and numbers will be encoded as JSON.
  945. if (typeof this.body === 'object' ||
  946. typeof this.body === 'number' ||
  947. typeof this.body === 'boolean') {
  948. return JSON_CONTENT_TYPE;
  949. }
  950. // No type could be inferred.
  951. return null;
  952. }
  953. clone(update = {}) {
  954. // For method, url, and responseType, take the current value unless
  955. // it is overridden in the update hash.
  956. const method = update.method || this.method;
  957. const url = update.url || this.url;
  958. const responseType = update.responseType || this.responseType;
  959. // Carefully handle the transferCache to differentiate between
  960. // `false` and `undefined` in the update args.
  961. const transferCache = update.transferCache ?? this.transferCache;
  962. // The body is somewhat special - a `null` value in update.body means
  963. // whatever current body is present is being overridden with an empty
  964. // body, whereas an `undefined` value in update.body implies no
  965. // override.
  966. const body = update.body !== undefined ? update.body : this.body;
  967. // Carefully handle the boolean options to differentiate between
  968. // `false` and `undefined` in the update args.
  969. const withCredentials = update.withCredentials ?? this.withCredentials;
  970. const reportProgress = update.reportProgress ?? this.reportProgress;
  971. // Headers and params may be appended to if `setHeaders` or
  972. // `setParams` are used.
  973. let headers = update.headers || this.headers;
  974. let params = update.params || this.params;
  975. // Pass on context if needed
  976. const context = update.context ?? this.context;
  977. // Check whether the caller has asked to add headers.
  978. if (update.setHeaders !== undefined) {
  979. // Set every requested header.
  980. headers = Object.keys(update.setHeaders).reduce((headers, name) => headers.set(name, update.setHeaders[name]), headers);
  981. }
  982. // Check whether the caller has asked to set params.
  983. if (update.setParams) {
  984. // Set every requested param.
  985. params = Object.keys(update.setParams).reduce((params, param) => params.set(param, update.setParams[param]), params);
  986. }
  987. // Finally, construct the new HttpRequest using the pieces from above.
  988. return new HttpRequest(method, url, body, {
  989. params,
  990. headers,
  991. context,
  992. reportProgress,
  993. responseType,
  994. withCredentials,
  995. transferCache,
  996. });
  997. }
  998. }
  999. /**
  1000. * Type enumeration for the different kinds of `HttpEvent`.
  1001. *
  1002. * @publicApi
  1003. */
  1004. var HttpEventType;
  1005. (function (HttpEventType) {
  1006. /**
  1007. * The request was sent out over the wire.
  1008. */
  1009. HttpEventType[HttpEventType["Sent"] = 0] = "Sent";
  1010. /**
  1011. * An upload progress event was received.
  1012. *
  1013. * Note: The `FetchBackend` doesn't support progress report on uploads.
  1014. */
  1015. HttpEventType[HttpEventType["UploadProgress"] = 1] = "UploadProgress";
  1016. /**
  1017. * The response status code and headers were received.
  1018. */
  1019. HttpEventType[HttpEventType["ResponseHeader"] = 2] = "ResponseHeader";
  1020. /**
  1021. * A download progress event was received.
  1022. */
  1023. HttpEventType[HttpEventType["DownloadProgress"] = 3] = "DownloadProgress";
  1024. /**
  1025. * The full response including the body was received.
  1026. */
  1027. HttpEventType[HttpEventType["Response"] = 4] = "Response";
  1028. /**
  1029. * A custom event from an interceptor or a backend.
  1030. */
  1031. HttpEventType[HttpEventType["User"] = 5] = "User";
  1032. })(HttpEventType || (HttpEventType = {}));
  1033. /**
  1034. * Base class for both `HttpResponse` and `HttpHeaderResponse`.
  1035. *
  1036. * @publicApi
  1037. */
  1038. class HttpResponseBase {
  1039. /**
  1040. * All response headers.
  1041. */
  1042. headers;
  1043. /**
  1044. * Response status code.
  1045. */
  1046. status;
  1047. /**
  1048. * Textual description of response status code, defaults to OK.
  1049. *
  1050. * Do not depend on this.
  1051. */
  1052. statusText;
  1053. /**
  1054. * URL of the resource retrieved, or null if not available.
  1055. */
  1056. url;
  1057. /**
  1058. * Whether the status code falls in the 2xx range.
  1059. */
  1060. ok;
  1061. /**
  1062. * Type of the response, narrowed to either the full response or the header.
  1063. */
  1064. type;
  1065. /**
  1066. * Super-constructor for all responses.
  1067. *
  1068. * The single parameter accepted is an initialization hash. Any properties
  1069. * of the response passed there will override the default values.
  1070. */
  1071. constructor(init, defaultStatus = 200, defaultStatusText = 'OK') {
  1072. // If the hash has values passed, use them to initialize the response.
  1073. // Otherwise use the default values.
  1074. this.headers = init.headers || new HttpHeaders();
  1075. this.status = init.status !== undefined ? init.status : defaultStatus;
  1076. this.statusText = init.statusText || defaultStatusText;
  1077. this.url = init.url || null;
  1078. // Cache the ok value to avoid defining a getter.
  1079. this.ok = this.status >= 200 && this.status < 300;
  1080. }
  1081. }
  1082. /**
  1083. * A partial HTTP response which only includes the status and header data,
  1084. * but no response body.
  1085. *
  1086. * `HttpHeaderResponse` is a `HttpEvent` available on the response
  1087. * event stream, only when progress events are requested.
  1088. *
  1089. * @publicApi
  1090. */
  1091. class HttpHeaderResponse extends HttpResponseBase {
  1092. /**
  1093. * Create a new `HttpHeaderResponse` with the given parameters.
  1094. */
  1095. constructor(init = {}) {
  1096. super(init);
  1097. }
  1098. type = HttpEventType.ResponseHeader;
  1099. /**
  1100. * Copy this `HttpHeaderResponse`, overriding its contents with the
  1101. * given parameter hash.
  1102. */
  1103. clone(update = {}) {
  1104. // Perform a straightforward initialization of the new HttpHeaderResponse,
  1105. // overriding the current parameters with new ones if given.
  1106. return new HttpHeaderResponse({
  1107. headers: update.headers || this.headers,
  1108. status: update.status !== undefined ? update.status : this.status,
  1109. statusText: update.statusText || this.statusText,
  1110. url: update.url || this.url || undefined,
  1111. });
  1112. }
  1113. }
  1114. /**
  1115. * A full HTTP response, including a typed response body (which may be `null`
  1116. * if one was not returned).
  1117. *
  1118. * `HttpResponse` is a `HttpEvent` available on the response event
  1119. * stream.
  1120. *
  1121. * @publicApi
  1122. */
  1123. class HttpResponse extends HttpResponseBase {
  1124. /**
  1125. * The response body, or `null` if one was not returned.
  1126. */
  1127. body;
  1128. /**
  1129. * Construct a new `HttpResponse`.
  1130. */
  1131. constructor(init = {}) {
  1132. super(init);
  1133. this.body = init.body !== undefined ? init.body : null;
  1134. }
  1135. type = HttpEventType.Response;
  1136. clone(update = {}) {
  1137. return new HttpResponse({
  1138. body: update.body !== undefined ? update.body : this.body,
  1139. headers: update.headers || this.headers,
  1140. status: update.status !== undefined ? update.status : this.status,
  1141. statusText: update.statusText || this.statusText,
  1142. url: update.url || this.url || undefined,
  1143. });
  1144. }
  1145. }
  1146. /**
  1147. * A response that represents an error or failure, either from a
  1148. * non-successful HTTP status, an error while executing the request,
  1149. * or some other failure which occurred during the parsing of the response.
  1150. *
  1151. * Any error returned on the `Observable` response stream will be
  1152. * wrapped in an `HttpErrorResponse` to provide additional context about
  1153. * the state of the HTTP layer when the error occurred. The error property
  1154. * will contain either a wrapped Error object or the error response returned
  1155. * from the server.
  1156. *
  1157. * @publicApi
  1158. */
  1159. class HttpErrorResponse extends HttpResponseBase {
  1160. name = 'HttpErrorResponse';
  1161. message;
  1162. error;
  1163. /**
  1164. * Errors are never okay, even when the status code is in the 2xx success range.
  1165. */
  1166. ok = false;
  1167. constructor(init) {
  1168. // Initialize with a default status of 0 / Unknown Error.
  1169. super(init, 0, 'Unknown Error');
  1170. // If the response was successful, then this was a parse error. Otherwise, it was
  1171. // a protocol-level failure of some sort. Either the request failed in transit
  1172. // or the server returned an unsuccessful status code.
  1173. if (this.status >= 200 && this.status < 300) {
  1174. this.message = `Http failure during parsing for ${init.url || '(unknown url)'}`;
  1175. }
  1176. else {
  1177. this.message = `Http failure response for ${init.url || '(unknown url)'}: ${init.status} ${init.statusText}`;
  1178. }
  1179. this.error = init.error || null;
  1180. }
  1181. }
  1182. /**
  1183. * We use these constant to prevent pulling the whole HttpStatusCode enum
  1184. * Those are the only ones referenced directly by the framework
  1185. */
  1186. const HTTP_STATUS_CODE_OK = 200;
  1187. const HTTP_STATUS_CODE_NO_CONTENT = 204;
  1188. /**
  1189. * Http status codes.
  1190. * As per https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
  1191. * @publicApi
  1192. */
  1193. var HttpStatusCode;
  1194. (function (HttpStatusCode) {
  1195. HttpStatusCode[HttpStatusCode["Continue"] = 100] = "Continue";
  1196. HttpStatusCode[HttpStatusCode["SwitchingProtocols"] = 101] = "SwitchingProtocols";
  1197. HttpStatusCode[HttpStatusCode["Processing"] = 102] = "Processing";
  1198. HttpStatusCode[HttpStatusCode["EarlyHints"] = 103] = "EarlyHints";
  1199. HttpStatusCode[HttpStatusCode["Ok"] = 200] = "Ok";
  1200. HttpStatusCode[HttpStatusCode["Created"] = 201] = "Created";
  1201. HttpStatusCode[HttpStatusCode["Accepted"] = 202] = "Accepted";
  1202. HttpStatusCode[HttpStatusCode["NonAuthoritativeInformation"] = 203] = "NonAuthoritativeInformation";
  1203. HttpStatusCode[HttpStatusCode["NoContent"] = 204] = "NoContent";
  1204. HttpStatusCode[HttpStatusCode["ResetContent"] = 205] = "ResetContent";
  1205. HttpStatusCode[HttpStatusCode["PartialContent"] = 206] = "PartialContent";
  1206. HttpStatusCode[HttpStatusCode["MultiStatus"] = 207] = "MultiStatus";
  1207. HttpStatusCode[HttpStatusCode["AlreadyReported"] = 208] = "AlreadyReported";
  1208. HttpStatusCode[HttpStatusCode["ImUsed"] = 226] = "ImUsed";
  1209. HttpStatusCode[HttpStatusCode["MultipleChoices"] = 300] = "MultipleChoices";
  1210. HttpStatusCode[HttpStatusCode["MovedPermanently"] = 301] = "MovedPermanently";
  1211. HttpStatusCode[HttpStatusCode["Found"] = 302] = "Found";
  1212. HttpStatusCode[HttpStatusCode["SeeOther"] = 303] = "SeeOther";
  1213. HttpStatusCode[HttpStatusCode["NotModified"] = 304] = "NotModified";
  1214. HttpStatusCode[HttpStatusCode["UseProxy"] = 305] = "UseProxy";
  1215. HttpStatusCode[HttpStatusCode["Unused"] = 306] = "Unused";
  1216. HttpStatusCode[HttpStatusCode["TemporaryRedirect"] = 307] = "TemporaryRedirect";
  1217. HttpStatusCode[HttpStatusCode["PermanentRedirect"] = 308] = "PermanentRedirect";
  1218. HttpStatusCode[HttpStatusCode["BadRequest"] = 400] = "BadRequest";
  1219. HttpStatusCode[HttpStatusCode["Unauthorized"] = 401] = "Unauthorized";
  1220. HttpStatusCode[HttpStatusCode["PaymentRequired"] = 402] = "PaymentRequired";
  1221. HttpStatusCode[HttpStatusCode["Forbidden"] = 403] = "Forbidden";
  1222. HttpStatusCode[HttpStatusCode["NotFound"] = 404] = "NotFound";
  1223. HttpStatusCode[HttpStatusCode["MethodNotAllowed"] = 405] = "MethodNotAllowed";
  1224. HttpStatusCode[HttpStatusCode["NotAcceptable"] = 406] = "NotAcceptable";
  1225. HttpStatusCode[HttpStatusCode["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
  1226. HttpStatusCode[HttpStatusCode["RequestTimeout"] = 408] = "RequestTimeout";
  1227. HttpStatusCode[HttpStatusCode["Conflict"] = 409] = "Conflict";
  1228. HttpStatusCode[HttpStatusCode["Gone"] = 410] = "Gone";
  1229. HttpStatusCode[HttpStatusCode["LengthRequired"] = 411] = "LengthRequired";
  1230. HttpStatusCode[HttpStatusCode["PreconditionFailed"] = 412] = "PreconditionFailed";
  1231. HttpStatusCode[HttpStatusCode["PayloadTooLarge"] = 413] = "PayloadTooLarge";
  1232. HttpStatusCode[HttpStatusCode["UriTooLong"] = 414] = "UriTooLong";
  1233. HttpStatusCode[HttpStatusCode["UnsupportedMediaType"] = 415] = "UnsupportedMediaType";
  1234. HttpStatusCode[HttpStatusCode["RangeNotSatisfiable"] = 416] = "RangeNotSatisfiable";
  1235. HttpStatusCode[HttpStatusCode["ExpectationFailed"] = 417] = "ExpectationFailed";
  1236. HttpStatusCode[HttpStatusCode["ImATeapot"] = 418] = "ImATeapot";
  1237. HttpStatusCode[HttpStatusCode["MisdirectedRequest"] = 421] = "MisdirectedRequest";
  1238. HttpStatusCode[HttpStatusCode["UnprocessableEntity"] = 422] = "UnprocessableEntity";
  1239. HttpStatusCode[HttpStatusCode["Locked"] = 423] = "Locked";
  1240. HttpStatusCode[HttpStatusCode["FailedDependency"] = 424] = "FailedDependency";
  1241. HttpStatusCode[HttpStatusCode["TooEarly"] = 425] = "TooEarly";
  1242. HttpStatusCode[HttpStatusCode["UpgradeRequired"] = 426] = "UpgradeRequired";
  1243. HttpStatusCode[HttpStatusCode["PreconditionRequired"] = 428] = "PreconditionRequired";
  1244. HttpStatusCode[HttpStatusCode["TooManyRequests"] = 429] = "TooManyRequests";
  1245. HttpStatusCode[HttpStatusCode["RequestHeaderFieldsTooLarge"] = 431] = "RequestHeaderFieldsTooLarge";
  1246. HttpStatusCode[HttpStatusCode["UnavailableForLegalReasons"] = 451] = "UnavailableForLegalReasons";
  1247. HttpStatusCode[HttpStatusCode["InternalServerError"] = 500] = "InternalServerError";
  1248. HttpStatusCode[HttpStatusCode["NotImplemented"] = 501] = "NotImplemented";
  1249. HttpStatusCode[HttpStatusCode["BadGateway"] = 502] = "BadGateway";
  1250. HttpStatusCode[HttpStatusCode["ServiceUnavailable"] = 503] = "ServiceUnavailable";
  1251. HttpStatusCode[HttpStatusCode["GatewayTimeout"] = 504] = "GatewayTimeout";
  1252. HttpStatusCode[HttpStatusCode["HttpVersionNotSupported"] = 505] = "HttpVersionNotSupported";
  1253. HttpStatusCode[HttpStatusCode["VariantAlsoNegotiates"] = 506] = "VariantAlsoNegotiates";
  1254. HttpStatusCode[HttpStatusCode["InsufficientStorage"] = 507] = "InsufficientStorage";
  1255. HttpStatusCode[HttpStatusCode["LoopDetected"] = 508] = "LoopDetected";
  1256. HttpStatusCode[HttpStatusCode["NotExtended"] = 510] = "NotExtended";
  1257. HttpStatusCode[HttpStatusCode["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired";
  1258. })(HttpStatusCode || (HttpStatusCode = {}));
  1259. /**
  1260. * Constructs an instance of `HttpRequestOptions<T>` from a source `HttpMethodOptions` and
  1261. * the given `body`. This function clones the object and adds the body.
  1262. *
  1263. * Note that the `responseType` *options* value is a String that identifies the
  1264. * single data type of the response.
  1265. * A single overload version of the method handles each response type.
  1266. * The value of `responseType` cannot be a union, as the combined signature could imply.
  1267. *
  1268. */
  1269. function addBody(options, body) {
  1270. return {
  1271. body,
  1272. headers: options.headers,
  1273. context: options.context,
  1274. observe: options.observe,
  1275. params: options.params,
  1276. reportProgress: options.reportProgress,
  1277. responseType: options.responseType,
  1278. withCredentials: options.withCredentials,
  1279. transferCache: options.transferCache,
  1280. };
  1281. }
  1282. /**
  1283. * Performs HTTP requests.
  1284. * This service is available as an injectable class, with methods to perform HTTP requests.
  1285. * Each request method has multiple signatures, and the return type varies based on
  1286. * the signature that is called (mainly the values of `observe` and `responseType`).
  1287. *
  1288. * Note that the `responseType` *options* value is a String that identifies the
  1289. * single data type of the response.
  1290. * A single overload version of the method handles each response type.
  1291. * The value of `responseType` cannot be a union, as the combined signature could imply.
  1292. *
  1293. * @usageNotes
  1294. *
  1295. * ### HTTP Request Example
  1296. *
  1297. * ```ts
  1298. * // GET heroes whose name contains search term
  1299. * searchHeroes(term: string): observable<Hero[]>{
  1300. *
  1301. * const params = new HttpParams({fromString: 'name=term'});
  1302. * return this.httpClient.request('GET', this.heroesUrl, {responseType:'json', params});
  1303. * }
  1304. * ```
  1305. *
  1306. * Alternatively, the parameter string can be used without invoking HttpParams
  1307. * by directly joining to the URL.
  1308. * ```ts
  1309. * this.httpClient.request('GET', this.heroesUrl + '?' + 'name=term', {responseType:'json'});
  1310. * ```
  1311. *
  1312. *
  1313. * ### JSONP Example
  1314. * ```ts
  1315. * requestJsonp(url, callback = 'callback') {
  1316. * return this.httpClient.jsonp(this.heroesURL, callback);
  1317. * }
  1318. * ```
  1319. *
  1320. * ### PATCH Example
  1321. * ```ts
  1322. * // PATCH one of the heroes' name
  1323. * patchHero (id: number, heroName: string): Observable<{}> {
  1324. * const url = `${this.heroesUrl}/${id}`; // PATCH api/heroes/42
  1325. * return this.httpClient.patch(url, {name: heroName}, httpOptions)
  1326. * .pipe(catchError(this.handleError('patchHero')));
  1327. * }
  1328. * ```
  1329. *
  1330. * @see [HTTP Guide](guide/http)
  1331. * @see [HTTP Request](api/common/http/HttpRequest)
  1332. *
  1333. * @publicApi
  1334. */
  1335. class HttpClient {
  1336. handler;
  1337. constructor(handler) {
  1338. this.handler = handler;
  1339. }
  1340. /**
  1341. * Constructs an observable for a generic HTTP request that, when subscribed,
  1342. * fires the request through the chain of registered interceptors and on to the
  1343. * server.
  1344. *
  1345. * You can pass an `HttpRequest` directly as the only parameter. In this case,
  1346. * the call returns an observable of the raw `HttpEvent` stream.
  1347. *
  1348. * Alternatively you can pass an HTTP method as the first parameter,
  1349. * a URL string as the second, and an options hash containing the request body as the third.
  1350. * See `addBody()`. In this case, the specified `responseType` and `observe` options determine the
  1351. * type of returned observable.
  1352. * * The `responseType` value determines how a successful response body is parsed.
  1353. * * If `responseType` is the default `json`, you can pass a type interface for the resulting
  1354. * object as a type parameter to the call.
  1355. *
  1356. * The `observe` value determines the return type, according to what you are interested in
  1357. * observing.
  1358. * * An `observe` value of events returns an observable of the raw `HttpEvent` stream, including
  1359. * progress events by default.
  1360. * * An `observe` value of response returns an observable of `HttpResponse<T>`,
  1361. * where the `T` parameter depends on the `responseType` and any optionally provided type
  1362. * parameter.
  1363. * * An `observe` value of body returns an observable of `<T>` with the same `T` body type.
  1364. *
  1365. */
  1366. request(first, url, options = {}) {
  1367. let req;
  1368. // First, check whether the primary argument is an instance of `HttpRequest`.
  1369. if (first instanceof HttpRequest) {
  1370. // It is. The other arguments must be undefined (per the signatures) and can be
  1371. // ignored.
  1372. req = first;
  1373. }
  1374. else {
  1375. // It's a string, so it represents a URL. Construct a request based on it,
  1376. // and incorporate the remaining arguments (assuming `GET` unless a method is
  1377. // provided.
  1378. // Figure out the headers.
  1379. let headers = undefined;
  1380. if (options.headers instanceof HttpHeaders) {
  1381. headers = options.headers;
  1382. }
  1383. else {
  1384. headers = new HttpHeaders(options.headers);
  1385. }
  1386. // Sort out parameters.
  1387. let params = undefined;
  1388. if (!!options.params) {
  1389. if (options.params instanceof HttpParams) {
  1390. params = options.params;
  1391. }
  1392. else {
  1393. params = new HttpParams({ fromObject: options.params });
  1394. }
  1395. }
  1396. // Construct the request.
  1397. req = new HttpRequest(first, url, options.body !== undefined ? options.body : null, {
  1398. headers,
  1399. context: options.context,
  1400. params,
  1401. reportProgress: options.reportProgress,
  1402. // By default, JSON is assumed to be returned for all calls.
  1403. responseType: options.responseType || 'json',
  1404. withCredentials: options.withCredentials,
  1405. transferCache: options.transferCache,
  1406. });
  1407. }
  1408. // Start with an Observable.of() the initial request, and run the handler (which
  1409. // includes all interceptors) inside a concatMap(). This way, the handler runs
  1410. // inside an Observable chain, which causes interceptors to be re-run on every
  1411. // subscription (this also makes retries re-run the handler, including interceptors).
  1412. const events$ = of(req).pipe(concatMap((req) => this.handler.handle(req)));
  1413. // If coming via the API signature which accepts a previously constructed HttpRequest,
  1414. // the only option is to get the event stream. Otherwise, return the event stream if
  1415. // that is what was requested.
  1416. if (first instanceof HttpRequest || options.observe === 'events') {
  1417. return events$;
  1418. }
  1419. // The requested stream contains either the full response or the body. In either
  1420. // case, the first step is to filter the event stream to extract a stream of
  1421. // responses(s).
  1422. const res$ = (events$.pipe(filter((event) => event instanceof HttpResponse)));
  1423. // Decide which stream to return.
  1424. switch (options.observe || 'body') {
  1425. case 'body':
  1426. // The requested stream is the body. Map the response stream to the response
  1427. // body. This could be done more simply, but a misbehaving interceptor might
  1428. // transform the response body into a different format and ignore the requested
  1429. // responseType. Guard against this by validating that the response is of the
  1430. // requested type.
  1431. switch (req.responseType) {
  1432. case 'arraybuffer':
  1433. return res$.pipe(map((res) => {
  1434. // Validate that the body is an ArrayBuffer.
  1435. if (res.body !== null && !(res.body instanceof ArrayBuffer)) {
  1436. throw new _RuntimeError(2806 /* RuntimeErrorCode.RESPONSE_IS_NOT_AN_ARRAY_BUFFER */, ngDevMode && 'Response is not an ArrayBuffer.');
  1437. }
  1438. return res.body;
  1439. }));
  1440. case 'blob':
  1441. return res$.pipe(map((res) => {
  1442. // Validate that the body is a Blob.
  1443. if (res.body !== null && !(res.body instanceof Blob)) {
  1444. throw new _RuntimeError(2807 /* RuntimeErrorCode.RESPONSE_IS_NOT_A_BLOB */, ngDevMode && 'Response is not a Blob.');
  1445. }
  1446. return res.body;
  1447. }));
  1448. case 'text':
  1449. return res$.pipe(map((res) => {
  1450. // Validate that the body is a string.
  1451. if (res.body !== null && typeof res.body !== 'string') {
  1452. throw new _RuntimeError(2808 /* RuntimeErrorCode.RESPONSE_IS_NOT_A_STRING */, ngDevMode && 'Response is not a string.');
  1453. }
  1454. return res.body;
  1455. }));
  1456. case 'json':
  1457. default:
  1458. // No validation needed for JSON responses, as they can be of any type.
  1459. return res$.pipe(map((res) => res.body));
  1460. }
  1461. case 'response':
  1462. // The response stream was requested directly, so return it.
  1463. return res$;
  1464. default:
  1465. // Guard against new future observe types being added.
  1466. throw new _RuntimeError(2809 /* RuntimeErrorCode.UNHANDLED_OBSERVE_TYPE */, ngDevMode && `Unreachable: unhandled observe type ${options.observe}}`);
  1467. }
  1468. }
  1469. /**
  1470. * Constructs an observable that, when subscribed, causes the configured
  1471. * `DELETE` request to execute on the server. See the individual overloads for
  1472. * details on the return type.
  1473. *
  1474. * @param url The endpoint URL.
  1475. * @param options The HTTP options to send with the request.
  1476. *
  1477. */
  1478. delete(url, options = {}) {
  1479. return this.request('DELETE', url, options);
  1480. }
  1481. /**
  1482. * Constructs an observable that, when subscribed, causes the configured
  1483. * `GET` request to execute on the server. See the individual overloads for
  1484. * details on the return type.
  1485. */
  1486. get(url, options = {}) {
  1487. return this.request('GET', url, options);
  1488. }
  1489. /**
  1490. * Constructs an observable that, when subscribed, causes the configured
  1491. * `HEAD` request to execute on the server. The `HEAD` method returns
  1492. * meta information about the resource without transferring the
  1493. * resource itself. See the individual overloads for
  1494. * details on the return type.
  1495. */
  1496. head(url, options = {}) {
  1497. return this.request('HEAD', url, options);
  1498. }
  1499. /**
  1500. * Constructs an `Observable` that, when subscribed, causes a request with the special method
  1501. * `JSONP` to be dispatched via the interceptor pipeline.
  1502. * The [JSONP pattern](https://en.wikipedia.org/wiki/JSONP) works around limitations of certain
  1503. * API endpoints that don't support newer,
  1504. * and preferable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) protocol.
  1505. * JSONP treats the endpoint API as a JavaScript file and tricks the browser to process the
  1506. * requests even if the API endpoint is not located on the same domain (origin) as the client-side
  1507. * application making the request.
  1508. * The endpoint API must support JSONP callback for JSONP requests to work.
  1509. * The resource API returns the JSON response wrapped in a callback function.
  1510. * You can pass the callback function name as one of the query parameters.
  1511. * Note that JSONP requests can only be used with `GET` requests.
  1512. *
  1513. * @param url The resource URL.
  1514. * @param callbackParam The callback function name.
  1515. *
  1516. */
  1517. jsonp(url, callbackParam) {
  1518. return this.request('JSONP', url, {
  1519. params: new HttpParams().append(callbackParam, 'JSONP_CALLBACK'),
  1520. observe: 'body',
  1521. responseType: 'json',
  1522. });
  1523. }
  1524. /**
  1525. * Constructs an `Observable` that, when subscribed, causes the configured
  1526. * `OPTIONS` request to execute on the server. This method allows the client
  1527. * to determine the supported HTTP methods and other capabilities of an endpoint,
  1528. * without implying a resource action. See the individual overloads for
  1529. * details on the return type.
  1530. */
  1531. options(url, options = {}) {
  1532. return this.request('OPTIONS', url, options);
  1533. }
  1534. /**
  1535. * Constructs an observable that, when subscribed, causes the configured
  1536. * `PATCH` request to execute on the server. See the individual overloads for
  1537. * details on the return type.
  1538. */
  1539. patch(url, body, options = {}) {
  1540. return this.request('PATCH', url, addBody(options, body));
  1541. }
  1542. /**
  1543. * Constructs an observable that, when subscribed, causes the configured
  1544. * `POST` request to execute on the server. The server responds with the location of
  1545. * the replaced resource. See the individual overloads for
  1546. * details on the return type.
  1547. */
  1548. post(url, body, options = {}) {
  1549. return this.request('POST', url, addBody(options, body));
  1550. }
  1551. /**
  1552. * Constructs an observable that, when subscribed, causes the configured
  1553. * `PUT` request to execute on the server. The `PUT` method replaces an existing resource
  1554. * with a new set of values.
  1555. * See the individual overloads for details on the return type.
  1556. */
  1557. put(url, body, options = {}) {
  1558. return this.request('PUT', url, addBody(options, body));
  1559. }
  1560. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClient, deps: [{ token: HttpHandler }], target: i0.ɵɵFactoryTarget.Injectable });
  1561. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClient });
  1562. }
  1563. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClient, decorators: [{
  1564. type: Injectable
  1565. }], ctorParameters: () => [{ type: HttpHandler }] });
  1566. const XSSI_PREFIX$1 = /^\)\]\}',?\n/;
  1567. /**
  1568. * Determine an appropriate URL for the response, by checking either
  1569. * response url or the X-Request-URL header.
  1570. */
  1571. function getResponseUrl$1(response) {
  1572. if (response.url) {
  1573. return response.url;
  1574. }
  1575. // stored as lowercase in the map
  1576. const xRequestUrl = X_REQUEST_URL_HEADER.toLocaleLowerCase();
  1577. return response.headers.get(xRequestUrl);
  1578. }
  1579. /**
  1580. * An internal injection token to reference `FetchBackend` implementation
  1581. * in a tree-shakable way.
  1582. */
  1583. const FETCH_BACKEND = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'FETCH_BACKEND' : '');
  1584. /**
  1585. * Uses `fetch` to send requests to a backend server.
  1586. *
  1587. * This `FetchBackend` requires the support of the
  1588. * [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) which is available on all
  1589. * supported browsers and on Node.js v18 or later.
  1590. *
  1591. * @see {@link HttpHandler}
  1592. *
  1593. * @publicApi
  1594. */
  1595. class FetchBackend {
  1596. // We use an arrow function to always reference the current global implementation of `fetch`.
  1597. // This is helpful for cases when the global `fetch` implementation is modified by external code,
  1598. // see https://github.com/angular/angular/issues/57527.
  1599. fetchImpl = inject(FetchFactory, { optional: true })?.fetch ?? ((...args) => globalThis.fetch(...args));
  1600. ngZone = inject(NgZone);
  1601. destroyRef = inject(DestroyRef);
  1602. destroyed = false;
  1603. constructor() {
  1604. this.destroyRef.onDestroy(() => {
  1605. this.destroyed = true;
  1606. });
  1607. }
  1608. handle(request) {
  1609. return new Observable((observer) => {
  1610. const aborter = new AbortController();
  1611. this.doRequest(request, aborter.signal, observer).then(noop, (error) => observer.error(new HttpErrorResponse({ error })));
  1612. return () => aborter.abort();
  1613. });
  1614. }
  1615. async doRequest(request, signal, observer) {
  1616. const init = this.createRequestInit(request);
  1617. let response;
  1618. try {
  1619. // Run fetch outside of Angular zone.
  1620. // This is due to Node.js fetch implementation (Undici) which uses a number of setTimeouts to check if
  1621. // the response should eventually timeout which causes extra CD cycles every 500ms
  1622. const fetchPromise = this.ngZone.runOutsideAngular(() => this.fetchImpl(request.urlWithParams, { signal, ...init }));
  1623. // Make sure Zone.js doesn't trigger false-positive unhandled promise
  1624. // error in case the Promise is rejected synchronously. See function
  1625. // description for additional information.
  1626. silenceSuperfluousUnhandledPromiseRejection(fetchPromise);
  1627. // Send the `Sent` event before awaiting the response.
  1628. observer.next({ type: HttpEventType.Sent });
  1629. response = await fetchPromise;
  1630. }
  1631. catch (error) {
  1632. observer.error(new HttpErrorResponse({
  1633. error,
  1634. status: error.status ?? 0,
  1635. statusText: error.statusText,
  1636. url: request.urlWithParams,
  1637. headers: error.headers,
  1638. }));
  1639. return;
  1640. }
  1641. const headers = new HttpHeaders(response.headers);
  1642. const statusText = response.statusText;
  1643. const url = getResponseUrl$1(response) ?? request.urlWithParams;
  1644. let status = response.status;
  1645. let body = null;
  1646. if (request.reportProgress) {
  1647. observer.next(new HttpHeaderResponse({ headers, status, statusText, url }));
  1648. }
  1649. if (response.body) {
  1650. // Read Progress
  1651. const contentLength = response.headers.get('content-length');
  1652. const chunks = [];
  1653. const reader = response.body.getReader();
  1654. let receivedLength = 0;
  1655. let decoder;
  1656. let partialText;
  1657. // We have to check whether the Zone is defined in the global scope because this may be called
  1658. // when the zone is nooped.
  1659. const reqZone = typeof Zone !== 'undefined' && Zone.current;
  1660. let canceled = false;
  1661. // Perform response processing outside of Angular zone to
  1662. // ensure no excessive change detection runs are executed
  1663. // Here calling the async ReadableStreamDefaultReader.read() is responsible for triggering CD
  1664. await this.ngZone.runOutsideAngular(async () => {
  1665. while (true) {
  1666. // Prevent reading chunks if the app is destroyed. Otherwise, we risk doing
  1667. // unnecessary work or triggering side effects after teardown.
  1668. // This may happen if the app was explicitly destroyed before
  1669. // the response returned entirely.
  1670. if (this.destroyed) {
  1671. // Streams left in a pending state (due to `break` without cancel) may
  1672. // continue consuming or holding onto data behind the scenes.
  1673. // Calling `reader.cancel()` allows the browser or the underlying
  1674. // system to release any network or memory resources associated with the stream.
  1675. await reader.cancel();
  1676. canceled = true;
  1677. break;
  1678. }
  1679. const { done, value } = await reader.read();
  1680. if (done) {
  1681. break;
  1682. }
  1683. chunks.push(value);
  1684. receivedLength += value.length;
  1685. if (request.reportProgress) {
  1686. partialText =
  1687. request.responseType === 'text'
  1688. ? (partialText ?? '') +
  1689. (decoder ??= new TextDecoder()).decode(value, { stream: true })
  1690. : undefined;
  1691. const reportProgress = () => observer.next({
  1692. type: HttpEventType.DownloadProgress,
  1693. total: contentLength ? +contentLength : undefined,
  1694. loaded: receivedLength,
  1695. partialText,
  1696. });
  1697. reqZone ? reqZone.run(reportProgress) : reportProgress();
  1698. }
  1699. }
  1700. });
  1701. // We need to manage the canceled state — because the Streams API does not
  1702. // expose a direct `.state` property on the reader.
  1703. // We need to `return` because `parseBody` may not be able to parse chunks
  1704. // that were only partially read (due to cancellation caused by app destruction).
  1705. if (canceled) {
  1706. observer.complete();
  1707. return;
  1708. }
  1709. // Combine all chunks.
  1710. const chunksAll = this.concatChunks(chunks, receivedLength);
  1711. try {
  1712. const contentType = response.headers.get(CONTENT_TYPE_HEADER) ?? '';
  1713. body = this.parseBody(request, chunksAll, contentType);
  1714. }
  1715. catch (error) {
  1716. // Body loading or parsing failed
  1717. observer.error(new HttpErrorResponse({
  1718. error,
  1719. headers: new HttpHeaders(response.headers),
  1720. status: response.status,
  1721. statusText: response.statusText,
  1722. url: getResponseUrl$1(response) ?? request.urlWithParams,
  1723. }));
  1724. return;
  1725. }
  1726. }
  1727. // Same behavior as the XhrBackend
  1728. if (status === 0) {
  1729. status = body ? HTTP_STATUS_CODE_OK : 0;
  1730. }
  1731. // ok determines whether the response will be transmitted on the event or
  1732. // error channel. Unsuccessful status codes (not 2xx) will always be errors,
  1733. // but a successful status code can still result in an error if the user
  1734. // asked for JSON data and the body cannot be parsed as such.
  1735. const ok = status >= 200 && status < 300;
  1736. if (ok) {
  1737. observer.next(new HttpResponse({
  1738. body,
  1739. headers,
  1740. status,
  1741. statusText,
  1742. url,
  1743. }));
  1744. // The full body has been received and delivered, no further events
  1745. // are possible. This request is complete.
  1746. observer.complete();
  1747. }
  1748. else {
  1749. observer.error(new HttpErrorResponse({
  1750. error: body,
  1751. headers,
  1752. status,
  1753. statusText,
  1754. url,
  1755. }));
  1756. }
  1757. }
  1758. parseBody(request, binContent, contentType) {
  1759. switch (request.responseType) {
  1760. case 'json':
  1761. // stripping the XSSI when present
  1762. const text = new TextDecoder().decode(binContent).replace(XSSI_PREFIX$1, '');
  1763. return text === '' ? null : JSON.parse(text);
  1764. case 'text':
  1765. return new TextDecoder().decode(binContent);
  1766. case 'blob':
  1767. return new Blob([binContent], { type: contentType });
  1768. case 'arraybuffer':
  1769. return binContent.buffer;
  1770. }
  1771. }
  1772. createRequestInit(req) {
  1773. // We could share some of this logic with the XhrBackend
  1774. const headers = {};
  1775. const credentials = req.withCredentials ? 'include' : undefined;
  1776. // Setting all the requested headers.
  1777. req.headers.forEach((name, values) => (headers[name] = values.join(',')));
  1778. // Add an Accept header if one isn't present already.
  1779. if (!req.headers.has(ACCEPT_HEADER)) {
  1780. headers[ACCEPT_HEADER] = ACCEPT_HEADER_VALUE;
  1781. }
  1782. // Auto-detect the Content-Type header if one isn't present already.
  1783. if (!req.headers.has(CONTENT_TYPE_HEADER)) {
  1784. const detectedType = req.detectContentTypeHeader();
  1785. // Sometimes Content-Type detection fails.
  1786. if (detectedType !== null) {
  1787. headers[CONTENT_TYPE_HEADER] = detectedType;
  1788. }
  1789. }
  1790. return {
  1791. body: req.serializeBody(),
  1792. method: req.method,
  1793. headers,
  1794. credentials,
  1795. };
  1796. }
  1797. concatChunks(chunks, totalLength) {
  1798. const chunksAll = new Uint8Array(totalLength);
  1799. let position = 0;
  1800. for (const chunk of chunks) {
  1801. chunksAll.set(chunk, position);
  1802. position += chunk.length;
  1803. }
  1804. return chunksAll;
  1805. }
  1806. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FetchBackend, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
  1807. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FetchBackend });
  1808. }
  1809. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FetchBackend, decorators: [{
  1810. type: Injectable
  1811. }], ctorParameters: () => [] });
  1812. /**
  1813. * Abstract class to provide a mocked implementation of `fetch()`
  1814. */
  1815. class FetchFactory {
  1816. }
  1817. function noop() { }
  1818. /**
  1819. * Zone.js treats a rejected promise that has not yet been awaited
  1820. * as an unhandled error. This function adds a noop `.then` to make
  1821. * sure that Zone.js doesn't throw an error if the Promise is rejected
  1822. * synchronously.
  1823. */
  1824. function silenceSuperfluousUnhandledPromiseRejection(promise) {
  1825. promise.then(noop, noop);
  1826. }
  1827. function interceptorChainEndFn(req, finalHandlerFn) {
  1828. return finalHandlerFn(req);
  1829. }
  1830. /**
  1831. * Constructs a `ChainedInterceptorFn` which adapts a legacy `HttpInterceptor` to the
  1832. * `ChainedInterceptorFn` interface.
  1833. */
  1834. function adaptLegacyInterceptorToChain(chainTailFn, interceptor) {
  1835. return (initialRequest, finalHandlerFn) => interceptor.intercept(initialRequest, {
  1836. handle: (downstreamRequest) => chainTailFn(downstreamRequest, finalHandlerFn),
  1837. });
  1838. }
  1839. /**
  1840. * Constructs a `ChainedInterceptorFn` which wraps and invokes a functional interceptor in the given
  1841. * injector.
  1842. */
  1843. function chainedInterceptorFn(chainTailFn, interceptorFn, injector) {
  1844. return (initialRequest, finalHandlerFn) => runInInjectionContext(injector, () => interceptorFn(initialRequest, (downstreamRequest) => chainTailFn(downstreamRequest, finalHandlerFn)));
  1845. }
  1846. /**
  1847. * A multi-provider token that represents the array of registered
  1848. * `HttpInterceptor` objects.
  1849. *
  1850. * @publicApi
  1851. */
  1852. const HTTP_INTERCEPTORS = new InjectionToken(ngDevMode ? 'HTTP_INTERCEPTORS' : '');
  1853. /**
  1854. * A multi-provided token of `HttpInterceptorFn`s.
  1855. */
  1856. const HTTP_INTERCEPTOR_FNS = new InjectionToken(ngDevMode ? 'HTTP_INTERCEPTOR_FNS' : '');
  1857. /**
  1858. * A multi-provided token of `HttpInterceptorFn`s that are only set in root.
  1859. */
  1860. const HTTP_ROOT_INTERCEPTOR_FNS = new InjectionToken(ngDevMode ? 'HTTP_ROOT_INTERCEPTOR_FNS' : '');
  1861. // TODO(atscott): We need a larger discussion about stability and what should contribute to stability.
  1862. // Should the whole interceptor chain contribute to stability or just the backend request #55075?
  1863. // Should HttpClient contribute to stability automatically at all?
  1864. const REQUESTS_CONTRIBUTE_TO_STABILITY = new InjectionToken(ngDevMode ? 'REQUESTS_CONTRIBUTE_TO_STABILITY' : '', { providedIn: 'root', factory: () => true });
  1865. /**
  1866. * Creates an `HttpInterceptorFn` which lazily initializes an interceptor chain from the legacy
  1867. * class-based interceptors and runs the request through it.
  1868. */
  1869. function legacyInterceptorFnFactory() {
  1870. let chain = null;
  1871. return (req, handler) => {
  1872. if (chain === null) {
  1873. const interceptors = inject(HTTP_INTERCEPTORS, { optional: true }) ?? [];
  1874. // Note: interceptors are wrapped right-to-left so that final execution order is
  1875. // left-to-right. That is, if `interceptors` is the array `[a, b, c]`, we want to
  1876. // produce a chain that is conceptually `c(b(a(end)))`, which we build from the inside
  1877. // out.
  1878. chain = interceptors.reduceRight(adaptLegacyInterceptorToChain, interceptorChainEndFn);
  1879. }
  1880. const pendingTasks = inject(_PendingTasksInternal);
  1881. const contributeToStability = inject(REQUESTS_CONTRIBUTE_TO_STABILITY);
  1882. if (contributeToStability) {
  1883. const taskId = pendingTasks.add();
  1884. return chain(req, handler).pipe(finalize(() => pendingTasks.remove(taskId)));
  1885. }
  1886. else {
  1887. return chain(req, handler);
  1888. }
  1889. };
  1890. }
  1891. let fetchBackendWarningDisplayed = false;
  1892. class HttpInterceptorHandler extends HttpHandler {
  1893. backend;
  1894. injector;
  1895. chain = null;
  1896. pendingTasks = inject(_PendingTasksInternal);
  1897. contributeToStability = inject(REQUESTS_CONTRIBUTE_TO_STABILITY);
  1898. constructor(backend, injector) {
  1899. super();
  1900. this.backend = backend;
  1901. this.injector = injector;
  1902. // We strongly recommend using fetch backend for HTTP calls when SSR is used
  1903. // for an application. The logic below checks if that's the case and produces
  1904. // a warning otherwise.
  1905. if ((typeof ngDevMode === 'undefined' || ngDevMode) && !fetchBackendWarningDisplayed) {
  1906. const isServer = isPlatformServer(injector.get(PLATFORM_ID));
  1907. // This flag is necessary because provideHttpClientTesting() overrides the backend
  1908. // even if `withFetch()` is used within the test. When the testing HTTP backend is provided,
  1909. // no HTTP calls are actually performed during the test, so producing a warning would be
  1910. // misleading.
  1911. const isTestingBackend = this.backend.isTestingBackend;
  1912. if (isServer && !(this.backend instanceof FetchBackend) && !isTestingBackend) {
  1913. fetchBackendWarningDisplayed = true;
  1914. injector
  1915. .get(_Console)
  1916. .warn(_formatRuntimeError(2801 /* RuntimeErrorCode.NOT_USING_FETCH_BACKEND_IN_SSR */, 'Angular detected that `HttpClient` is not configured ' +
  1917. "to use `fetch` APIs. It's strongly recommended to " +
  1918. 'enable `fetch` for applications that use Server-Side Rendering ' +
  1919. 'for better performance and compatibility. ' +
  1920. 'To enable `fetch`, add the `withFetch()` to the `provideHttpClient()` ' +
  1921. 'call at the root of the application.'));
  1922. }
  1923. }
  1924. }
  1925. handle(initialRequest) {
  1926. if (this.chain === null) {
  1927. const dedupedInterceptorFns = Array.from(new Set([
  1928. ...this.injector.get(HTTP_INTERCEPTOR_FNS),
  1929. ...this.injector.get(HTTP_ROOT_INTERCEPTOR_FNS, []),
  1930. ]));
  1931. // Note: interceptors are wrapped right-to-left so that final execution order is
  1932. // left-to-right. That is, if `dedupedInterceptorFns` is the array `[a, b, c]`, we want to
  1933. // produce a chain that is conceptually `c(b(a(end)))`, which we build from the inside
  1934. // out.
  1935. this.chain = dedupedInterceptorFns.reduceRight((nextSequencedFn, interceptorFn) => chainedInterceptorFn(nextSequencedFn, interceptorFn, this.injector), interceptorChainEndFn);
  1936. }
  1937. if (this.contributeToStability) {
  1938. const taskId = this.pendingTasks.add();
  1939. return this.chain(initialRequest, (downstreamRequest) => this.backend.handle(downstreamRequest)).pipe(finalize(() => this.pendingTasks.remove(taskId)));
  1940. }
  1941. else {
  1942. return this.chain(initialRequest, (downstreamRequest) => this.backend.handle(downstreamRequest));
  1943. }
  1944. }
  1945. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpInterceptorHandler, deps: [{ token: HttpBackend }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
  1946. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpInterceptorHandler });
  1947. }
  1948. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpInterceptorHandler, decorators: [{
  1949. type: Injectable
  1950. }], ctorParameters: () => [{ type: HttpBackend }, { type: i0.EnvironmentInjector }] });
  1951. // Every request made through JSONP needs a callback name that's unique across the
  1952. // whole page. Each request is assigned an id and the callback name is constructed
  1953. // from that. The next id to be assigned is tracked in a global variable here that
  1954. // is shared among all applications on the page.
  1955. let nextRequestId = 0;
  1956. /**
  1957. * When a pending <script> is unsubscribed we'll move it to this document, so it won't be
  1958. * executed.
  1959. */
  1960. let foreignDocument;
  1961. // Error text given when a JSONP script is injected, but doesn't invoke the callback
  1962. // passed in its URL.
  1963. const JSONP_ERR_NO_CALLBACK = 'JSONP injected script did not invoke callback.';
  1964. // Error text given when a request is passed to the JsonpClientBackend that doesn't
  1965. // have a request method JSONP.
  1966. const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use JSONP request method.';
  1967. const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json response type.';
  1968. // Error text given when a request is passed to the JsonpClientBackend that has
  1969. // headers set
  1970. const JSONP_ERR_HEADERS_NOT_SUPPORTED = 'JSONP requests do not support headers.';
  1971. /**
  1972. * DI token/abstract type representing a map of JSONP callbacks.
  1973. *
  1974. * In the browser, this should always be the `window` object.
  1975. *
  1976. *
  1977. */
  1978. class JsonpCallbackContext {
  1979. }
  1980. /**
  1981. * Factory function that determines where to store JSONP callbacks.
  1982. *
  1983. * Ordinarily JSONP callbacks are stored on the `window` object, but this may not exist
  1984. * in test environments. In that case, callbacks are stored on an anonymous object instead.
  1985. *
  1986. *
  1987. */
  1988. function jsonpCallbackContext() {
  1989. if (typeof window === 'object') {
  1990. return window;
  1991. }
  1992. return {};
  1993. }
  1994. /**
  1995. * Processes an `HttpRequest` with the JSONP method,
  1996. * by performing JSONP style requests.
  1997. * @see {@link HttpHandler}
  1998. * @see {@link HttpXhrBackend}
  1999. *
  2000. * @publicApi
  2001. */
  2002. class JsonpClientBackend {
  2003. callbackMap;
  2004. document;
  2005. /**
  2006. * A resolved promise that can be used to schedule microtasks in the event handlers.
  2007. */
  2008. resolvedPromise = Promise.resolve();
  2009. constructor(callbackMap, document) {
  2010. this.callbackMap = callbackMap;
  2011. this.document = document;
  2012. }
  2013. /**
  2014. * Get the name of the next callback method, by incrementing the global `nextRequestId`.
  2015. */
  2016. nextCallback() {
  2017. return `ng_jsonp_callback_${nextRequestId++}`;
  2018. }
  2019. /**
  2020. * Processes a JSONP request and returns an event stream of the results.
  2021. * @param req The request object.
  2022. * @returns An observable of the response events.
  2023. *
  2024. */
  2025. handle(req) {
  2026. // Firstly, check both the method and response type. If either doesn't match
  2027. // then the request was improperly routed here and cannot be handled.
  2028. if (req.method !== 'JSONP') {
  2029. throw new Error(JSONP_ERR_WRONG_METHOD);
  2030. }
  2031. else if (req.responseType !== 'json') {
  2032. throw new Error(JSONP_ERR_WRONG_RESPONSE_TYPE);
  2033. }
  2034. // Check the request headers. JSONP doesn't support headers and
  2035. // cannot set any that were supplied.
  2036. if (req.headers.keys().length > 0) {
  2037. throw new Error(JSONP_ERR_HEADERS_NOT_SUPPORTED);
  2038. }
  2039. // Everything else happens inside the Observable boundary.
  2040. return new Observable((observer) => {
  2041. // The first step to make a request is to generate the callback name, and replace the
  2042. // callback placeholder in the URL with the name. Care has to be taken here to ensure
  2043. // a trailing &, if matched, gets inserted back into the URL in the correct place.
  2044. const callback = this.nextCallback();
  2045. const url = req.urlWithParams.replace(/=JSONP_CALLBACK(&|$)/, `=${callback}$1`);
  2046. // Construct the <script> tag and point it at the URL.
  2047. const node = this.document.createElement('script');
  2048. node.src = url;
  2049. // A JSONP request requires waiting for multiple callbacks. These variables
  2050. // are closed over and track state across those callbacks.
  2051. // The response object, if one has been received, or null otherwise.
  2052. let body = null;
  2053. // Whether the response callback has been called.
  2054. let finished = false;
  2055. // Set the response callback in this.callbackMap (which will be the window
  2056. // object in the browser. The script being loaded via the <script> tag will
  2057. // eventually call this callback.
  2058. this.callbackMap[callback] = (data) => {
  2059. // Data has been received from the JSONP script. Firstly, delete this callback.
  2060. delete this.callbackMap[callback];
  2061. // Set state to indicate data was received.
  2062. body = data;
  2063. finished = true;
  2064. };
  2065. // cleanup() is a utility closure that removes the <script> from the page and
  2066. // the response callback from the window. This logic is used in both the
  2067. // success, error, and cancellation paths, so it's extracted out for convenience.
  2068. const cleanup = () => {
  2069. node.removeEventListener('load', onLoad);
  2070. node.removeEventListener('error', onError);
  2071. // Remove the <script> tag if it's still on the page.
  2072. node.remove();
  2073. // Remove the response callback from the callbackMap (window object in the
  2074. // browser).
  2075. delete this.callbackMap[callback];
  2076. };
  2077. // onLoad() is the success callback which runs after the response callback
  2078. // if the JSONP script loads successfully. The event itself is unimportant.
  2079. // If something went wrong, onLoad() may run without the response callback
  2080. // having been invoked.
  2081. const onLoad = (event) => {
  2082. // We wrap it in an extra Promise, to ensure the microtask
  2083. // is scheduled after the loaded endpoint has executed any potential microtask itself,
  2084. // which is not guaranteed in Internet Explorer and EdgeHTML. See issue #39496
  2085. this.resolvedPromise.then(() => {
  2086. // Cleanup the page.
  2087. cleanup();
  2088. // Check whether the response callback has run.
  2089. if (!finished) {
  2090. // It hasn't, something went wrong with the request. Return an error via
  2091. // the Observable error path. All JSONP errors have status 0.
  2092. observer.error(new HttpErrorResponse({
  2093. url,
  2094. status: 0,
  2095. statusText: 'JSONP Error',
  2096. error: new Error(JSONP_ERR_NO_CALLBACK),
  2097. }));
  2098. return;
  2099. }
  2100. // Success. body either contains the response body or null if none was
  2101. // returned.
  2102. observer.next(new HttpResponse({
  2103. body,
  2104. status: HTTP_STATUS_CODE_OK,
  2105. statusText: 'OK',
  2106. url,
  2107. }));
  2108. // Complete the stream, the response is over.
  2109. observer.complete();
  2110. });
  2111. };
  2112. // onError() is the error callback, which runs if the script returned generates
  2113. // a Javascript error. It emits the error via the Observable error channel as
  2114. // a HttpErrorResponse.
  2115. const onError = (error) => {
  2116. cleanup();
  2117. // Wrap the error in a HttpErrorResponse.
  2118. observer.error(new HttpErrorResponse({
  2119. error,
  2120. status: 0,
  2121. statusText: 'JSONP Error',
  2122. url,
  2123. }));
  2124. };
  2125. // Subscribe to both the success (load) and error events on the <script> tag,
  2126. // and add it to the page.
  2127. node.addEventListener('load', onLoad);
  2128. node.addEventListener('error', onError);
  2129. this.document.body.appendChild(node);
  2130. // The request has now been successfully sent.
  2131. observer.next({ type: HttpEventType.Sent });
  2132. // Cancellation handler.
  2133. return () => {
  2134. if (!finished) {
  2135. this.removeListeners(node);
  2136. }
  2137. // And finally, clean up the page.
  2138. cleanup();
  2139. };
  2140. });
  2141. }
  2142. removeListeners(script) {
  2143. // Issue #34818
  2144. // Changing <script>'s ownerDocument will prevent it from execution.
  2145. // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
  2146. foreignDocument ??= this.document.implementation.createHTMLDocument();
  2147. foreignDocument.adoptNode(script);
  2148. }
  2149. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: JsonpClientBackend, deps: [{ token: JsonpCallbackContext }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
  2150. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: JsonpClientBackend });
  2151. }
  2152. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: JsonpClientBackend, decorators: [{
  2153. type: Injectable
  2154. }], ctorParameters: () => [{ type: JsonpCallbackContext }, { type: undefined, decorators: [{
  2155. type: Inject,
  2156. args: [DOCUMENT]
  2157. }] }] });
  2158. /**
  2159. * Identifies requests with the method JSONP and shifts them to the `JsonpClientBackend`.
  2160. */
  2161. function jsonpInterceptorFn(req, next) {
  2162. if (req.method === 'JSONP') {
  2163. return inject(JsonpClientBackend).handle(req);
  2164. }
  2165. // Fall through for normal HTTP requests.
  2166. return next(req);
  2167. }
  2168. /**
  2169. * Identifies requests with the method JSONP and
  2170. * shifts them to the `JsonpClientBackend`.
  2171. *
  2172. * @see {@link HttpInterceptor}
  2173. *
  2174. * @publicApi
  2175. */
  2176. class JsonpInterceptor {
  2177. injector;
  2178. constructor(injector) {
  2179. this.injector = injector;
  2180. }
  2181. /**
  2182. * Identifies and handles a given JSONP request.
  2183. * @param initialRequest The outgoing request object to handle.
  2184. * @param next The next interceptor in the chain, or the backend
  2185. * if no interceptors remain in the chain.
  2186. * @returns An observable of the event stream.
  2187. */
  2188. intercept(initialRequest, next) {
  2189. return runInInjectionContext(this.injector, () => jsonpInterceptorFn(initialRequest, (downstreamRequest) => next.handle(downstreamRequest)));
  2190. }
  2191. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: JsonpInterceptor, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
  2192. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: JsonpInterceptor });
  2193. }
  2194. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: JsonpInterceptor, decorators: [{
  2195. type: Injectable
  2196. }], ctorParameters: () => [{ type: i0.EnvironmentInjector }] });
  2197. const XSSI_PREFIX = /^\)\]\}',?\n/;
  2198. const X_REQUEST_URL_REGEXP = RegExp(`^${X_REQUEST_URL_HEADER}:`, 'm');
  2199. /**
  2200. * Determine an appropriate URL for the response, by checking either
  2201. * XMLHttpRequest.responseURL or the X-Request-URL header.
  2202. */
  2203. function getResponseUrl(xhr) {
  2204. if ('responseURL' in xhr && xhr.responseURL) {
  2205. return xhr.responseURL;
  2206. }
  2207. if (X_REQUEST_URL_REGEXP.test(xhr.getAllResponseHeaders())) {
  2208. return xhr.getResponseHeader(X_REQUEST_URL_HEADER);
  2209. }
  2210. return null;
  2211. }
  2212. /**
  2213. * Uses `XMLHttpRequest` to send requests to a backend server.
  2214. * @see {@link HttpHandler}
  2215. * @see {@link JsonpClientBackend}
  2216. *
  2217. * @publicApi
  2218. */
  2219. class HttpXhrBackend {
  2220. xhrFactory;
  2221. constructor(xhrFactory) {
  2222. this.xhrFactory = xhrFactory;
  2223. }
  2224. /**
  2225. * Processes a request and returns a stream of response events.
  2226. * @param req The request object.
  2227. * @returns An observable of the response events.
  2228. */
  2229. handle(req) {
  2230. // Quick check to give a better error message when a user attempts to use
  2231. // HttpClient.jsonp() without installing the HttpClientJsonpModule
  2232. if (req.method === 'JSONP') {
  2233. throw new _RuntimeError(-2800 /* RuntimeErrorCode.MISSING_JSONP_MODULE */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
  2234. `Cannot make a JSONP request without JSONP support. To fix the problem, either add the \`withJsonpSupport()\` call (if \`provideHttpClient()\` is used) or import the \`HttpClientJsonpModule\` in the root NgModule.`);
  2235. }
  2236. // Check whether this factory has a special function to load an XHR implementation
  2237. // for various non-browser environments. We currently limit it to only `ServerXhr`
  2238. // class, which needs to load an XHR implementation.
  2239. const xhrFactory = this.xhrFactory;
  2240. const source = xhrFactory.ɵloadImpl
  2241. ? from(xhrFactory.ɵloadImpl())
  2242. : of(null);
  2243. return source.pipe(switchMap(() => {
  2244. // Everything happens on Observable subscription.
  2245. return new Observable((observer) => {
  2246. // Start by setting up the XHR object with request method, URL, and withCredentials
  2247. // flag.
  2248. const xhr = xhrFactory.build();
  2249. xhr.open(req.method, req.urlWithParams);
  2250. if (req.withCredentials) {
  2251. xhr.withCredentials = true;
  2252. }
  2253. // Add all the requested headers.
  2254. req.headers.forEach((name, values) => xhr.setRequestHeader(name, values.join(',')));
  2255. // Add an Accept header if one isn't present already.
  2256. if (!req.headers.has(ACCEPT_HEADER)) {
  2257. xhr.setRequestHeader(ACCEPT_HEADER, ACCEPT_HEADER_VALUE);
  2258. }
  2259. // Auto-detect the Content-Type header if one isn't present already.
  2260. if (!req.headers.has(CONTENT_TYPE_HEADER)) {
  2261. const detectedType = req.detectContentTypeHeader();
  2262. // Sometimes Content-Type detection fails.
  2263. if (detectedType !== null) {
  2264. xhr.setRequestHeader(CONTENT_TYPE_HEADER, detectedType);
  2265. }
  2266. }
  2267. // Set the responseType if one was requested.
  2268. if (req.responseType) {
  2269. const responseType = req.responseType.toLowerCase();
  2270. // JSON responses need to be processed as text. This is because if the server
  2271. // returns an XSSI-prefixed JSON response, the browser will fail to parse it,
  2272. // xhr.response will be null, and xhr.responseText cannot be accessed to
  2273. // retrieve the prefixed JSON data in order to strip the prefix. Thus, all JSON
  2274. // is parsed by first requesting text and then applying JSON.parse.
  2275. xhr.responseType = (responseType !== 'json' ? responseType : 'text');
  2276. }
  2277. // Serialize the request body if one is present. If not, this will be set to null.
  2278. const reqBody = req.serializeBody();
  2279. // If progress events are enabled, response headers will be delivered
  2280. // in two events - the HttpHeaderResponse event and the full HttpResponse
  2281. // event. However, since response headers don't change in between these
  2282. // two events, it doesn't make sense to parse them twice. So headerResponse
  2283. // caches the data extracted from the response whenever it's first parsed,
  2284. // to ensure parsing isn't duplicated.
  2285. let headerResponse = null;
  2286. // partialFromXhr extracts the HttpHeaderResponse from the current XMLHttpRequest
  2287. // state, and memoizes it into headerResponse.
  2288. const partialFromXhr = () => {
  2289. if (headerResponse !== null) {
  2290. return headerResponse;
  2291. }
  2292. const statusText = xhr.statusText || 'OK';
  2293. // Parse headers from XMLHttpRequest - this step is lazy.
  2294. const headers = new HttpHeaders(xhr.getAllResponseHeaders());
  2295. // Read the response URL from the XMLHttpResponse instance and fall back on the
  2296. // request URL.
  2297. const url = getResponseUrl(xhr) || req.url;
  2298. // Construct the HttpHeaderResponse and memoize it.
  2299. headerResponse = new HttpHeaderResponse({ headers, status: xhr.status, statusText, url });
  2300. return headerResponse;
  2301. };
  2302. // Next, a few closures are defined for the various events which XMLHttpRequest can
  2303. // emit. This allows them to be unregistered as event listeners later.
  2304. // First up is the load event, which represents a response being fully available.
  2305. const onLoad = () => {
  2306. // Read response state from the memoized partial data.
  2307. let { headers, status, statusText, url } = partialFromXhr();
  2308. // The body will be read out if present.
  2309. let body = null;
  2310. if (status !== HTTP_STATUS_CODE_NO_CONTENT) {
  2311. // Use XMLHttpRequest.response if set, responseText otherwise.
  2312. body = typeof xhr.response === 'undefined' ? xhr.responseText : xhr.response;
  2313. }
  2314. // Normalize another potential bug (this one comes from CORS).
  2315. if (status === 0) {
  2316. status = !!body ? HTTP_STATUS_CODE_OK : 0;
  2317. }
  2318. // ok determines whether the response will be transmitted on the event or
  2319. // error channel. Unsuccessful status codes (not 2xx) will always be errors,
  2320. // but a successful status code can still result in an error if the user
  2321. // asked for JSON data and the body cannot be parsed as such.
  2322. let ok = status >= 200 && status < 300;
  2323. // Check whether the body needs to be parsed as JSON (in many cases the browser
  2324. // will have done that already).
  2325. if (req.responseType === 'json' && typeof body === 'string') {
  2326. // Save the original body, before attempting XSSI prefix stripping.
  2327. const originalBody = body;
  2328. body = body.replace(XSSI_PREFIX, '');
  2329. try {
  2330. // Attempt the parse. If it fails, a parse error should be delivered to the
  2331. // user.
  2332. body = body !== '' ? JSON.parse(body) : null;
  2333. }
  2334. catch (error) {
  2335. // Since the JSON.parse failed, it's reasonable to assume this might not have
  2336. // been a JSON response. Restore the original body (including any XSSI prefix)
  2337. // to deliver a better error response.
  2338. body = originalBody;
  2339. // If this was an error request to begin with, leave it as a string, it
  2340. // probably just isn't JSON. Otherwise, deliver the parsing error to the user.
  2341. if (ok) {
  2342. // Even though the response status was 2xx, this is still an error.
  2343. ok = false;
  2344. // The parse error contains the text of the body that failed to parse.
  2345. body = { error, text: body };
  2346. }
  2347. }
  2348. }
  2349. if (ok) {
  2350. // A successful response is delivered on the event stream.
  2351. observer.next(new HttpResponse({
  2352. body,
  2353. headers,
  2354. status,
  2355. statusText,
  2356. url: url || undefined,
  2357. }));
  2358. // The full body has been received and delivered, no further events
  2359. // are possible. This request is complete.
  2360. observer.complete();
  2361. }
  2362. else {
  2363. // An unsuccessful request is delivered on the error channel.
  2364. observer.error(new HttpErrorResponse({
  2365. // The error in this case is the response body (error from the server).
  2366. error: body,
  2367. headers,
  2368. status,
  2369. statusText,
  2370. url: url || undefined,
  2371. }));
  2372. }
  2373. };
  2374. // The onError callback is called when something goes wrong at the network level.
  2375. // Connection timeout, DNS error, offline, etc. These are actual errors, and are
  2376. // transmitted on the error channel.
  2377. const onError = (error) => {
  2378. const { url } = partialFromXhr();
  2379. const res = new HttpErrorResponse({
  2380. error,
  2381. status: xhr.status || 0,
  2382. statusText: xhr.statusText || 'Unknown Error',
  2383. url: url || undefined,
  2384. });
  2385. observer.error(res);
  2386. };
  2387. // The sentHeaders flag tracks whether the HttpResponseHeaders event
  2388. // has been sent on the stream. This is necessary to track if progress
  2389. // is enabled since the event will be sent on only the first download
  2390. // progress event.
  2391. let sentHeaders = false;
  2392. // The download progress event handler, which is only registered if
  2393. // progress events are enabled.
  2394. const onDownProgress = (event) => {
  2395. // Send the HttpResponseHeaders event if it hasn't been sent already.
  2396. if (!sentHeaders) {
  2397. observer.next(partialFromXhr());
  2398. sentHeaders = true;
  2399. }
  2400. // Start building the download progress event to deliver on the response
  2401. // event stream.
  2402. let progressEvent = {
  2403. type: HttpEventType.DownloadProgress,
  2404. loaded: event.loaded,
  2405. };
  2406. // Set the total number of bytes in the event if it's available.
  2407. if (event.lengthComputable) {
  2408. progressEvent.total = event.total;
  2409. }
  2410. // If the request was for text content and a partial response is
  2411. // available on XMLHttpRequest, include it in the progress event
  2412. // to allow for streaming reads.
  2413. if (req.responseType === 'text' && !!xhr.responseText) {
  2414. progressEvent.partialText = xhr.responseText;
  2415. }
  2416. // Finally, fire the event.
  2417. observer.next(progressEvent);
  2418. };
  2419. // The upload progress event handler, which is only registered if
  2420. // progress events are enabled.
  2421. const onUpProgress = (event) => {
  2422. // Upload progress events are simpler. Begin building the progress
  2423. // event.
  2424. let progress = {
  2425. type: HttpEventType.UploadProgress,
  2426. loaded: event.loaded,
  2427. };
  2428. // If the total number of bytes being uploaded is available, include
  2429. // it.
  2430. if (event.lengthComputable) {
  2431. progress.total = event.total;
  2432. }
  2433. // Send the event.
  2434. observer.next(progress);
  2435. };
  2436. // By default, register for load and error events.
  2437. xhr.addEventListener('load', onLoad);
  2438. xhr.addEventListener('error', onError);
  2439. xhr.addEventListener('timeout', onError);
  2440. xhr.addEventListener('abort', onError);
  2441. // Progress events are only enabled if requested.
  2442. if (req.reportProgress) {
  2443. // Download progress is always enabled if requested.
  2444. xhr.addEventListener('progress', onDownProgress);
  2445. // Upload progress depends on whether there is a body to upload.
  2446. if (reqBody !== null && xhr.upload) {
  2447. xhr.upload.addEventListener('progress', onUpProgress);
  2448. }
  2449. }
  2450. // Fire the request, and notify the event stream that it was fired.
  2451. xhr.send(reqBody);
  2452. observer.next({ type: HttpEventType.Sent });
  2453. // This is the return from the Observable function, which is the
  2454. // request cancellation handler.
  2455. return () => {
  2456. // On a cancellation, remove all registered event listeners.
  2457. xhr.removeEventListener('error', onError);
  2458. xhr.removeEventListener('abort', onError);
  2459. xhr.removeEventListener('load', onLoad);
  2460. xhr.removeEventListener('timeout', onError);
  2461. if (req.reportProgress) {
  2462. xhr.removeEventListener('progress', onDownProgress);
  2463. if (reqBody !== null && xhr.upload) {
  2464. xhr.upload.removeEventListener('progress', onUpProgress);
  2465. }
  2466. }
  2467. // Finally, abort the in-flight request.
  2468. if (xhr.readyState !== xhr.DONE) {
  2469. xhr.abort();
  2470. }
  2471. };
  2472. });
  2473. }));
  2474. }
  2475. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXhrBackend, deps: [{ token: XhrFactory }], target: i0.ɵɵFactoryTarget.Injectable });
  2476. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXhrBackend });
  2477. }
  2478. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXhrBackend, decorators: [{
  2479. type: Injectable
  2480. }], ctorParameters: () => [{ type: XhrFactory }] });
  2481. const XSRF_ENABLED = new InjectionToken(ngDevMode ? 'XSRF_ENABLED' : '');
  2482. const XSRF_DEFAULT_COOKIE_NAME = 'XSRF-TOKEN';
  2483. const XSRF_COOKIE_NAME = new InjectionToken(ngDevMode ? 'XSRF_COOKIE_NAME' : '', {
  2484. providedIn: 'root',
  2485. factory: () => XSRF_DEFAULT_COOKIE_NAME,
  2486. });
  2487. const XSRF_DEFAULT_HEADER_NAME = 'X-XSRF-TOKEN';
  2488. const XSRF_HEADER_NAME = new InjectionToken(ngDevMode ? 'XSRF_HEADER_NAME' : '', {
  2489. providedIn: 'root',
  2490. factory: () => XSRF_DEFAULT_HEADER_NAME,
  2491. });
  2492. /**
  2493. * Retrieves the current XSRF token to use with the next outgoing request.
  2494. *
  2495. * @publicApi
  2496. */
  2497. class HttpXsrfTokenExtractor {
  2498. }
  2499. /**
  2500. * `HttpXsrfTokenExtractor` which retrieves the token from a cookie.
  2501. */
  2502. class HttpXsrfCookieExtractor {
  2503. doc;
  2504. cookieName;
  2505. lastCookieString = '';
  2506. lastToken = null;
  2507. /**
  2508. * @internal for testing
  2509. */
  2510. parseCount = 0;
  2511. constructor(doc, cookieName) {
  2512. this.doc = doc;
  2513. this.cookieName = cookieName;
  2514. }
  2515. getToken() {
  2516. if (typeof ngServerMode !== 'undefined' && ngServerMode) {
  2517. return null;
  2518. }
  2519. const cookieString = this.doc.cookie || '';
  2520. if (cookieString !== this.lastCookieString) {
  2521. this.parseCount++;
  2522. this.lastToken = parseCookieValue(cookieString, this.cookieName);
  2523. this.lastCookieString = cookieString;
  2524. }
  2525. return this.lastToken;
  2526. }
  2527. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXsrfCookieExtractor, deps: [{ token: DOCUMENT }, { token: XSRF_COOKIE_NAME }], target: i0.ɵɵFactoryTarget.Injectable });
  2528. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXsrfCookieExtractor });
  2529. }
  2530. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXsrfCookieExtractor, decorators: [{
  2531. type: Injectable
  2532. }], ctorParameters: () => [{ type: undefined, decorators: [{
  2533. type: Inject,
  2534. args: [DOCUMENT]
  2535. }] }, { type: undefined, decorators: [{
  2536. type: Inject,
  2537. args: [XSRF_COOKIE_NAME]
  2538. }] }] });
  2539. function xsrfInterceptorFn(req, next) {
  2540. const lcUrl = req.url.toLowerCase();
  2541. // Skip both non-mutating requests and absolute URLs.
  2542. // Non-mutating requests don't require a token, and absolute URLs require special handling
  2543. // anyway as the cookie set
  2544. // on our origin is not the same as the token expected by another origin.
  2545. if (!inject(XSRF_ENABLED) ||
  2546. req.method === 'GET' ||
  2547. req.method === 'HEAD' ||
  2548. lcUrl.startsWith('http://') ||
  2549. lcUrl.startsWith('https://')) {
  2550. return next(req);
  2551. }
  2552. const token = inject(HttpXsrfTokenExtractor).getToken();
  2553. const headerName = inject(XSRF_HEADER_NAME);
  2554. // Be careful not to overwrite an existing header of the same name.
  2555. if (token != null && !req.headers.has(headerName)) {
  2556. req = req.clone({ headers: req.headers.set(headerName, token) });
  2557. }
  2558. return next(req);
  2559. }
  2560. /**
  2561. * `HttpInterceptor` which adds an XSRF token to eligible outgoing requests.
  2562. */
  2563. class HttpXsrfInterceptor {
  2564. injector;
  2565. constructor(injector) {
  2566. this.injector = injector;
  2567. }
  2568. intercept(initialRequest, next) {
  2569. return runInInjectionContext(this.injector, () => xsrfInterceptorFn(initialRequest, (downstreamRequest) => next.handle(downstreamRequest)));
  2570. }
  2571. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXsrfInterceptor, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
  2572. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXsrfInterceptor });
  2573. }
  2574. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpXsrfInterceptor, decorators: [{
  2575. type: Injectable
  2576. }], ctorParameters: () => [{ type: i0.EnvironmentInjector }] });
  2577. /**
  2578. * Identifies a particular kind of `HttpFeature`.
  2579. *
  2580. * @publicApi
  2581. */
  2582. var HttpFeatureKind;
  2583. (function (HttpFeatureKind) {
  2584. HttpFeatureKind[HttpFeatureKind["Interceptors"] = 0] = "Interceptors";
  2585. HttpFeatureKind[HttpFeatureKind["LegacyInterceptors"] = 1] = "LegacyInterceptors";
  2586. HttpFeatureKind[HttpFeatureKind["CustomXsrfConfiguration"] = 2] = "CustomXsrfConfiguration";
  2587. HttpFeatureKind[HttpFeatureKind["NoXsrfProtection"] = 3] = "NoXsrfProtection";
  2588. HttpFeatureKind[HttpFeatureKind["JsonpSupport"] = 4] = "JsonpSupport";
  2589. HttpFeatureKind[HttpFeatureKind["RequestsMadeViaParent"] = 5] = "RequestsMadeViaParent";
  2590. HttpFeatureKind[HttpFeatureKind["Fetch"] = 6] = "Fetch";
  2591. })(HttpFeatureKind || (HttpFeatureKind = {}));
  2592. function makeHttpFeature(kind, providers) {
  2593. return {
  2594. ɵkind: kind,
  2595. ɵproviders: providers,
  2596. };
  2597. }
  2598. /**
  2599. * Configures Angular's `HttpClient` service to be available for injection.
  2600. *
  2601. * By default, `HttpClient` will be configured for injection with its default options for XSRF
  2602. * protection of outgoing requests. Additional configuration options can be provided by passing
  2603. * feature functions to `provideHttpClient`. For example, HTTP interceptors can be added using the
  2604. * `withInterceptors(...)` feature.
  2605. *
  2606. * <div class="docs-alert docs-alert-helpful">
  2607. *
  2608. * It's strongly recommended to enable
  2609. * [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) for applications that use
  2610. * Server-Side Rendering for better performance and compatibility. To enable `fetch`, add
  2611. * `withFetch()` feature to the `provideHttpClient()` call at the root of the application:
  2612. *
  2613. * ```ts
  2614. * provideHttpClient(withFetch());
  2615. * ```
  2616. *
  2617. * </div>
  2618. *
  2619. * @see {@link withInterceptors}
  2620. * @see {@link withInterceptorsFromDi}
  2621. * @see {@link withXsrfConfiguration}
  2622. * @see {@link withNoXsrfProtection}
  2623. * @see {@link withJsonpSupport}
  2624. * @see {@link withRequestsMadeViaParent}
  2625. * @see {@link withFetch}
  2626. */
  2627. function provideHttpClient(...features) {
  2628. if (ngDevMode) {
  2629. const featureKinds = new Set(features.map((f) => f.ɵkind));
  2630. if (featureKinds.has(HttpFeatureKind.NoXsrfProtection) &&
  2631. featureKinds.has(HttpFeatureKind.CustomXsrfConfiguration)) {
  2632. throw new Error(ngDevMode
  2633. ? `Configuration error: found both withXsrfConfiguration() and withNoXsrfProtection() in the same call to provideHttpClient(), which is a contradiction.`
  2634. : '');
  2635. }
  2636. }
  2637. const providers = [
  2638. HttpClient,
  2639. HttpXhrBackend,
  2640. HttpInterceptorHandler,
  2641. { provide: HttpHandler, useExisting: HttpInterceptorHandler },
  2642. {
  2643. provide: HttpBackend,
  2644. useFactory: () => {
  2645. return inject(FETCH_BACKEND, { optional: true }) ?? inject(HttpXhrBackend);
  2646. },
  2647. },
  2648. {
  2649. provide: HTTP_INTERCEPTOR_FNS,
  2650. useValue: xsrfInterceptorFn,
  2651. multi: true,
  2652. },
  2653. { provide: XSRF_ENABLED, useValue: true },
  2654. { provide: HttpXsrfTokenExtractor, useClass: HttpXsrfCookieExtractor },
  2655. ];
  2656. for (const feature of features) {
  2657. providers.push(...feature.ɵproviders);
  2658. }
  2659. return makeEnvironmentProviders(providers);
  2660. }
  2661. /**
  2662. * Adds one or more functional-style HTTP interceptors to the configuration of the `HttpClient`
  2663. * instance.
  2664. *
  2665. * @see {@link HttpInterceptorFn}
  2666. * @see {@link provideHttpClient}
  2667. * @publicApi
  2668. */
  2669. function withInterceptors(interceptorFns) {
  2670. return makeHttpFeature(HttpFeatureKind.Interceptors, interceptorFns.map((interceptorFn) => {
  2671. return {
  2672. provide: HTTP_INTERCEPTOR_FNS,
  2673. useValue: interceptorFn,
  2674. multi: true,
  2675. };
  2676. }));
  2677. }
  2678. const LEGACY_INTERCEPTOR_FN = new InjectionToken(ngDevMode ? 'LEGACY_INTERCEPTOR_FN' : '');
  2679. /**
  2680. * Includes class-based interceptors configured using a multi-provider in the current injector into
  2681. * the configured `HttpClient` instance.
  2682. *
  2683. * Prefer `withInterceptors` and functional interceptors instead, as support for DI-provided
  2684. * interceptors may be phased out in a later release.
  2685. *
  2686. * @see {@link HttpInterceptor}
  2687. * @see {@link HTTP_INTERCEPTORS}
  2688. * @see {@link provideHttpClient}
  2689. */
  2690. function withInterceptorsFromDi() {
  2691. // Note: the legacy interceptor function is provided here via an intermediate token
  2692. // (`LEGACY_INTERCEPTOR_FN`), using a pattern which guarantees that if these providers are
  2693. // included multiple times, all of the multi-provider entries will have the same instance of the
  2694. // interceptor function. That way, the `HttpINterceptorHandler` will dedup them and legacy
  2695. // interceptors will not run multiple times.
  2696. return makeHttpFeature(HttpFeatureKind.LegacyInterceptors, [
  2697. {
  2698. provide: LEGACY_INTERCEPTOR_FN,
  2699. useFactory: legacyInterceptorFnFactory,
  2700. },
  2701. {
  2702. provide: HTTP_INTERCEPTOR_FNS,
  2703. useExisting: LEGACY_INTERCEPTOR_FN,
  2704. multi: true,
  2705. },
  2706. ]);
  2707. }
  2708. /**
  2709. * Customizes the XSRF protection for the configuration of the current `HttpClient` instance.
  2710. *
  2711. * This feature is incompatible with the `withNoXsrfProtection` feature.
  2712. *
  2713. * @see {@link provideHttpClient}
  2714. */
  2715. function withXsrfConfiguration({ cookieName, headerName, }) {
  2716. const providers = [];
  2717. if (cookieName !== undefined) {
  2718. providers.push({ provide: XSRF_COOKIE_NAME, useValue: cookieName });
  2719. }
  2720. if (headerName !== undefined) {
  2721. providers.push({ provide: XSRF_HEADER_NAME, useValue: headerName });
  2722. }
  2723. return makeHttpFeature(HttpFeatureKind.CustomXsrfConfiguration, providers);
  2724. }
  2725. /**
  2726. * Disables XSRF protection in the configuration of the current `HttpClient` instance.
  2727. *
  2728. * This feature is incompatible with the `withXsrfConfiguration` feature.
  2729. *
  2730. * @see {@link provideHttpClient}
  2731. */
  2732. function withNoXsrfProtection() {
  2733. return makeHttpFeature(HttpFeatureKind.NoXsrfProtection, [
  2734. {
  2735. provide: XSRF_ENABLED,
  2736. useValue: false,
  2737. },
  2738. ]);
  2739. }
  2740. /**
  2741. * Add JSONP support to the configuration of the current `HttpClient` instance.
  2742. *
  2743. * @see {@link provideHttpClient}
  2744. */
  2745. function withJsonpSupport() {
  2746. return makeHttpFeature(HttpFeatureKind.JsonpSupport, [
  2747. JsonpClientBackend,
  2748. { provide: JsonpCallbackContext, useFactory: jsonpCallbackContext },
  2749. { provide: HTTP_INTERCEPTOR_FNS, useValue: jsonpInterceptorFn, multi: true },
  2750. ]);
  2751. }
  2752. /**
  2753. * Configures the current `HttpClient` instance to make requests via the parent injector's
  2754. * `HttpClient` instead of directly.
  2755. *
  2756. * By default, `provideHttpClient` configures `HttpClient` in its injector to be an independent
  2757. * instance. For example, even if `HttpClient` is configured in the parent injector with
  2758. * one or more interceptors, they will not intercept requests made via this instance.
  2759. *
  2760. * With this option enabled, once the request has passed through the current injector's
  2761. * interceptors, it will be delegated to the parent injector's `HttpClient` chain instead of
  2762. * dispatched directly, and interceptors in the parent configuration will be applied to the request.
  2763. *
  2764. * If there are several `HttpClient` instances in the injector hierarchy, it's possible for
  2765. * `withRequestsMadeViaParent` to be used at multiple levels, which will cause the request to
  2766. * "bubble up" until either reaching the root level or an `HttpClient` which was not configured with
  2767. * this option.
  2768. *
  2769. * @see {@link provideHttpClient}
  2770. * @publicApi
  2771. */
  2772. function withRequestsMadeViaParent() {
  2773. return makeHttpFeature(HttpFeatureKind.RequestsMadeViaParent, [
  2774. {
  2775. provide: HttpBackend,
  2776. useFactory: () => {
  2777. const handlerFromParent = inject(HttpHandler, { skipSelf: true, optional: true });
  2778. if (ngDevMode && handlerFromParent === null) {
  2779. throw new Error('withRequestsMadeViaParent() can only be used when the parent injector also configures HttpClient');
  2780. }
  2781. return handlerFromParent;
  2782. },
  2783. },
  2784. ]);
  2785. }
  2786. /**
  2787. * Configures the current `HttpClient` instance to make requests using the fetch API.
  2788. *
  2789. * Note: The Fetch API doesn't support progress report on uploads.
  2790. *
  2791. * @publicApi
  2792. */
  2793. function withFetch() {
  2794. return makeHttpFeature(HttpFeatureKind.Fetch, [
  2795. FetchBackend,
  2796. { provide: FETCH_BACKEND, useExisting: FetchBackend },
  2797. { provide: HttpBackend, useExisting: FetchBackend },
  2798. ]);
  2799. }
  2800. /**
  2801. * Configures XSRF protection support for outgoing requests.
  2802. *
  2803. * For a server that supports a cookie-based XSRF protection system,
  2804. * use directly to configure XSRF protection with the correct
  2805. * cookie and header names.
  2806. *
  2807. * If no names are supplied, the default cookie name is `XSRF-TOKEN`
  2808. * and the default header name is `X-XSRF-TOKEN`.
  2809. *
  2810. * @publicApi
  2811. * @deprecated Use withXsrfConfiguration({cookieName: 'XSRF-TOKEN', headerName: 'X-XSRF-TOKEN'}) as
  2812. * providers instead or `withNoXsrfProtection` if you want to disabled XSRF protection.
  2813. */
  2814. class HttpClientXsrfModule {
  2815. /**
  2816. * Disable the default XSRF protection.
  2817. */
  2818. static disable() {
  2819. return {
  2820. ngModule: HttpClientXsrfModule,
  2821. providers: [withNoXsrfProtection().ɵproviders],
  2822. };
  2823. }
  2824. /**
  2825. * Configure XSRF protection.
  2826. * @param options An object that can specify either or both
  2827. * cookie name or header name.
  2828. * - Cookie name default is `XSRF-TOKEN`.
  2829. * - Header name default is `X-XSRF-TOKEN`.
  2830. *
  2831. */
  2832. static withOptions(options = {}) {
  2833. return {
  2834. ngModule: HttpClientXsrfModule,
  2835. providers: withXsrfConfiguration(options).ɵproviders,
  2836. };
  2837. }
  2838. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientXsrfModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
  2839. static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: HttpClientXsrfModule });
  2840. static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientXsrfModule, providers: [
  2841. HttpXsrfInterceptor,
  2842. { provide: HTTP_INTERCEPTORS, useExisting: HttpXsrfInterceptor, multi: true },
  2843. { provide: HttpXsrfTokenExtractor, useClass: HttpXsrfCookieExtractor },
  2844. withXsrfConfiguration({
  2845. cookieName: XSRF_DEFAULT_COOKIE_NAME,
  2846. headerName: XSRF_DEFAULT_HEADER_NAME,
  2847. }).ɵproviders,
  2848. { provide: XSRF_ENABLED, useValue: true },
  2849. ] });
  2850. }
  2851. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientXsrfModule, decorators: [{
  2852. type: NgModule,
  2853. args: [{
  2854. providers: [
  2855. HttpXsrfInterceptor,
  2856. { provide: HTTP_INTERCEPTORS, useExisting: HttpXsrfInterceptor, multi: true },
  2857. { provide: HttpXsrfTokenExtractor, useClass: HttpXsrfCookieExtractor },
  2858. withXsrfConfiguration({
  2859. cookieName: XSRF_DEFAULT_COOKIE_NAME,
  2860. headerName: XSRF_DEFAULT_HEADER_NAME,
  2861. }).ɵproviders,
  2862. { provide: XSRF_ENABLED, useValue: true },
  2863. ],
  2864. }]
  2865. }] });
  2866. /**
  2867. * Configures the dependency injector for `HttpClient`
  2868. * with supporting services for XSRF. Automatically imported by `HttpClientModule`.
  2869. *
  2870. * You can add interceptors to the chain behind `HttpClient` by binding them to the
  2871. * multiprovider for built-in DI token `HTTP_INTERCEPTORS`.
  2872. *
  2873. * @publicApi
  2874. * @deprecated use `provideHttpClient(withInterceptorsFromDi())` as providers instead
  2875. */
  2876. class HttpClientModule {
  2877. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
  2878. static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: HttpClientModule });
  2879. static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientModule, providers: [provideHttpClient(withInterceptorsFromDi())] });
  2880. }
  2881. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientModule, decorators: [{
  2882. type: NgModule,
  2883. args: [{
  2884. /**
  2885. * Configures the dependency injector where it is imported
  2886. * with supporting services for HTTP communications.
  2887. */
  2888. providers: [provideHttpClient(withInterceptorsFromDi())],
  2889. }]
  2890. }] });
  2891. /**
  2892. * Configures the dependency injector for `HttpClient`
  2893. * with supporting services for JSONP.
  2894. * Without this module, Jsonp requests reach the backend
  2895. * with method JSONP, where they are rejected.
  2896. *
  2897. * @publicApi
  2898. * @deprecated `withJsonpSupport()` as providers instead
  2899. */
  2900. class HttpClientJsonpModule {
  2901. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientJsonpModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
  2902. static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: HttpClientJsonpModule });
  2903. static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientJsonpModule, providers: [withJsonpSupport().ɵproviders] });
  2904. }
  2905. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: HttpClientJsonpModule, decorators: [{
  2906. type: NgModule,
  2907. args: [{
  2908. providers: [withJsonpSupport().ɵproviders],
  2909. }]
  2910. }] });
  2911. export { FetchBackend, HTTP_INTERCEPTORS, HTTP_ROOT_INTERCEPTOR_FNS, HttpBackend, HttpClient, HttpClientJsonpModule, HttpClientModule, HttpClientXsrfModule, HttpContext, HttpContextToken, HttpErrorResponse, HttpEventType, HttpFeatureKind, HttpHandler, HttpHeaderResponse, HttpHeaders, HttpInterceptorHandler, HttpParams, HttpRequest, HttpResponse, HttpResponseBase, HttpStatusCode, HttpUrlEncodingCodec, HttpXhrBackend, HttpXsrfTokenExtractor, JsonpClientBackend, JsonpInterceptor, REQUESTS_CONTRIBUTE_TO_STABILITY, provideHttpClient, withFetch, withInterceptors, withInterceptorsFromDi, withJsonpSupport, withNoXsrfProtection, withRequestsMadeViaParent, withXsrfConfiguration };
  2912. //# sourceMappingURL=module-z3bvLlVg.mjs.map