@angular/common 20.2.0 → 21.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,42 +1,15 @@
1
1
  /**
2
- * @license Angular v20.2.0
2
+ * @license Angular v21.0.0-next.0
3
3
  * (c) 2010-2025 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
6
6
 
7
7
  import * as i0 from '@angular/core';
8
- import { ɵRuntimeError as _RuntimeError, Injectable, InjectionToken, inject, NgZone, DestroyRef, ɵformatRuntimeError as _formatRuntimeError, PendingTasks, ɵConsole as _Console, runInInjectionContext, DOCUMENT, Inject, makeEnvironmentProviders, NgModule } from '@angular/core';
9
- import { concatMap, filter, map, finalize, switchMap } from 'rxjs/operators';
10
- import { of, Observable, from } from 'rxjs';
8
+ import { ɵRuntimeError as _RuntimeError, InjectionToken, inject, NgZone, DestroyRef, Injectable, ɵformatRuntimeError as _formatRuntimeError, runInInjectionContext, PendingTasks, ɵConsole as _Console, DOCUMENT, Inject, makeEnvironmentProviders, NgModule } from '@angular/core';
9
+ import { switchMap, finalize, concatMap, filter, map } from 'rxjs/operators';
10
+ import { Observable, from, of } from 'rxjs';
11
11
  import { XhrFactory, parseCookieValue } from './xhr.mjs';
12
12
 
13
- /**
14
- * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a
15
- * `HttpResponse`.
16
- *
17
- * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the
18
- * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the
19
- * `HttpBackend`.
20
- *
21
- * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain.
22
- *
23
- * @publicApi
24
- */
25
- class HttpHandler {
26
- }
27
- /**
28
- * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend.
29
- *
30
- * Interceptors sit between the `HttpClient` interface and the `HttpBackend`.
31
- *
32
- * When injected, `HttpBackend` dispatches requests directly to the backend, without going
33
- * through the interceptor chain.
34
- *
35
- * @publicApi
36
- */
37
- class HttpBackend {
38
- }
39
-
40
13
  /**
41
14
  * Represents the header configuration options for an HTTP request.
42
15
  * Instances are immutable. Modifying methods return a cloned
@@ -288,6 +261,106 @@ function assertValidHeaders(headers) {
288
261
  }
289
262
  }
290
263
 
264
+ /**
265
+ * A token used to manipulate and access values stored in `HttpContext`.
266
+ *
267
+ * @publicApi
268
+ */
269
+ class HttpContextToken {
270
+ defaultValue;
271
+ constructor(defaultValue) {
272
+ this.defaultValue = defaultValue;
273
+ }
274
+ }
275
+ /**
276
+ * Http context stores arbitrary user defined values and ensures type safety without
277
+ * actually knowing the types. It is backed by a `Map` and guarantees that keys do not clash.
278
+ *
279
+ * This context is mutable and is shared between cloned requests unless explicitly specified.
280
+ *
281
+ * @usageNotes
282
+ *
283
+ * ### Usage Example
284
+ *
285
+ * ```ts
286
+ * // inside cache.interceptors.ts
287
+ * export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false);
288
+ *
289
+ * export class CacheInterceptor implements HttpInterceptor {
290
+ *
291
+ * intercept(req: HttpRequest<any>, delegate: HttpHandler): Observable<HttpEvent<any>> {
292
+ * if (req.context.get(IS_CACHE_ENABLED) === true) {
293
+ * return ...;
294
+ * }
295
+ * return delegate.handle(req);
296
+ * }
297
+ * }
298
+ *
299
+ * // inside a service
300
+ *
301
+ * this.httpClient.get('/api/weather', {
302
+ * context: new HttpContext().set(IS_CACHE_ENABLED, true)
303
+ * }).subscribe(...);
304
+ * ```
305
+ *
306
+ * @publicApi
307
+ */
308
+ class HttpContext {
309
+ map = new Map();
310
+ /**
311
+ * Store a value in the context. If a value is already present it will be overwritten.
312
+ *
313
+ * @param token The reference to an instance of `HttpContextToken`.
314
+ * @param value The value to store.
315
+ *
316
+ * @returns A reference to itself for easy chaining.
317
+ */
318
+ set(token, value) {
319
+ this.map.set(token, value);
320
+ return this;
321
+ }
322
+ /**
323
+ * Retrieve the value associated with the given token.
324
+ *
325
+ * @param token The reference to an instance of `HttpContextToken`.
326
+ *
327
+ * @returns The stored value or default if one is defined.
328
+ */
329
+ get(token) {
330
+ if (!this.map.has(token)) {
331
+ this.map.set(token, token.defaultValue());
332
+ }
333
+ return this.map.get(token);
334
+ }
335
+ /**
336
+ * Delete the value associated with the given token.
337
+ *
338
+ * @param token The reference to an instance of `HttpContextToken`.
339
+ *
340
+ * @returns A reference to itself for easy chaining.
341
+ */
342
+ delete(token) {
343
+ this.map.delete(token);
344
+ return this;
345
+ }
346
+ /**
347
+ * Checks for existence of a given token.
348
+ *
349
+ * @param token The reference to an instance of `HttpContextToken`.
350
+ *
351
+ * @returns True if the token exists, false otherwise.
352
+ */
353
+ has(token) {
354
+ return this.map.has(token);
355
+ }
356
+ /**
357
+ * @returns a list of tokens currently stored in the context.
358
+ */
359
+ keys() {
360
+ return this.map.keys();
361
+ }
362
+ }
363
+
291
364
  /**
292
365
  * Provides encoding and decoding of URL parameter and query-string values.
293
366
  *
@@ -559,106 +632,6 @@ class HttpParams {
559
632
  }
560
633
  }
561
634
 
562
- /**
563
- * A token used to manipulate and access values stored in `HttpContext`.
564
- *
565
- * @publicApi
566
- */
567
- class HttpContextToken {
568
- defaultValue;
569
- constructor(defaultValue) {
570
- this.defaultValue = defaultValue;
571
- }
572
- }
573
- /**
574
- * Http context stores arbitrary user defined values and ensures type safety without
575
- * actually knowing the types. It is backed by a `Map` and guarantees that keys do not clash.
576
- *
577
- * This context is mutable and is shared between cloned requests unless explicitly specified.
578
- *
579
- * @usageNotes
580
- *
581
- * ### Usage Example
582
- *
583
- * ```ts
584
- * // inside cache.interceptors.ts
585
- * export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false);
586
- *
587
- * export class CacheInterceptor implements HttpInterceptor {
588
- *
589
- * intercept(req: HttpRequest<any>, delegate: HttpHandler): Observable<HttpEvent<any>> {
590
- * if (req.context.get(IS_CACHE_ENABLED) === true) {
591
- * return ...;
592
- * }
593
- * return delegate.handle(req);
594
- * }
595
- * }
596
- *
597
- * // inside a service
598
- *
599
- * this.httpClient.get('/api/weather', {
600
- * context: new HttpContext().set(IS_CACHE_ENABLED, true)
601
- * }).subscribe(...);
602
- * ```
603
- *
604
- * @publicApi
605
- */
606
- class HttpContext {
607
- map = new Map();
608
- /**
609
- * Store a value in the context. If a value is already present it will be overwritten.
610
- *
611
- * @param token The reference to an instance of `HttpContextToken`.
612
- * @param value The value to store.
613
- *
614
- * @returns A reference to itself for easy chaining.
615
- */
616
- set(token, value) {
617
- this.map.set(token, value);
618
- return this;
619
- }
620
- /**
621
- * Retrieve the value associated with the given token.
622
- *
623
- * @param token The reference to an instance of `HttpContextToken`.
624
- *
625
- * @returns The stored value or default if one is defined.
626
- */
627
- get(token) {
628
- if (!this.map.has(token)) {
629
- this.map.set(token, token.defaultValue());
630
- }
631
- return this.map.get(token);
632
- }
633
- /**
634
- * Delete the value associated with the given token.
635
- *
636
- * @param token The reference to an instance of `HttpContextToken`.
637
- *
638
- * @returns A reference to itself for easy chaining.
639
- */
640
- delete(token) {
641
- this.map.delete(token);
642
- return this;
643
- }
644
- /**
645
- * Checks for existence of a given token.
646
- *
647
- * @param token The reference to an instance of `HttpContextToken`.
648
- *
649
- * @returns True if the token exists, false otherwise.
650
- */
651
- has(token) {
652
- return this.map.has(token);
653
- }
654
- /**
655
- * @returns a list of tokens currently stored in the context.
656
- */
657
- keys() {
658
- return this.map.keys();
659
- }
660
- }
661
-
662
635
  /**
663
636
  * Determine whether the given HTTP method may include a body.
664
637
  */
@@ -1361,421 +1334,95 @@ var HttpStatusCode;
1361
1334
  HttpStatusCode[HttpStatusCode["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired";
1362
1335
  })(HttpStatusCode || (HttpStatusCode = {}));
1363
1336
 
1337
+ const XSSI_PREFIX$1 = /^\)\]\}',?\n/;
1364
1338
  /**
1365
- * Constructs an instance of `HttpRequestOptions<T>` from a source `HttpMethodOptions` and
1366
- * the given `body`. This function clones the object and adds the body.
1339
+ * Determine an appropriate URL for the response, by checking either
1340
+ * response url or the X-Request-URL header.
1341
+ */
1342
+ function getResponseUrl$1(response) {
1343
+ if (response.url) {
1344
+ return response.url;
1345
+ }
1346
+ // stored as lowercase in the map
1347
+ const xRequestUrl = X_REQUEST_URL_HEADER.toLocaleLowerCase();
1348
+ return response.headers.get(xRequestUrl);
1349
+ }
1350
+ /**
1351
+ * An internal injection token to reference `FetchBackend` implementation
1352
+ * in a tree-shakable way.
1353
+ */
1354
+ const FETCH_BACKEND = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'FETCH_BACKEND' : '');
1355
+ /**
1356
+ * Uses `fetch` to send requests to a backend server.
1367
1357
  *
1368
- * Note that the `responseType` *options* value is a String that identifies the
1369
- * single data type of the response.
1370
- * A single overload version of the method handles each response type.
1371
- * The value of `responseType` cannot be a union, as the combined signature could imply.
1358
+ * This `FetchBackend` requires the support of the
1359
+ * [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) which is available on all
1360
+ * supported browsers and on Node.js v18 or later.
1372
1361
  *
1373
- */
1374
- function addBody(options, body) {
1375
- return {
1376
- body,
1377
- headers: options.headers,
1378
- context: options.context,
1379
- observe: options.observe,
1380
- params: options.params,
1381
- reportProgress: options.reportProgress,
1382
- responseType: options.responseType,
1383
- withCredentials: options.withCredentials,
1384
- credentials: options.credentials,
1385
- transferCache: options.transferCache,
1386
- timeout: options.timeout,
1387
- keepalive: options.keepalive,
1388
- priority: options.priority,
1389
- cache: options.cache,
1390
- mode: options.mode,
1391
- redirect: options.redirect,
1392
- integrity: options.integrity,
1393
- referrer: options.referrer,
1394
- };
1395
- }
1396
- /**
1397
- * Performs HTTP requests.
1398
- * This service is available as an injectable class, with methods to perform HTTP requests.
1399
- * Each request method has multiple signatures, and the return type varies based on
1400
- * the signature that is called (mainly the values of `observe` and `responseType`).
1401
- *
1402
- * Note that the `responseType` *options* value is a String that identifies the
1403
- * single data type of the response.
1404
- * A single overload version of the method handles each response type.
1405
- * The value of `responseType` cannot be a union, as the combined signature could imply.
1406
- *
1407
- * @usageNotes
1408
- *
1409
- * ### HTTP Request Example
1410
- *
1411
- * ```ts
1412
- * // GET heroes whose name contains search term
1413
- * searchHeroes(term: string): observable<Hero[]>{
1414
- *
1415
- * const params = new HttpParams({fromString: 'name=term'});
1416
- * return this.httpClient.request('GET', this.heroesUrl, {responseType:'json', params});
1417
- * }
1418
- * ```
1419
- *
1420
- * Alternatively, the parameter string can be used without invoking HttpParams
1421
- * by directly joining to the URL.
1422
- * ```ts
1423
- * this.httpClient.request('GET', this.heroesUrl + '?' + 'name=term', {responseType:'json'});
1424
- * ```
1425
- *
1426
- *
1427
- * ### JSONP Example
1428
- * ```ts
1429
- * requestJsonp(url, callback = 'callback') {
1430
- * return this.httpClient.jsonp(this.heroesURL, callback);
1431
- * }
1432
- * ```
1433
- *
1434
- * ### PATCH Example
1435
- * ```ts
1436
- * // PATCH one of the heroes' name
1437
- * patchHero (id: number, heroName: string): Observable<{}> {
1438
- * const url = `${this.heroesUrl}/${id}`; // PATCH api/heroes/42
1439
- * return this.httpClient.patch(url, {name: heroName}, httpOptions)
1440
- * .pipe(catchError(this.handleError('patchHero')));
1441
- * }
1442
- * ```
1443
- *
1444
- * @see [HTTP Guide](guide/http)
1445
- * @see [HTTP Request](api/common/http/HttpRequest)
1362
+ * @see {@link HttpHandler}
1446
1363
  *
1447
1364
  * @publicApi
1448
1365
  */
1449
- class HttpClient {
1450
- handler;
1451
- constructor(handler) {
1452
- this.handler = handler;
1366
+ class FetchBackend {
1367
+ // We use an arrow function to always reference the current global implementation of `fetch`.
1368
+ // This is helpful for cases when the global `fetch` implementation is modified by external code,
1369
+ // see https://github.com/angular/angular/issues/57527.
1370
+ fetchImpl = inject(FetchFactory, { optional: true })?.fetch ?? ((...args) => globalThis.fetch(...args));
1371
+ ngZone = inject(NgZone);
1372
+ destroyRef = inject(DestroyRef);
1373
+ destroyed = false;
1374
+ constructor() {
1375
+ this.destroyRef.onDestroy(() => {
1376
+ this.destroyed = true;
1377
+ });
1453
1378
  }
1454
- /**
1455
- * Constructs an observable for a generic HTTP request that, when subscribed,
1456
- * fires the request through the chain of registered interceptors and on to the
1457
- * server.
1458
- *
1459
- * You can pass an `HttpRequest` directly as the only parameter. In this case,
1460
- * the call returns an observable of the raw `HttpEvent` stream.
1461
- *
1462
- * Alternatively you can pass an HTTP method as the first parameter,
1463
- * a URL string as the second, and an options hash containing the request body as the third.
1464
- * See `addBody()`. In this case, the specified `responseType` and `observe` options determine the
1465
- * type of returned observable.
1466
- * * The `responseType` value determines how a successful response body is parsed.
1467
- * * If `responseType` is the default `json`, you can pass a type interface for the resulting
1468
- * object as a type parameter to the call.
1469
- *
1470
- * The `observe` value determines the return type, according to what you are interested in
1471
- * observing.
1472
- * * An `observe` value of events returns an observable of the raw `HttpEvent` stream, including
1473
- * progress events by default.
1474
- * * An `observe` value of response returns an observable of `HttpResponse<T>`,
1475
- * where the `T` parameter depends on the `responseType` and any optionally provided type
1476
- * parameter.
1477
- * * An `observe` value of body returns an observable of `<T>` with the same `T` body type.
1478
- *
1479
- */
1480
- request(first, url, options = {}) {
1481
- let req;
1482
- // First, check whether the primary argument is an instance of `HttpRequest`.
1483
- if (first instanceof HttpRequest) {
1484
- // It is. The other arguments must be undefined (per the signatures) and can be
1485
- // ignored.
1486
- req = first;
1487
- }
1488
- else {
1489
- // It's a string, so it represents a URL. Construct a request based on it,
1490
- // and incorporate the remaining arguments (assuming `GET` unless a method is
1491
- // provided.
1492
- // Figure out the headers.
1493
- let headers = undefined;
1494
- if (options.headers instanceof HttpHeaders) {
1495
- headers = options.headers;
1496
- }
1497
- else {
1498
- headers = new HttpHeaders(options.headers);
1379
+ handle(request) {
1380
+ return new Observable((observer) => {
1381
+ const aborter = new AbortController();
1382
+ this.doRequest(request, aborter.signal, observer).then(noop, (error) => observer.error(new HttpErrorResponse({ error })));
1383
+ let timeoutId;
1384
+ if (request.timeout) {
1385
+ // TODO: Replace with AbortSignal.any([aborter.signal, AbortSignal.timeout(request.timeout)])
1386
+ // when AbortSignal.any support is Baseline widely available (NET nov. 2026)
1387
+ timeoutId = this.ngZone.runOutsideAngular(() => setTimeout(() => {
1388
+ if (!aborter.signal.aborted) {
1389
+ aborter.abort(new DOMException('signal timed out', 'TimeoutError'));
1390
+ }
1391
+ }, request.timeout));
1499
1392
  }
1500
- // Sort out parameters.
1501
- let params = undefined;
1502
- if (!!options.params) {
1503
- if (options.params instanceof HttpParams) {
1504
- params = options.params;
1505
- }
1506
- else {
1507
- params = new HttpParams({ fromObject: options.params });
1393
+ return () => {
1394
+ if (timeoutId !== undefined) {
1395
+ clearTimeout(timeoutId);
1508
1396
  }
1509
- }
1510
- // Construct the request.
1511
- req = new HttpRequest(first, url, options.body !== undefined ? options.body : null, {
1512
- headers,
1513
- context: options.context,
1514
- params,
1515
- reportProgress: options.reportProgress,
1516
- // By default, JSON is assumed to be returned for all calls.
1517
- responseType: options.responseType || 'json',
1518
- withCredentials: options.withCredentials,
1519
- transferCache: options.transferCache,
1520
- keepalive: options.keepalive,
1521
- priority: options.priority,
1522
- cache: options.cache,
1523
- mode: options.mode,
1524
- redirect: options.redirect,
1525
- credentials: options.credentials,
1526
- referrer: options.referrer,
1527
- integrity: options.integrity,
1528
- timeout: options.timeout,
1529
- });
1397
+ aborter.abort();
1398
+ };
1399
+ });
1400
+ }
1401
+ async doRequest(request, signal, observer) {
1402
+ const init = this.createRequestInit(request);
1403
+ let response;
1404
+ try {
1405
+ // Run fetch outside of Angular zone.
1406
+ // This is due to Node.js fetch implementation (Undici) which uses a number of setTimeouts to check if
1407
+ // the response should eventually timeout which causes extra CD cycles every 500ms
1408
+ const fetchPromise = this.ngZone.runOutsideAngular(() => this.fetchImpl(request.urlWithParams, { signal, ...init }));
1409
+ // Make sure Zone.js doesn't trigger false-positive unhandled promise
1410
+ // error in case the Promise is rejected synchronously. See function
1411
+ // description for additional information.
1412
+ silenceSuperfluousUnhandledPromiseRejection(fetchPromise);
1413
+ // Send the `Sent` event before awaiting the response.
1414
+ observer.next({ type: HttpEventType.Sent });
1415
+ response = await fetchPromise;
1530
1416
  }
1531
- // Start with an Observable.of() the initial request, and run the handler (which
1532
- // includes all interceptors) inside a concatMap(). This way, the handler runs
1533
- // inside an Observable chain, which causes interceptors to be re-run on every
1534
- // subscription (this also makes retries re-run the handler, including interceptors).
1535
- const events$ = of(req).pipe(concatMap((req) => this.handler.handle(req)));
1536
- // If coming via the API signature which accepts a previously constructed HttpRequest,
1537
- // the only option is to get the event stream. Otherwise, return the event stream if
1538
- // that is what was requested.
1539
- if (first instanceof HttpRequest || options.observe === 'events') {
1540
- return events$;
1541
- }
1542
- // The requested stream contains either the full response or the body. In either
1543
- // case, the first step is to filter the event stream to extract a stream of
1544
- // responses(s).
1545
- const res$ = (events$.pipe(filter((event) => event instanceof HttpResponse)));
1546
- // Decide which stream to return.
1547
- switch (options.observe || 'body') {
1548
- case 'body':
1549
- // The requested stream is the body. Map the response stream to the response
1550
- // body. This could be done more simply, but a misbehaving interceptor might
1551
- // transform the response body into a different format and ignore the requested
1552
- // responseType. Guard against this by validating that the response is of the
1553
- // requested type.
1554
- switch (req.responseType) {
1555
- case 'arraybuffer':
1556
- return res$.pipe(map((res) => {
1557
- // Validate that the body is an ArrayBuffer.
1558
- if (res.body !== null && !(res.body instanceof ArrayBuffer)) {
1559
- throw new _RuntimeError(2806 /* RuntimeErrorCode.RESPONSE_IS_NOT_AN_ARRAY_BUFFER */, ngDevMode && 'Response is not an ArrayBuffer.');
1560
- }
1561
- return res.body;
1562
- }));
1563
- case 'blob':
1564
- return res$.pipe(map((res) => {
1565
- // Validate that the body is a Blob.
1566
- if (res.body !== null && !(res.body instanceof Blob)) {
1567
- throw new _RuntimeError(2807 /* RuntimeErrorCode.RESPONSE_IS_NOT_A_BLOB */, ngDevMode && 'Response is not a Blob.');
1568
- }
1569
- return res.body;
1570
- }));
1571
- case 'text':
1572
- return res$.pipe(map((res) => {
1573
- // Validate that the body is a string.
1574
- if (res.body !== null && typeof res.body !== 'string') {
1575
- throw new _RuntimeError(2808 /* RuntimeErrorCode.RESPONSE_IS_NOT_A_STRING */, ngDevMode && 'Response is not a string.');
1576
- }
1577
- return res.body;
1578
- }));
1579
- case 'json':
1580
- default:
1581
- // No validation needed for JSON responses, as they can be of any type.
1582
- return res$.pipe(map((res) => res.body));
1583
- }
1584
- case 'response':
1585
- // The response stream was requested directly, so return it.
1586
- return res$;
1587
- default:
1588
- // Guard against new future observe types being added.
1589
- throw new _RuntimeError(2809 /* RuntimeErrorCode.UNHANDLED_OBSERVE_TYPE */, ngDevMode && `Unreachable: unhandled observe type ${options.observe}}`);
1590
- }
1591
- }
1592
- /**
1593
- * Constructs an observable that, when subscribed, causes the configured
1594
- * `DELETE` request to execute on the server. See the individual overloads for
1595
- * details on the return type.
1596
- *
1597
- * @param url The endpoint URL.
1598
- * @param options The HTTP options to send with the request.
1599
- *
1600
- */
1601
- delete(url, options = {}) {
1602
- return this.request('DELETE', url, options);
1603
- }
1604
- /**
1605
- * Constructs an observable that, when subscribed, causes the configured
1606
- * `GET` request to execute on the server. See the individual overloads for
1607
- * details on the return type.
1608
- */
1609
- get(url, options = {}) {
1610
- return this.request('GET', url, options);
1611
- }
1612
- /**
1613
- * Constructs an observable that, when subscribed, causes the configured
1614
- * `HEAD` request to execute on the server. The `HEAD` method returns
1615
- * meta information about the resource without transferring the
1616
- * resource itself. See the individual overloads for
1617
- * details on the return type.
1618
- */
1619
- head(url, options = {}) {
1620
- return this.request('HEAD', url, options);
1621
- }
1622
- /**
1623
- * Constructs an `Observable` that, when subscribed, causes a request with the special method
1624
- * `JSONP` to be dispatched via the interceptor pipeline.
1625
- * The [JSONP pattern](https://en.wikipedia.org/wiki/JSONP) works around limitations of certain
1626
- * API endpoints that don't support newer,
1627
- * and preferable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) protocol.
1628
- * JSONP treats the endpoint API as a JavaScript file and tricks the browser to process the
1629
- * requests even if the API endpoint is not located on the same domain (origin) as the client-side
1630
- * application making the request.
1631
- * The endpoint API must support JSONP callback for JSONP requests to work.
1632
- * The resource API returns the JSON response wrapped in a callback function.
1633
- * You can pass the callback function name as one of the query parameters.
1634
- * Note that JSONP requests can only be used with `GET` requests.
1635
- *
1636
- * @param url The resource URL.
1637
- * @param callbackParam The callback function name.
1638
- *
1639
- */
1640
- jsonp(url, callbackParam) {
1641
- return this.request('JSONP', url, {
1642
- params: new HttpParams().append(callbackParam, 'JSONP_CALLBACK'),
1643
- observe: 'body',
1644
- responseType: 'json',
1645
- });
1646
- }
1647
- /**
1648
- * Constructs an `Observable` that, when subscribed, causes the configured
1649
- * `OPTIONS` request to execute on the server. This method allows the client
1650
- * to determine the supported HTTP methods and other capabilities of an endpoint,
1651
- * without implying a resource action. See the individual overloads for
1652
- * details on the return type.
1653
- */
1654
- options(url, options = {}) {
1655
- return this.request('OPTIONS', url, options);
1656
- }
1657
- /**
1658
- * Constructs an observable that, when subscribed, causes the configured
1659
- * `PATCH` request to execute on the server. See the individual overloads for
1660
- * details on the return type.
1661
- */
1662
- patch(url, body, options = {}) {
1663
- return this.request('PATCH', url, addBody(options, body));
1664
- }
1665
- /**
1666
- * Constructs an observable that, when subscribed, causes the configured
1667
- * `POST` request to execute on the server. The server responds with the location of
1668
- * the replaced resource. See the individual overloads for
1669
- * details on the return type.
1670
- */
1671
- post(url, body, options = {}) {
1672
- return this.request('POST', url, addBody(options, body));
1673
- }
1674
- /**
1675
- * Constructs an observable that, when subscribed, causes the configured
1676
- * `PUT` request to execute on the server. The `PUT` method replaces an existing resource
1677
- * with a new set of values.
1678
- * See the individual overloads for details on the return type.
1679
- */
1680
- put(url, body, options = {}) {
1681
- return this.request('PUT', url, addBody(options, body));
1682
- }
1683
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpClient, deps: [{ token: HttpHandler }], target: i0.ɵɵFactoryTarget.Injectable });
1684
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpClient });
1685
- }
1686
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpClient, decorators: [{
1687
- type: Injectable
1688
- }], ctorParameters: () => [{ type: HttpHandler }] });
1689
-
1690
- const XSSI_PREFIX$1 = /^\)\]\}',?\n/;
1691
- /**
1692
- * Determine an appropriate URL for the response, by checking either
1693
- * response url or the X-Request-URL header.
1694
- */
1695
- function getResponseUrl$1(response) {
1696
- if (response.url) {
1697
- return response.url;
1698
- }
1699
- // stored as lowercase in the map
1700
- const xRequestUrl = X_REQUEST_URL_HEADER.toLocaleLowerCase();
1701
- return response.headers.get(xRequestUrl);
1702
- }
1703
- /**
1704
- * An internal injection token to reference `FetchBackend` implementation
1705
- * in a tree-shakable way.
1706
- */
1707
- const FETCH_BACKEND = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'FETCH_BACKEND' : '');
1708
- /**
1709
- * Uses `fetch` to send requests to a backend server.
1710
- *
1711
- * This `FetchBackend` requires the support of the
1712
- * [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) which is available on all
1713
- * supported browsers and on Node.js v18 or later.
1714
- *
1715
- * @see {@link HttpHandler}
1716
- *
1717
- * @publicApi
1718
- */
1719
- class FetchBackend {
1720
- // We use an arrow function to always reference the current global implementation of `fetch`.
1721
- // This is helpful for cases when the global `fetch` implementation is modified by external code,
1722
- // see https://github.com/angular/angular/issues/57527.
1723
- fetchImpl = inject(FetchFactory, { optional: true })?.fetch ?? ((...args) => globalThis.fetch(...args));
1724
- ngZone = inject(NgZone);
1725
- destroyRef = inject(DestroyRef);
1726
- destroyed = false;
1727
- constructor() {
1728
- this.destroyRef.onDestroy(() => {
1729
- this.destroyed = true;
1730
- });
1731
- }
1732
- handle(request) {
1733
- return new Observable((observer) => {
1734
- const aborter = new AbortController();
1735
- this.doRequest(request, aborter.signal, observer).then(noop, (error) => observer.error(new HttpErrorResponse({ error })));
1736
- let timeoutId;
1737
- if (request.timeout) {
1738
- // TODO: Replace with AbortSignal.any([aborter.signal, AbortSignal.timeout(request.timeout)])
1739
- // when AbortSignal.any support is Baseline widely available (NET nov. 2026)
1740
- timeoutId = this.ngZone.runOutsideAngular(() => setTimeout(() => {
1741
- if (!aborter.signal.aborted) {
1742
- aborter.abort(new DOMException('signal timed out', 'TimeoutError'));
1743
- }
1744
- }, request.timeout));
1745
- }
1746
- return () => {
1747
- if (timeoutId !== undefined) {
1748
- clearTimeout(timeoutId);
1749
- }
1750
- aborter.abort();
1751
- };
1752
- });
1753
- }
1754
- async doRequest(request, signal, observer) {
1755
- const init = this.createRequestInit(request);
1756
- let response;
1757
- try {
1758
- // Run fetch outside of Angular zone.
1759
- // This is due to Node.js fetch implementation (Undici) which uses a number of setTimeouts to check if
1760
- // the response should eventually timeout which causes extra CD cycles every 500ms
1761
- const fetchPromise = this.ngZone.runOutsideAngular(() => this.fetchImpl(request.urlWithParams, { signal, ...init }));
1762
- // Make sure Zone.js doesn't trigger false-positive unhandled promise
1763
- // error in case the Promise is rejected synchronously. See function
1764
- // description for additional information.
1765
- silenceSuperfluousUnhandledPromiseRejection(fetchPromise);
1766
- // Send the `Sent` event before awaiting the response.
1767
- observer.next({ type: HttpEventType.Sent });
1768
- response = await fetchPromise;
1769
- }
1770
- catch (error) {
1771
- observer.error(new HttpErrorResponse({
1772
- error,
1773
- status: error.status ?? 0,
1774
- statusText: error.statusText,
1775
- url: request.urlWithParams,
1776
- headers: error.headers,
1777
- }));
1778
- return;
1417
+ catch (error) {
1418
+ observer.error(new HttpErrorResponse({
1419
+ error,
1420
+ status: error.status ?? 0,
1421
+ statusText: error.statusText,
1422
+ url: request.urlWithParams,
1423
+ headers: error.headers,
1424
+ }));
1425
+ return;
1779
1426
  }
1780
1427
  const headers = new HttpHeaders(response.headers);
1781
1428
  const statusText = response.statusText;
@@ -1916,92 +1563,448 @@ class FetchBackend {
1916
1563
  if (status < 200 || status >= 300) {
1917
1564
  return text;
1918
1565
  }
1919
- throw e;
1566
+ throw e;
1567
+ }
1568
+ case 'text':
1569
+ return new TextDecoder().decode(binContent);
1570
+ case 'blob':
1571
+ return new Blob([binContent], { type: contentType });
1572
+ case 'arraybuffer':
1573
+ return binContent.buffer;
1574
+ }
1575
+ }
1576
+ createRequestInit(req) {
1577
+ // We could share some of this logic with the XhrBackend
1578
+ const headers = {};
1579
+ let credentials;
1580
+ // If the request has a credentials property, use it.
1581
+ // Otherwise, if the request has withCredentials set to true, use 'include'.
1582
+ credentials = req.credentials;
1583
+ // If withCredentials is true should be set to 'include', for compatibility
1584
+ if (req.withCredentials) {
1585
+ // A warning is logged in development mode if the request has both
1586
+ (typeof ngDevMode === 'undefined' || ngDevMode) && warningOptionsMessage(req);
1587
+ credentials = 'include';
1588
+ }
1589
+ // Setting all the requested headers.
1590
+ req.headers.forEach((name, values) => (headers[name] = values.join(',')));
1591
+ // Add an Accept header if one isn't present already.
1592
+ if (!req.headers.has(ACCEPT_HEADER)) {
1593
+ headers[ACCEPT_HEADER] = ACCEPT_HEADER_VALUE;
1594
+ }
1595
+ // Auto-detect the Content-Type header if one isn't present already.
1596
+ if (!req.headers.has(CONTENT_TYPE_HEADER)) {
1597
+ const detectedType = req.detectContentTypeHeader();
1598
+ // Sometimes Content-Type detection fails.
1599
+ if (detectedType !== null) {
1600
+ headers[CONTENT_TYPE_HEADER] = detectedType;
1601
+ }
1602
+ }
1603
+ return {
1604
+ body: req.serializeBody(),
1605
+ method: req.method,
1606
+ headers,
1607
+ credentials,
1608
+ keepalive: req.keepalive,
1609
+ cache: req.cache,
1610
+ priority: req.priority,
1611
+ mode: req.mode,
1612
+ redirect: req.redirect,
1613
+ referrer: req.referrer,
1614
+ integrity: req.integrity,
1615
+ };
1616
+ }
1617
+ concatChunks(chunks, totalLength) {
1618
+ const chunksAll = new Uint8Array(totalLength);
1619
+ let position = 0;
1620
+ for (const chunk of chunks) {
1621
+ chunksAll.set(chunk, position);
1622
+ position += chunk.length;
1623
+ }
1624
+ return chunksAll;
1625
+ }
1626
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FetchBackend, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1627
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FetchBackend });
1628
+ }
1629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FetchBackend, decorators: [{
1630
+ type: Injectable
1631
+ }], ctorParameters: () => [] });
1632
+ /**
1633
+ * Abstract class to provide a mocked implementation of `fetch()`
1634
+ */
1635
+ class FetchFactory {
1636
+ }
1637
+ function noop() { }
1638
+ function warningOptionsMessage(req) {
1639
+ if (req.credentials && req.withCredentials) {
1640
+ console.warn(_formatRuntimeError(2819 /* RuntimeErrorCode.WITH_CREDENTIALS_OVERRIDES_EXPLICIT_CREDENTIALS */, `Angular detected that a \`HttpClient\` request has both \`withCredentials: true\` and \`credentials: '${req.credentials}'\` options. The \`withCredentials\` option is overriding the explicit \`credentials\` setting to 'include'. Consider removing \`withCredentials\` and using \`credentials: '${req.credentials}'\` directly for clarity.`));
1641
+ }
1642
+ }
1643
+ /**
1644
+ * Zone.js treats a rejected promise that has not yet been awaited
1645
+ * as an unhandled error. This function adds a noop `.then` to make
1646
+ * sure that Zone.js doesn't throw an error if the Promise is rejected
1647
+ * synchronously.
1648
+ */
1649
+ function silenceSuperfluousUnhandledPromiseRejection(promise) {
1650
+ promise.then(noop, noop);
1651
+ }
1652
+
1653
+ const XSSI_PREFIX = /^\)\]\}',?\n/;
1654
+ const X_REQUEST_URL_REGEXP = RegExp(`^${X_REQUEST_URL_HEADER}:`, 'm');
1655
+ /**
1656
+ * Determine an appropriate URL for the response, by checking either
1657
+ * XMLHttpRequest.responseURL or the X-Request-URL header.
1658
+ */
1659
+ function getResponseUrl(xhr) {
1660
+ if ('responseURL' in xhr && xhr.responseURL) {
1661
+ return xhr.responseURL;
1662
+ }
1663
+ if (X_REQUEST_URL_REGEXP.test(xhr.getAllResponseHeaders())) {
1664
+ return xhr.getResponseHeader(X_REQUEST_URL_HEADER);
1665
+ }
1666
+ return null;
1667
+ }
1668
+ /**
1669
+ * Validates whether the request is compatible with the XHR backend.
1670
+ * Show a warning if the request contains options that are not supported by XHR.
1671
+ */
1672
+ function validateXhrCompatibility(req) {
1673
+ const unsupportedOptions = [
1674
+ {
1675
+ property: 'keepalive',
1676
+ errorCode: 2813 /* RuntimeErrorCode.KEEPALIVE_NOT_SUPPORTED_WITH_XHR */,
1677
+ },
1678
+ {
1679
+ property: 'cache',
1680
+ errorCode: 2814 /* RuntimeErrorCode.CACHE_NOT_SUPPORTED_WITH_XHR */,
1681
+ },
1682
+ {
1683
+ property: 'priority',
1684
+ errorCode: 2815 /* RuntimeErrorCode.PRIORITY_NOT_SUPPORTED_WITH_XHR */,
1685
+ },
1686
+ {
1687
+ property: 'mode',
1688
+ errorCode: 2816 /* RuntimeErrorCode.MODE_NOT_SUPPORTED_WITH_XHR */,
1689
+ },
1690
+ {
1691
+ property: 'redirect',
1692
+ errorCode: 2817 /* RuntimeErrorCode.REDIRECT_NOT_SUPPORTED_WITH_XHR */,
1693
+ },
1694
+ {
1695
+ property: 'credentials',
1696
+ errorCode: 2818 /* RuntimeErrorCode.CREDENTIALS_NOT_SUPPORTED_WITH_XHR */,
1697
+ },
1698
+ {
1699
+ property: 'integrity',
1700
+ errorCode: 2820 /* RuntimeErrorCode.INTEGRITY_NOT_SUPPORTED_WITH_XHR */,
1701
+ },
1702
+ {
1703
+ property: 'referrer',
1704
+ errorCode: 2821 /* RuntimeErrorCode.REFERRER_NOT_SUPPORTED_WITH_XHR */,
1705
+ },
1706
+ ];
1707
+ // Check each unsupported option and warn if present
1708
+ for (const { property, errorCode } of unsupportedOptions) {
1709
+ if (req[property]) {
1710
+ console.warn(_formatRuntimeError(errorCode, `Angular detected that a \`HttpClient\` request with the \`${property}\` option was sent using XHR, which does not support it. To use the \`${property}\` option, enable Fetch API support by passing \`withFetch()\` as an argument to \`provideHttpClient()\`.`));
1711
+ }
1712
+ }
1713
+ }
1714
+ /**
1715
+ * Uses `XMLHttpRequest` to send requests to a backend server.
1716
+ * @see {@link HttpHandler}
1717
+ * @see {@link JsonpClientBackend}
1718
+ *
1719
+ * @publicApi
1720
+ */
1721
+ class HttpXhrBackend {
1722
+ xhrFactory;
1723
+ constructor(xhrFactory) {
1724
+ this.xhrFactory = xhrFactory;
1725
+ }
1726
+ /**
1727
+ * Processes a request and returns a stream of response events.
1728
+ * @param req The request object.
1729
+ * @returns An observable of the response events.
1730
+ */
1731
+ handle(req) {
1732
+ // Quick check to give a better error message when a user attempts to use
1733
+ // HttpClient.jsonp() without installing the HttpClientJsonpModule
1734
+ if (req.method === 'JSONP') {
1735
+ throw new _RuntimeError(-2800 /* RuntimeErrorCode.MISSING_JSONP_MODULE */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
1736
+ `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.`);
1737
+ }
1738
+ // Validate that the request is compatible with the XHR backend.
1739
+ ngDevMode && validateXhrCompatibility(req);
1740
+ // Check whether this factory has a special function to load an XHR implementation
1741
+ // for various non-browser environments. We currently limit it to only `ServerXhr`
1742
+ // class, which needs to load an XHR implementation.
1743
+ const xhrFactory = this.xhrFactory;
1744
+ const source =
1745
+ // Note that `ɵloadImpl` is never defined in client bundles and can be
1746
+ // safely dropped whenever we're running in the browser.
1747
+ // This branching is redundant.
1748
+ // The `ngServerMode` guard also enables tree-shaking of the `from()`
1749
+ // function from the common bundle, as it's only used in server code.
1750
+ typeof ngServerMode !== 'undefined' && ngServerMode && xhrFactory.ɵloadImpl
1751
+ ? from(xhrFactory.ɵloadImpl())
1752
+ : of(null);
1753
+ return source.pipe(switchMap(() => {
1754
+ // Everything happens on Observable subscription.
1755
+ return new Observable((observer) => {
1756
+ // Start by setting up the XHR object with request method, URL, and withCredentials
1757
+ // flag.
1758
+ const xhr = xhrFactory.build();
1759
+ xhr.open(req.method, req.urlWithParams);
1760
+ if (req.withCredentials) {
1761
+ xhr.withCredentials = true;
1762
+ }
1763
+ // Add all the requested headers.
1764
+ req.headers.forEach((name, values) => xhr.setRequestHeader(name, values.join(',')));
1765
+ // Add an Accept header if one isn't present already.
1766
+ if (!req.headers.has(ACCEPT_HEADER)) {
1767
+ xhr.setRequestHeader(ACCEPT_HEADER, ACCEPT_HEADER_VALUE);
1768
+ }
1769
+ // Auto-detect the Content-Type header if one isn't present already.
1770
+ if (!req.headers.has(CONTENT_TYPE_HEADER)) {
1771
+ const detectedType = req.detectContentTypeHeader();
1772
+ // Sometimes Content-Type detection fails.
1773
+ if (detectedType !== null) {
1774
+ xhr.setRequestHeader(CONTENT_TYPE_HEADER, detectedType);
1775
+ }
1776
+ }
1777
+ if (req.timeout) {
1778
+ xhr.timeout = req.timeout;
1779
+ }
1780
+ // Set the responseType if one was requested.
1781
+ if (req.responseType) {
1782
+ const responseType = req.responseType.toLowerCase();
1783
+ // JSON responses need to be processed as text. This is because if the server
1784
+ // returns an XSSI-prefixed JSON response, the browser will fail to parse it,
1785
+ // xhr.response will be null, and xhr.responseText cannot be accessed to
1786
+ // retrieve the prefixed JSON data in order to strip the prefix. Thus, all JSON
1787
+ // is parsed by first requesting text and then applying JSON.parse.
1788
+ xhr.responseType = (responseType !== 'json' ? responseType : 'text');
1789
+ }
1790
+ // Serialize the request body if one is present. If not, this will be set to null.
1791
+ const reqBody = req.serializeBody();
1792
+ // If progress events are enabled, response headers will be delivered
1793
+ // in two events - the HttpHeaderResponse event and the full HttpResponse
1794
+ // event. However, since response headers don't change in between these
1795
+ // two events, it doesn't make sense to parse them twice. So headerResponse
1796
+ // caches the data extracted from the response whenever it's first parsed,
1797
+ // to ensure parsing isn't duplicated.
1798
+ let headerResponse = null;
1799
+ // partialFromXhr extracts the HttpHeaderResponse from the current XMLHttpRequest
1800
+ // state, and memoizes it into headerResponse.
1801
+ const partialFromXhr = () => {
1802
+ if (headerResponse !== null) {
1803
+ return headerResponse;
1804
+ }
1805
+ const statusText = xhr.statusText || 'OK';
1806
+ // Parse headers from XMLHttpRequest - this step is lazy.
1807
+ const headers = new HttpHeaders(xhr.getAllResponseHeaders());
1808
+ // Read the response URL from the XMLHttpResponse instance and fall back on the
1809
+ // request URL.
1810
+ const url = getResponseUrl(xhr) || req.url;
1811
+ // Construct the HttpHeaderResponse and memoize it.
1812
+ headerResponse = new HttpHeaderResponse({ headers, status: xhr.status, statusText, url });
1813
+ return headerResponse;
1814
+ };
1815
+ // Next, a few closures are defined for the various events which XMLHttpRequest can
1816
+ // emit. This allows them to be unregistered as event listeners later.
1817
+ // First up is the load event, which represents a response being fully available.
1818
+ const onLoad = () => {
1819
+ // Read response state from the memoized partial data.
1820
+ let { headers, status, statusText, url } = partialFromXhr();
1821
+ // The body will be read out if present.
1822
+ let body = null;
1823
+ if (status !== HTTP_STATUS_CODE_NO_CONTENT) {
1824
+ // Use XMLHttpRequest.response if set, responseText otherwise.
1825
+ body = typeof xhr.response === 'undefined' ? xhr.responseText : xhr.response;
1826
+ }
1827
+ // Normalize another potential bug (this one comes from CORS).
1828
+ if (status === 0) {
1829
+ status = !!body ? HTTP_STATUS_CODE_OK : 0;
1830
+ }
1831
+ // ok determines whether the response will be transmitted on the event or
1832
+ // error channel. Unsuccessful status codes (not 2xx) will always be errors,
1833
+ // but a successful status code can still result in an error if the user
1834
+ // asked for JSON data and the body cannot be parsed as such.
1835
+ let ok = status >= 200 && status < 300;
1836
+ // Check whether the body needs to be parsed as JSON (in many cases the browser
1837
+ // will have done that already).
1838
+ if (req.responseType === 'json' && typeof body === 'string') {
1839
+ // Save the original body, before attempting XSSI prefix stripping.
1840
+ const originalBody = body;
1841
+ body = body.replace(XSSI_PREFIX, '');
1842
+ try {
1843
+ // Attempt the parse. If it fails, a parse error should be delivered to the
1844
+ // user.
1845
+ body = body !== '' ? JSON.parse(body) : null;
1846
+ }
1847
+ catch (error) {
1848
+ // Since the JSON.parse failed, it's reasonable to assume this might not have
1849
+ // been a JSON response. Restore the original body (including any XSSI prefix)
1850
+ // to deliver a better error response.
1851
+ body = originalBody;
1852
+ // If this was an error request to begin with, leave it as a string, it
1853
+ // probably just isn't JSON. Otherwise, deliver the parsing error to the user.
1854
+ if (ok) {
1855
+ // Even though the response status was 2xx, this is still an error.
1856
+ ok = false;
1857
+ // The parse error contains the text of the body that failed to parse.
1858
+ body = { error, text: body };
1859
+ }
1860
+ }
1861
+ }
1862
+ if (ok) {
1863
+ // A successful response is delivered on the event stream.
1864
+ observer.next(new HttpResponse({
1865
+ body,
1866
+ headers,
1867
+ status,
1868
+ statusText,
1869
+ url: url || undefined,
1870
+ }));
1871
+ // The full body has been received and delivered, no further events
1872
+ // are possible. This request is complete.
1873
+ observer.complete();
1874
+ }
1875
+ else {
1876
+ // An unsuccessful request is delivered on the error channel.
1877
+ observer.error(new HttpErrorResponse({
1878
+ // The error in this case is the response body (error from the server).
1879
+ error: body,
1880
+ headers,
1881
+ status,
1882
+ statusText,
1883
+ url: url || undefined,
1884
+ }));
1885
+ }
1886
+ };
1887
+ // The onError callback is called when something goes wrong at the network level.
1888
+ // Connection timeout, DNS error, offline, etc. These are actual errors, and are
1889
+ // transmitted on the error channel.
1890
+ const onError = (error) => {
1891
+ const { url } = partialFromXhr();
1892
+ const res = new HttpErrorResponse({
1893
+ error,
1894
+ status: xhr.status || 0,
1895
+ statusText: xhr.statusText || 'Unknown Error',
1896
+ url: url || undefined,
1897
+ });
1898
+ observer.error(res);
1899
+ };
1900
+ let onTimeout = onError;
1901
+ if (req.timeout) {
1902
+ onTimeout = (_) => {
1903
+ const { url } = partialFromXhr();
1904
+ const res = new HttpErrorResponse({
1905
+ error: new DOMException('Request timed out', 'TimeoutError'),
1906
+ status: xhr.status || 0,
1907
+ statusText: xhr.statusText || 'Request timeout',
1908
+ url: url || undefined,
1909
+ });
1910
+ observer.error(res);
1911
+ };
1912
+ }
1913
+ // The sentHeaders flag tracks whether the HttpResponseHeaders event
1914
+ // has been sent on the stream. This is necessary to track if progress
1915
+ // is enabled since the event will be sent on only the first download
1916
+ // progress event.
1917
+ let sentHeaders = false;
1918
+ // The download progress event handler, which is only registered if
1919
+ // progress events are enabled.
1920
+ const onDownProgress = (event) => {
1921
+ // Send the HttpResponseHeaders event if it hasn't been sent already.
1922
+ if (!sentHeaders) {
1923
+ observer.next(partialFromXhr());
1924
+ sentHeaders = true;
1925
+ }
1926
+ // Start building the download progress event to deliver on the response
1927
+ // event stream.
1928
+ let progressEvent = {
1929
+ type: HttpEventType.DownloadProgress,
1930
+ loaded: event.loaded,
1931
+ };
1932
+ // Set the total number of bytes in the event if it's available.
1933
+ if (event.lengthComputable) {
1934
+ progressEvent.total = event.total;
1935
+ }
1936
+ // If the request was for text content and a partial response is
1937
+ // available on XMLHttpRequest, include it in the progress event
1938
+ // to allow for streaming reads.
1939
+ if (req.responseType === 'text' && !!xhr.responseText) {
1940
+ progressEvent.partialText = xhr.responseText;
1941
+ }
1942
+ // Finally, fire the event.
1943
+ observer.next(progressEvent);
1944
+ };
1945
+ // The upload progress event handler, which is only registered if
1946
+ // progress events are enabled.
1947
+ const onUpProgress = (event) => {
1948
+ // Upload progress events are simpler. Begin building the progress
1949
+ // event.
1950
+ let progress = {
1951
+ type: HttpEventType.UploadProgress,
1952
+ loaded: event.loaded,
1953
+ };
1954
+ // If the total number of bytes being uploaded is available, include
1955
+ // it.
1956
+ if (event.lengthComputable) {
1957
+ progress.total = event.total;
1958
+ }
1959
+ // Send the event.
1960
+ observer.next(progress);
1961
+ };
1962
+ // By default, register for load and error events.
1963
+ xhr.addEventListener('load', onLoad);
1964
+ xhr.addEventListener('error', onError);
1965
+ xhr.addEventListener('timeout', onTimeout);
1966
+ xhr.addEventListener('abort', onError);
1967
+ // Progress events are only enabled if requested.
1968
+ if (req.reportProgress) {
1969
+ // Download progress is always enabled if requested.
1970
+ xhr.addEventListener('progress', onDownProgress);
1971
+ // Upload progress depends on whether there is a body to upload.
1972
+ if (reqBody !== null && xhr.upload) {
1973
+ xhr.upload.addEventListener('progress', onUpProgress);
1974
+ }
1920
1975
  }
1921
- case 'text':
1922
- return new TextDecoder().decode(binContent);
1923
- case 'blob':
1924
- return new Blob([binContent], { type: contentType });
1925
- case 'arraybuffer':
1926
- return binContent.buffer;
1927
- }
1928
- }
1929
- createRequestInit(req) {
1930
- // We could share some of this logic with the XhrBackend
1931
- const headers = {};
1932
- let credentials;
1933
- // If the request has a credentials property, use it.
1934
- // Otherwise, if the request has withCredentials set to true, use 'include'.
1935
- credentials = req.credentials;
1936
- // If withCredentials is true should be set to 'include', for compatibility
1937
- if (req.withCredentials) {
1938
- // A warning is logged in development mode if the request has both
1939
- (typeof ngDevMode === 'undefined' || ngDevMode) && warningOptionsMessage(req);
1940
- credentials = 'include';
1941
- }
1942
- // Setting all the requested headers.
1943
- req.headers.forEach((name, values) => (headers[name] = values.join(',')));
1944
- // Add an Accept header if one isn't present already.
1945
- if (!req.headers.has(ACCEPT_HEADER)) {
1946
- headers[ACCEPT_HEADER] = ACCEPT_HEADER_VALUE;
1947
- }
1948
- // Auto-detect the Content-Type header if one isn't present already.
1949
- if (!req.headers.has(CONTENT_TYPE_HEADER)) {
1950
- const detectedType = req.detectContentTypeHeader();
1951
- // Sometimes Content-Type detection fails.
1952
- if (detectedType !== null) {
1953
- headers[CONTENT_TYPE_HEADER] = detectedType;
1954
- }
1955
- }
1956
- return {
1957
- body: req.serializeBody(),
1958
- method: req.method,
1959
- headers,
1960
- credentials,
1961
- keepalive: req.keepalive,
1962
- cache: req.cache,
1963
- priority: req.priority,
1964
- mode: req.mode,
1965
- redirect: req.redirect,
1966
- referrer: req.referrer,
1967
- integrity: req.integrity,
1968
- };
1969
- }
1970
- concatChunks(chunks, totalLength) {
1971
- const chunksAll = new Uint8Array(totalLength);
1972
- let position = 0;
1973
- for (const chunk of chunks) {
1974
- chunksAll.set(chunk, position);
1975
- position += chunk.length;
1976
- }
1977
- return chunksAll;
1978
- }
1979
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FetchBackend, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1980
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FetchBackend });
1981
- }
1982
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FetchBackend, decorators: [{
1983
- type: Injectable
1984
- }], ctorParameters: () => [] });
1985
- /**
1986
- * Abstract class to provide a mocked implementation of `fetch()`
1987
- */
1988
- class FetchFactory {
1989
- }
1990
- function noop() { }
1991
- function warningOptionsMessage(req) {
1992
- if (req.credentials && req.withCredentials) {
1993
- console.warn(_formatRuntimeError(2819 /* RuntimeErrorCode.WITH_CREDENTIALS_OVERRIDES_EXPLICIT_CREDENTIALS */, `Angular detected that a \`HttpClient\` request has both \`withCredentials: true\` and \`credentials: '${req.credentials}'\` options. The \`withCredentials\` option is overriding the explicit \`credentials\` setting to 'include'. Consider removing \`withCredentials\` and using \`credentials: '${req.credentials}'\` directly for clarity.`));
1976
+ // Fire the request, and notify the event stream that it was fired.
1977
+ xhr.send(reqBody);
1978
+ observer.next({ type: HttpEventType.Sent });
1979
+ // This is the return from the Observable function, which is the
1980
+ // request cancellation handler.
1981
+ return () => {
1982
+ // On a cancellation, remove all registered event listeners.
1983
+ xhr.removeEventListener('error', onError);
1984
+ xhr.removeEventListener('abort', onError);
1985
+ xhr.removeEventListener('load', onLoad);
1986
+ xhr.removeEventListener('timeout', onTimeout);
1987
+ if (req.reportProgress) {
1988
+ xhr.removeEventListener('progress', onDownProgress);
1989
+ if (reqBody !== null && xhr.upload) {
1990
+ xhr.upload.removeEventListener('progress', onUpProgress);
1991
+ }
1992
+ }
1993
+ // Finally, abort the in-flight request.
1994
+ if (xhr.readyState !== xhr.DONE) {
1995
+ xhr.abort();
1996
+ }
1997
+ };
1998
+ });
1999
+ }));
1994
2000
  }
2001
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXhrBackend, deps: [{ token: XhrFactory }], target: i0.ɵɵFactoryTarget.Injectable });
2002
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXhrBackend, providedIn: 'root' });
1995
2003
  }
1996
- /**
1997
- * Zone.js treats a rejected promise that has not yet been awaited
1998
- * as an unhandled error. This function adds a noop `.then` to make
1999
- * sure that Zone.js doesn't throw an error if the Promise is rejected
2000
- * synchronously.
2001
- */
2002
- function silenceSuperfluousUnhandledPromiseRejection(promise) {
2003
- promise.then(noop, noop);
2004
- }
2004
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXhrBackend, decorators: [{
2005
+ type: Injectable,
2006
+ args: [{ providedIn: 'root' }]
2007
+ }], ctorParameters: () => [{ type: XhrFactory }] });
2005
2008
 
2006
2009
  function interceptorChainEndFn(req, finalHandlerFn) {
2007
2010
  return finalHandlerFn(req);
@@ -2032,7 +2035,7 @@ const HTTP_INTERCEPTORS = new InjectionToken(ngDevMode ? 'HTTP_INTERCEPTORS' : '
2032
2035
  /**
2033
2036
  * A multi-provided token of `HttpInterceptorFn`s.
2034
2037
  */
2035
- const HTTP_INTERCEPTOR_FNS = new InjectionToken(ngDevMode ? 'HTTP_INTERCEPTOR_FNS' : '');
2038
+ const HTTP_INTERCEPTOR_FNS = new InjectionToken(ngDevMode ? 'HTTP_INTERCEPTOR_FNS' : '', { factory: () => [] });
2036
2039
  /**
2037
2040
  * A multi-provided token of `HttpInterceptorFn`s that are only set in root.
2038
2041
  */
@@ -2067,15 +2070,33 @@ function legacyInterceptorFnFactory() {
2067
2070
  }
2068
2071
  };
2069
2072
  }
2073
+
2074
+ /**
2075
+ * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend.
2076
+ *
2077
+ * Interceptors sit between the `HttpClient` interface and the `HttpBackend`.
2078
+ *
2079
+ * When injected, `HttpBackend` dispatches requests directly to the backend, without going
2080
+ * through the interceptor chain.
2081
+ *
2082
+ * @publicApi
2083
+ */
2084
+ class HttpBackend {
2085
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpBackend, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2086
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpBackend, providedIn: 'root', useExisting: HttpXhrBackend });
2087
+ }
2088
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpBackend, decorators: [{
2089
+ type: Injectable,
2090
+ args: [{ providedIn: 'root', useExisting: HttpXhrBackend }]
2091
+ }] });
2070
2092
  let fetchBackendWarningDisplayed = false;
2071
- class HttpInterceptorHandler extends HttpHandler {
2093
+ class HttpInterceptorHandler {
2072
2094
  backend;
2073
2095
  injector;
2074
2096
  chain = null;
2075
2097
  pendingTasks = inject(PendingTasks);
2076
2098
  contributeToStability = inject(REQUESTS_CONTRIBUTE_TO_STABILITY);
2077
2099
  constructor(backend, injector) {
2078
- super();
2079
2100
  this.backend = backend;
2080
2101
  this.injector = injector;
2081
2102
  // We strongly recommend using fetch backend for HTTP calls when SSR is used
@@ -2124,615 +2145,610 @@ class HttpInterceptorHandler extends HttpHandler {
2124
2145
  }
2125
2146
  }
2126
2147
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpInterceptorHandler, deps: [{ token: HttpBackend }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
2127
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpInterceptorHandler });
2148
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpInterceptorHandler, providedIn: 'root' });
2128
2149
  }
2129
2150
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpInterceptorHandler, decorators: [{
2130
- type: Injectable
2151
+ type: Injectable,
2152
+ args: [{ providedIn: 'root' }]
2131
2153
  }], ctorParameters: () => [{ type: HttpBackend }, { type: i0.EnvironmentInjector }] });
2154
+ /**
2155
+ * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a
2156
+ * `HttpResponse`.
2157
+ *
2158
+ * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the
2159
+ * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the
2160
+ * `HttpBackend`.
2161
+ *
2162
+ * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain.
2163
+ *
2164
+ * @publicApi
2165
+ */
2166
+ class HttpHandler {
2167
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2168
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpHandler, providedIn: 'root', useExisting: HttpInterceptorHandler });
2169
+ }
2170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpHandler, decorators: [{
2171
+ type: Injectable,
2172
+ args: [{ providedIn: 'root', useExisting: HttpInterceptorHandler }]
2173
+ }] });
2132
2174
 
2133
- // Every request made through JSONP needs a callback name that's unique across the
2134
- // whole page. Each request is assigned an id and the callback name is constructed
2135
- // from that. The next id to be assigned is tracked in a global variable here that
2136
- // is shared among all applications on the page.
2137
- let nextRequestId = 0;
2138
2175
  /**
2139
- * When a pending <script> is unsubscribed we'll move it to this document, so it won't be
2140
- * executed.
2176
+ * Constructs an instance of `HttpRequestOptions<T>` from a source `HttpMethodOptions` and
2177
+ * the given `body`. This function clones the object and adds the body.
2178
+ *
2179
+ * Note that the `responseType` *options* value is a String that identifies the
2180
+ * single data type of the response.
2181
+ * A single overload version of the method handles each response type.
2182
+ * The value of `responseType` cannot be a union, as the combined signature could imply.
2183
+ *
2141
2184
  */
2142
- let foreignDocument;
2143
- // Error text given when a JSONP script is injected, but doesn't invoke the callback
2144
- // passed in its URL.
2145
- const JSONP_ERR_NO_CALLBACK = 'JSONP injected script did not invoke callback.';
2146
- // Error text given when a request is passed to the JsonpClientBackend that doesn't
2147
- // have a request method JSONP.
2148
- const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use JSONP request method.';
2149
- const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json response type.';
2150
- // Error text given when a request is passed to the JsonpClientBackend that has
2151
- // headers set
2152
- const JSONP_ERR_HEADERS_NOT_SUPPORTED = 'JSONP requests do not support headers.';
2185
+ function addBody(options, body) {
2186
+ return {
2187
+ body,
2188
+ headers: options.headers,
2189
+ context: options.context,
2190
+ observe: options.observe,
2191
+ params: options.params,
2192
+ reportProgress: options.reportProgress,
2193
+ responseType: options.responseType,
2194
+ withCredentials: options.withCredentials,
2195
+ credentials: options.credentials,
2196
+ transferCache: options.transferCache,
2197
+ timeout: options.timeout,
2198
+ keepalive: options.keepalive,
2199
+ priority: options.priority,
2200
+ cache: options.cache,
2201
+ mode: options.mode,
2202
+ redirect: options.redirect,
2203
+ integrity: options.integrity,
2204
+ referrer: options.referrer,
2205
+ };
2206
+ }
2153
2207
  /**
2154
- * DI token/abstract type representing a map of JSONP callbacks.
2208
+ * Performs HTTP requests.
2209
+ * This service is available as an injectable class, with methods to perform HTTP requests.
2210
+ * Each request method has multiple signatures, and the return type varies based on
2211
+ * the signature that is called (mainly the values of `observe` and `responseType`).
2212
+ *
2213
+ * Note that the `responseType` *options* value is a String that identifies the
2214
+ * single data type of the response.
2215
+ * A single overload version of the method handles each response type.
2216
+ * The value of `responseType` cannot be a union, as the combined signature could imply.
2217
+ *
2218
+ * @usageNotes
2155
2219
  *
2156
- * In the browser, this should always be the `window` object.
2220
+ * ### HTTP Request Example
2157
2221
  *
2222
+ * ```ts
2223
+ * // GET heroes whose name contains search term
2224
+ * searchHeroes(term: string): observable<Hero[]>{
2158
2225
  *
2159
- */
2160
- class JsonpCallbackContext {
2161
- }
2162
- /**
2163
- * Factory function that determines where to store JSONP callbacks.
2226
+ * const params = new HttpParams({fromString: 'name=term'});
2227
+ * return this.httpClient.request('GET', this.heroesUrl, {responseType:'json', params});
2228
+ * }
2229
+ * ```
2164
2230
  *
2165
- * Ordinarily JSONP callbacks are stored on the `window` object, but this may not exist
2166
- * in test environments. In that case, callbacks are stored on an anonymous object instead.
2231
+ * Alternatively, the parameter string can be used without invoking HttpParams
2232
+ * by directly joining to the URL.
2233
+ * ```ts
2234
+ * this.httpClient.request('GET', this.heroesUrl + '?' + 'name=term', {responseType:'json'});
2235
+ * ```
2167
2236
  *
2168
2237
  *
2169
- */
2170
- function jsonpCallbackContext() {
2171
- if (typeof window === 'object') {
2172
- return window;
2173
- }
2174
- return {};
2175
- }
2176
- /**
2177
- * Processes an `HttpRequest` with the JSONP method,
2178
- * by performing JSONP style requests.
2179
- * @see {@link HttpHandler}
2180
- * @see {@link HttpXhrBackend}
2238
+ * ### JSONP Example
2239
+ * ```ts
2240
+ * requestJsonp(url, callback = 'callback') {
2241
+ * return this.httpClient.jsonp(this.heroesURL, callback);
2242
+ * }
2243
+ * ```
2244
+ *
2245
+ * ### PATCH Example
2246
+ * ```ts
2247
+ * // PATCH one of the heroes' name
2248
+ * patchHero (id: number, heroName: string): Observable<{}> {
2249
+ * const url = `${this.heroesUrl}/${id}`; // PATCH api/heroes/42
2250
+ * return this.httpClient.patch(url, {name: heroName}, httpOptions)
2251
+ * .pipe(catchError(this.handleError('patchHero')));
2252
+ * }
2253
+ * ```
2254
+ *
2255
+ * @see [HTTP Guide](guide/http)
2256
+ * @see [HTTP Request](api/common/http/HttpRequest)
2181
2257
  *
2182
2258
  * @publicApi
2183
2259
  */
2184
- class JsonpClientBackend {
2185
- callbackMap;
2186
- document;
2187
- /**
2188
- * A resolved promise that can be used to schedule microtasks in the event handlers.
2189
- */
2190
- resolvedPromise = Promise.resolve();
2191
- constructor(callbackMap, document) {
2192
- this.callbackMap = callbackMap;
2193
- this.document = document;
2194
- }
2195
- /**
2196
- * Get the name of the next callback method, by incrementing the global `nextRequestId`.
2197
- */
2198
- nextCallback() {
2199
- return `ng_jsonp_callback_${nextRequestId++}`;
2260
+ class HttpClient {
2261
+ handler;
2262
+ constructor(handler) {
2263
+ this.handler = handler;
2200
2264
  }
2201
2265
  /**
2202
- * Processes a JSONP request and returns an event stream of the results.
2203
- * @param req The request object.
2204
- * @returns An observable of the response events.
2266
+ * Constructs an observable for a generic HTTP request that, when subscribed,
2267
+ * fires the request through the chain of registered interceptors and on to the
2268
+ * server.
2269
+ *
2270
+ * You can pass an `HttpRequest` directly as the only parameter. In this case,
2271
+ * the call returns an observable of the raw `HttpEvent` stream.
2272
+ *
2273
+ * Alternatively you can pass an HTTP method as the first parameter,
2274
+ * a URL string as the second, and an options hash containing the request body as the third.
2275
+ * See `addBody()`. In this case, the specified `responseType` and `observe` options determine the
2276
+ * type of returned observable.
2277
+ * * The `responseType` value determines how a successful response body is parsed.
2278
+ * * If `responseType` is the default `json`, you can pass a type interface for the resulting
2279
+ * object as a type parameter to the call.
2280
+ *
2281
+ * The `observe` value determines the return type, according to what you are interested in
2282
+ * observing.
2283
+ * * An `observe` value of events returns an observable of the raw `HttpEvent` stream, including
2284
+ * progress events by default.
2285
+ * * An `observe` value of response returns an observable of `HttpResponse<T>`,
2286
+ * where the `T` parameter depends on the `responseType` and any optionally provided type
2287
+ * parameter.
2288
+ * * An `observe` value of body returns an observable of `<T>` with the same `T` body type.
2205
2289
  *
2206
2290
  */
2207
- handle(req) {
2208
- // Firstly, check both the method and response type. If either doesn't match
2209
- // then the request was improperly routed here and cannot be handled.
2210
- if (req.method !== 'JSONP') {
2211
- throw new _RuntimeError(2810 /* RuntimeErrorCode.JSONP_WRONG_METHOD */, ngDevMode && JSONP_ERR_WRONG_METHOD);
2291
+ request(first, url, options = {}) {
2292
+ let req;
2293
+ // First, check whether the primary argument is an instance of `HttpRequest`.
2294
+ if (first instanceof HttpRequest) {
2295
+ // It is. The other arguments must be undefined (per the signatures) and can be
2296
+ // ignored.
2297
+ req = first;
2212
2298
  }
2213
- else if (req.responseType !== 'json') {
2214
- throw new _RuntimeError(2811 /* RuntimeErrorCode.JSONP_WRONG_RESPONSE_TYPE */, ngDevMode && JSONP_ERR_WRONG_RESPONSE_TYPE);
2299
+ else {
2300
+ // It's a string, so it represents a URL. Construct a request based on it,
2301
+ // and incorporate the remaining arguments (assuming `GET` unless a method is
2302
+ // provided.
2303
+ // Figure out the headers.
2304
+ let headers = undefined;
2305
+ if (options.headers instanceof HttpHeaders) {
2306
+ headers = options.headers;
2307
+ }
2308
+ else {
2309
+ headers = new HttpHeaders(options.headers);
2310
+ }
2311
+ // Sort out parameters.
2312
+ let params = undefined;
2313
+ if (!!options.params) {
2314
+ if (options.params instanceof HttpParams) {
2315
+ params = options.params;
2316
+ }
2317
+ else {
2318
+ params = new HttpParams({ fromObject: options.params });
2319
+ }
2320
+ }
2321
+ // Construct the request.
2322
+ req = new HttpRequest(first, url, options.body !== undefined ? options.body : null, {
2323
+ headers,
2324
+ context: options.context,
2325
+ params,
2326
+ reportProgress: options.reportProgress,
2327
+ // By default, JSON is assumed to be returned for all calls.
2328
+ responseType: options.responseType || 'json',
2329
+ withCredentials: options.withCredentials,
2330
+ transferCache: options.transferCache,
2331
+ keepalive: options.keepalive,
2332
+ priority: options.priority,
2333
+ cache: options.cache,
2334
+ mode: options.mode,
2335
+ redirect: options.redirect,
2336
+ credentials: options.credentials,
2337
+ referrer: options.referrer,
2338
+ integrity: options.integrity,
2339
+ timeout: options.timeout,
2340
+ });
2215
2341
  }
2216
- // Check the request headers. JSONP doesn't support headers and
2217
- // cannot set any that were supplied.
2218
- if (req.headers.keys().length > 0) {
2219
- throw new _RuntimeError(2812 /* RuntimeErrorCode.JSONP_HEADERS_NOT_SUPPORTED */, ngDevMode && JSONP_ERR_HEADERS_NOT_SUPPORTED);
2342
+ // Start with an Observable.of() the initial request, and run the handler (which
2343
+ // includes all interceptors) inside a concatMap(). This way, the handler runs
2344
+ // inside an Observable chain, which causes interceptors to be re-run on every
2345
+ // subscription (this also makes retries re-run the handler, including interceptors).
2346
+ const events$ = of(req).pipe(concatMap((req) => this.handler.handle(req)));
2347
+ // If coming via the API signature which accepts a previously constructed HttpRequest,
2348
+ // the only option is to get the event stream. Otherwise, return the event stream if
2349
+ // that is what was requested.
2350
+ if (first instanceof HttpRequest || options.observe === 'events') {
2351
+ return events$;
2220
2352
  }
2221
- // Everything else happens inside the Observable boundary.
2222
- return new Observable((observer) => {
2223
- // The first step to make a request is to generate the callback name, and replace the
2224
- // callback placeholder in the URL with the name. Care has to be taken here to ensure
2225
- // a trailing &, if matched, gets inserted back into the URL in the correct place.
2226
- const callback = this.nextCallback();
2227
- const url = req.urlWithParams.replace(/=JSONP_CALLBACK(&|$)/, `=${callback}$1`);
2228
- // Construct the <script> tag and point it at the URL.
2229
- const node = this.document.createElement('script');
2230
- node.src = url;
2231
- // A JSONP request requires waiting for multiple callbacks. These variables
2232
- // are closed over and track state across those callbacks.
2233
- // The response object, if one has been received, or null otherwise.
2234
- let body = null;
2235
- // Whether the response callback has been called.
2236
- let finished = false;
2237
- // Set the response callback in this.callbackMap (which will be the window
2238
- // object in the browser. The script being loaded via the <script> tag will
2239
- // eventually call this callback.
2240
- this.callbackMap[callback] = (data) => {
2241
- // Data has been received from the JSONP script. Firstly, delete this callback.
2242
- delete this.callbackMap[callback];
2243
- // Set state to indicate data was received.
2244
- body = data;
2245
- finished = true;
2246
- };
2247
- // cleanup() is a utility closure that removes the <script> from the page and
2248
- // the response callback from the window. This logic is used in both the
2249
- // success, error, and cancellation paths, so it's extracted out for convenience.
2250
- const cleanup = () => {
2251
- node.removeEventListener('load', onLoad);
2252
- node.removeEventListener('error', onError);
2253
- // Remove the <script> tag if it's still on the page.
2254
- node.remove();
2255
- // Remove the response callback from the callbackMap (window object in the
2256
- // browser).
2257
- delete this.callbackMap[callback];
2258
- };
2259
- // onLoad() is the success callback which runs after the response callback
2260
- // if the JSONP script loads successfully. The event itself is unimportant.
2261
- // If something went wrong, onLoad() may run without the response callback
2262
- // having been invoked.
2263
- const onLoad = () => {
2264
- // We wrap it in an extra Promise, to ensure the microtask
2265
- // is scheduled after the loaded endpoint has executed any potential microtask itself,
2266
- // which is not guaranteed in Internet Explorer and EdgeHTML. See issue #39496
2267
- this.resolvedPromise.then(() => {
2268
- // Cleanup the page.
2269
- cleanup();
2270
- // Check whether the response callback has run.
2271
- if (!finished) {
2272
- // It hasn't, something went wrong with the request. Return an error via
2273
- // the Observable error path. All JSONP errors have status 0.
2274
- observer.error(new HttpErrorResponse({
2275
- url,
2276
- status: 0,
2277
- statusText: 'JSONP Error',
2278
- error: new Error(JSONP_ERR_NO_CALLBACK),
2353
+ // The requested stream contains either the full response or the body. In either
2354
+ // case, the first step is to filter the event stream to extract a stream of
2355
+ // responses(s).
2356
+ const res$ = (events$.pipe(filter((event) => event instanceof HttpResponse)));
2357
+ // Decide which stream to return.
2358
+ switch (options.observe || 'body') {
2359
+ case 'body':
2360
+ // The requested stream is the body. Map the response stream to the response
2361
+ // body. This could be done more simply, but a misbehaving interceptor might
2362
+ // transform the response body into a different format and ignore the requested
2363
+ // responseType. Guard against this by validating that the response is of the
2364
+ // requested type.
2365
+ switch (req.responseType) {
2366
+ case 'arraybuffer':
2367
+ return res$.pipe(map((res) => {
2368
+ // Validate that the body is an ArrayBuffer.
2369
+ if (res.body !== null && !(res.body instanceof ArrayBuffer)) {
2370
+ throw new _RuntimeError(2806 /* RuntimeErrorCode.RESPONSE_IS_NOT_AN_ARRAY_BUFFER */, ngDevMode && 'Response is not an ArrayBuffer.');
2371
+ }
2372
+ return res.body;
2373
+ }));
2374
+ case 'blob':
2375
+ return res$.pipe(map((res) => {
2376
+ // Validate that the body is a Blob.
2377
+ if (res.body !== null && !(res.body instanceof Blob)) {
2378
+ throw new _RuntimeError(2807 /* RuntimeErrorCode.RESPONSE_IS_NOT_A_BLOB */, ngDevMode && 'Response is not a Blob.');
2379
+ }
2380
+ return res.body;
2381
+ }));
2382
+ case 'text':
2383
+ return res$.pipe(map((res) => {
2384
+ // Validate that the body is a string.
2385
+ if (res.body !== null && typeof res.body !== 'string') {
2386
+ throw new _RuntimeError(2808 /* RuntimeErrorCode.RESPONSE_IS_NOT_A_STRING */, ngDevMode && 'Response is not a string.');
2387
+ }
2388
+ return res.body;
2279
2389
  }));
2280
- return;
2281
- }
2282
- // Success. body either contains the response body or null if none was
2283
- // returned.
2284
- observer.next(new HttpResponse({
2285
- body,
2286
- status: HTTP_STATUS_CODE_OK,
2287
- statusText: 'OK',
2288
- url,
2289
- }));
2290
- // Complete the stream, the response is over.
2291
- observer.complete();
2292
- });
2293
- };
2294
- // onError() is the error callback, which runs if the script returned generates
2295
- // a Javascript error. It emits the error via the Observable error channel as
2296
- // a HttpErrorResponse.
2297
- const onError = (error) => {
2298
- cleanup();
2299
- // Wrap the error in a HttpErrorResponse.
2300
- observer.error(new HttpErrorResponse({
2301
- error,
2302
- status: 0,
2303
- statusText: 'JSONP Error',
2304
- url,
2305
- }));
2306
- };
2307
- // Subscribe to both the success (load) and error events on the <script> tag,
2308
- // and add it to the page.
2309
- node.addEventListener('load', onLoad);
2310
- node.addEventListener('error', onError);
2311
- this.document.body.appendChild(node);
2312
- // The request has now been successfully sent.
2313
- observer.next({ type: HttpEventType.Sent });
2314
- // Cancellation handler.
2315
- return () => {
2316
- if (!finished) {
2317
- this.removeListeners(node);
2390
+ case 'json':
2391
+ default:
2392
+ // No validation needed for JSON responses, as they can be of any type.
2393
+ return res$.pipe(map((res) => res.body));
2318
2394
  }
2319
- // And finally, clean up the page.
2320
- cleanup();
2321
- };
2322
- });
2395
+ case 'response':
2396
+ // The response stream was requested directly, so return it.
2397
+ return res$;
2398
+ default:
2399
+ // Guard against new future observe types being added.
2400
+ throw new _RuntimeError(2809 /* RuntimeErrorCode.UNHANDLED_OBSERVE_TYPE */, ngDevMode && `Unreachable: unhandled observe type ${options.observe}}`);
2401
+ }
2323
2402
  }
2324
- removeListeners(script) {
2325
- // Issue #34818
2326
- // Changing <script>'s ownerDocument will prevent it from execution.
2327
- // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
2328
- foreignDocument ??= this.document.implementation.createHTMLDocument();
2329
- foreignDocument.adoptNode(script);
2403
+ /**
2404
+ * Constructs an observable that, when subscribed, causes the configured
2405
+ * `DELETE` request to execute on the server. See the individual overloads for
2406
+ * details on the return type.
2407
+ *
2408
+ * @param url The endpoint URL.
2409
+ * @param options The HTTP options to send with the request.
2410
+ *
2411
+ */
2412
+ delete(url, options = {}) {
2413
+ return this.request('DELETE', url, options);
2330
2414
  }
2331
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpClientBackend, deps: [{ token: JsonpCallbackContext }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
2332
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpClientBackend });
2333
- }
2334
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpClientBackend, decorators: [{
2335
- type: Injectable
2336
- }], ctorParameters: () => [{ type: JsonpCallbackContext }, { type: undefined, decorators: [{
2337
- type: Inject,
2338
- args: [DOCUMENT]
2339
- }] }] });
2340
- /**
2341
- * Identifies requests with the method JSONP and shifts them to the `JsonpClientBackend`.
2342
- */
2343
- function jsonpInterceptorFn(req, next) {
2344
- if (req.method === 'JSONP') {
2345
- return inject(JsonpClientBackend).handle(req);
2415
+ /**
2416
+ * Constructs an observable that, when subscribed, causes the configured
2417
+ * `GET` request to execute on the server. See the individual overloads for
2418
+ * details on the return type.
2419
+ */
2420
+ get(url, options = {}) {
2421
+ return this.request('GET', url, options);
2346
2422
  }
2347
- // Fall through for normal HTTP requests.
2348
- return next(req);
2349
- }
2350
- /**
2351
- * Identifies requests with the method JSONP and
2352
- * shifts them to the `JsonpClientBackend`.
2353
- *
2354
- * @see {@link HttpInterceptor}
2355
- *
2356
- * @publicApi
2357
- */
2358
- class JsonpInterceptor {
2359
- injector;
2360
- constructor(injector) {
2361
- this.injector = injector;
2423
+ /**
2424
+ * Constructs an observable that, when subscribed, causes the configured
2425
+ * `HEAD` request to execute on the server. The `HEAD` method returns
2426
+ * meta information about the resource without transferring the
2427
+ * resource itself. See the individual overloads for
2428
+ * details on the return type.
2429
+ */
2430
+ head(url, options = {}) {
2431
+ return this.request('HEAD', url, options);
2362
2432
  }
2363
2433
  /**
2364
- * Identifies and handles a given JSONP request.
2365
- * @param initialRequest The outgoing request object to handle.
2366
- * @param next The next interceptor in the chain, or the backend
2367
- * if no interceptors remain in the chain.
2368
- * @returns An observable of the event stream.
2434
+ * Constructs an `Observable` that, when subscribed, causes a request with the special method
2435
+ * `JSONP` to be dispatched via the interceptor pipeline.
2436
+ * The [JSONP pattern](https://en.wikipedia.org/wiki/JSONP) works around limitations of certain
2437
+ * API endpoints that don't support newer,
2438
+ * and preferable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) protocol.
2439
+ * JSONP treats the endpoint API as a JavaScript file and tricks the browser to process the
2440
+ * requests even if the API endpoint is not located on the same domain (origin) as the client-side
2441
+ * application making the request.
2442
+ * The endpoint API must support JSONP callback for JSONP requests to work.
2443
+ * The resource API returns the JSON response wrapped in a callback function.
2444
+ * You can pass the callback function name as one of the query parameters.
2445
+ * Note that JSONP requests can only be used with `GET` requests.
2446
+ *
2447
+ * @param url The resource URL.
2448
+ * @param callbackParam The callback function name.
2449
+ *
2369
2450
  */
2370
- intercept(initialRequest, next) {
2371
- return runInInjectionContext(this.injector, () => jsonpInterceptorFn(initialRequest, (downstreamRequest) => next.handle(downstreamRequest)));
2451
+ jsonp(url, callbackParam) {
2452
+ return this.request('JSONP', url, {
2453
+ params: new HttpParams().append(callbackParam, 'JSONP_CALLBACK'),
2454
+ observe: 'body',
2455
+ responseType: 'json',
2456
+ });
2372
2457
  }
2373
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpInterceptor, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
2374
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpInterceptor });
2375
- }
2376
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpInterceptor, decorators: [{
2377
- type: Injectable
2378
- }], ctorParameters: () => [{ type: i0.EnvironmentInjector }] });
2379
-
2380
- const XSSI_PREFIX = /^\)\]\}',?\n/;
2381
- const X_REQUEST_URL_REGEXP = RegExp(`^${X_REQUEST_URL_HEADER}:`, 'm');
2382
- /**
2383
- * Determine an appropriate URL for the response, by checking either
2384
- * XMLHttpRequest.responseURL or the X-Request-URL header.
2385
- */
2386
- function getResponseUrl(xhr) {
2387
- if ('responseURL' in xhr && xhr.responseURL) {
2388
- return xhr.responseURL;
2458
+ /**
2459
+ * Constructs an `Observable` that, when subscribed, causes the configured
2460
+ * `OPTIONS` request to execute on the server. This method allows the client
2461
+ * to determine the supported HTTP methods and other capabilities of an endpoint,
2462
+ * without implying a resource action. See the individual overloads for
2463
+ * details on the return type.
2464
+ */
2465
+ options(url, options = {}) {
2466
+ return this.request('OPTIONS', url, options);
2389
2467
  }
2390
- if (X_REQUEST_URL_REGEXP.test(xhr.getAllResponseHeaders())) {
2391
- return xhr.getResponseHeader(X_REQUEST_URL_HEADER);
2468
+ /**
2469
+ * Constructs an observable that, when subscribed, causes the configured
2470
+ * `PATCH` request to execute on the server. See the individual overloads for
2471
+ * details on the return type.
2472
+ */
2473
+ patch(url, body, options = {}) {
2474
+ return this.request('PATCH', url, addBody(options, body));
2392
2475
  }
2393
- return null;
2476
+ /**
2477
+ * Constructs an observable that, when subscribed, causes the configured
2478
+ * `POST` request to execute on the server. The server responds with the location of
2479
+ * the replaced resource. See the individual overloads for
2480
+ * details on the return type.
2481
+ */
2482
+ post(url, body, options = {}) {
2483
+ return this.request('POST', url, addBody(options, body));
2484
+ }
2485
+ /**
2486
+ * Constructs an observable that, when subscribed, causes the configured
2487
+ * `PUT` request to execute on the server. The `PUT` method replaces an existing resource
2488
+ * with a new set of values.
2489
+ * See the individual overloads for details on the return type.
2490
+ */
2491
+ put(url, body, options = {}) {
2492
+ return this.request('PUT', url, addBody(options, body));
2493
+ }
2494
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpClient, deps: [{ token: HttpHandler }], target: i0.ɵɵFactoryTarget.Injectable });
2495
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpClient, providedIn: 'root' });
2394
2496
  }
2395
- /**
2396
- * Validates whether the request is compatible with the XHR backend.
2397
- * Show a warning if the request contains options that are not supported by XHR.
2398
- */
2399
- function validateXhrCompatibility(req) {
2400
- const unsupportedOptions = [
2401
- {
2402
- property: 'keepalive',
2403
- errorCode: 2813 /* RuntimeErrorCode.KEEPALIVE_NOT_SUPPORTED_WITH_XHR */,
2404
- },
2405
- {
2406
- property: 'cache',
2407
- errorCode: 2814 /* RuntimeErrorCode.CACHE_NOT_SUPPORTED_WITH_XHR */,
2408
- },
2409
- {
2410
- property: 'priority',
2411
- errorCode: 2815 /* RuntimeErrorCode.PRIORITY_NOT_SUPPORTED_WITH_XHR */,
2412
- },
2413
- {
2414
- property: 'mode',
2415
- errorCode: 2816 /* RuntimeErrorCode.MODE_NOT_SUPPORTED_WITH_XHR */,
2416
- },
2417
- {
2418
- property: 'redirect',
2419
- errorCode: 2817 /* RuntimeErrorCode.REDIRECT_NOT_SUPPORTED_WITH_XHR */,
2420
- },
2421
- {
2422
- property: 'credentials',
2423
- errorCode: 2818 /* RuntimeErrorCode.CREDENTIALS_NOT_SUPPORTED_WITH_XHR */,
2424
- },
2425
- {
2426
- property: 'integrity',
2427
- errorCode: 2820 /* RuntimeErrorCode.INTEGRITY_NOT_SUPPORTED_WITH_XHR */,
2428
- },
2429
- {
2430
- property: 'referrer',
2431
- errorCode: 2821 /* RuntimeErrorCode.REFERRER_NOT_SUPPORTED_WITH_XHR */,
2432
- },
2433
- ];
2434
- // Check each unsupported option and warn if present
2435
- for (const { property, errorCode } of unsupportedOptions) {
2436
- if (req[property]) {
2437
- console.warn(_formatRuntimeError(errorCode, `Angular detected that a \`HttpClient\` request with the \`${property}\` option was sent using XHR, which does not support it. To use the \`${property}\` option, enable Fetch API support by passing \`withFetch()\` as an argument to \`provideHttpClient()\`.`));
2438
- }
2497
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpClient, decorators: [{
2498
+ type: Injectable,
2499
+ args: [{ providedIn: 'root' }]
2500
+ }], ctorParameters: () => [{ type: HttpHandler }] });
2501
+
2502
+ // Every request made through JSONP needs a callback name that's unique across the
2503
+ // whole page. Each request is assigned an id and the callback name is constructed
2504
+ // from that. The next id to be assigned is tracked in a global variable here that
2505
+ // is shared among all applications on the page.
2506
+ let nextRequestId = 0;
2507
+ /**
2508
+ * When a pending <script> is unsubscribed we'll move it to this document, so it won't be
2509
+ * executed.
2510
+ */
2511
+ let foreignDocument;
2512
+ // Error text given when a JSONP script is injected, but doesn't invoke the callback
2513
+ // passed in its URL.
2514
+ const JSONP_ERR_NO_CALLBACK = 'JSONP injected script did not invoke callback.';
2515
+ // Error text given when a request is passed to the JsonpClientBackend that doesn't
2516
+ // have a request method JSONP.
2517
+ const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use JSONP request method.';
2518
+ const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json response type.';
2519
+ // Error text given when a request is passed to the JsonpClientBackend that has
2520
+ // headers set
2521
+ const JSONP_ERR_HEADERS_NOT_SUPPORTED = 'JSONP requests do not support headers.';
2522
+ /**
2523
+ * DI token/abstract type representing a map of JSONP callbacks.
2524
+ *
2525
+ * In the browser, this should always be the `window` object.
2526
+ *
2527
+ *
2528
+ */
2529
+ class JsonpCallbackContext {
2530
+ }
2531
+ /**
2532
+ * Factory function that determines where to store JSONP callbacks.
2533
+ *
2534
+ * Ordinarily JSONP callbacks are stored on the `window` object, but this may not exist
2535
+ * in test environments. In that case, callbacks are stored on an anonymous object instead.
2536
+ *
2537
+ *
2538
+ */
2539
+ function jsonpCallbackContext() {
2540
+ if (typeof window === 'object') {
2541
+ return window;
2439
2542
  }
2543
+ return {};
2440
2544
  }
2441
2545
  /**
2442
- * Uses `XMLHttpRequest` to send requests to a backend server.
2546
+ * Processes an `HttpRequest` with the JSONP method,
2547
+ * by performing JSONP style requests.
2443
2548
  * @see {@link HttpHandler}
2444
- * @see {@link JsonpClientBackend}
2549
+ * @see {@link HttpXhrBackend}
2445
2550
  *
2446
2551
  * @publicApi
2447
2552
  */
2448
- class HttpXhrBackend {
2449
- xhrFactory;
2450
- constructor(xhrFactory) {
2451
- this.xhrFactory = xhrFactory;
2553
+ class JsonpClientBackend {
2554
+ callbackMap;
2555
+ document;
2556
+ /**
2557
+ * A resolved promise that can be used to schedule microtasks in the event handlers.
2558
+ */
2559
+ resolvedPromise = Promise.resolve();
2560
+ constructor(callbackMap, document) {
2561
+ this.callbackMap = callbackMap;
2562
+ this.document = document;
2452
2563
  }
2453
2564
  /**
2454
- * Processes a request and returns a stream of response events.
2565
+ * Get the name of the next callback method, by incrementing the global `nextRequestId`.
2566
+ */
2567
+ nextCallback() {
2568
+ return `ng_jsonp_callback_${nextRequestId++}`;
2569
+ }
2570
+ /**
2571
+ * Processes a JSONP request and returns an event stream of the results.
2455
2572
  * @param req The request object.
2456
2573
  * @returns An observable of the response events.
2574
+ *
2457
2575
  */
2458
2576
  handle(req) {
2459
- // Quick check to give a better error message when a user attempts to use
2460
- // HttpClient.jsonp() without installing the HttpClientJsonpModule
2461
- if (req.method === 'JSONP') {
2462
- throw new _RuntimeError(-2800 /* RuntimeErrorCode.MISSING_JSONP_MODULE */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
2463
- `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.`);
2577
+ // Firstly, check both the method and response type. If either doesn't match
2578
+ // then the request was improperly routed here and cannot be handled.
2579
+ if (req.method !== 'JSONP') {
2580
+ throw new _RuntimeError(2810 /* RuntimeErrorCode.JSONP_WRONG_METHOD */, ngDevMode && JSONP_ERR_WRONG_METHOD);
2464
2581
  }
2465
- // Validate that the request is compatible with the XHR backend.
2466
- ngDevMode && validateXhrCompatibility(req);
2467
- // Check whether this factory has a special function to load an XHR implementation
2468
- // for various non-browser environments. We currently limit it to only `ServerXhr`
2469
- // class, which needs to load an XHR implementation.
2470
- const xhrFactory = this.xhrFactory;
2471
- const source =
2472
- // Note that `ɵloadImpl` is never defined in client bundles and can be
2473
- // safely dropped whenever we're running in the browser.
2474
- // This branching is redundant.
2475
- // The `ngServerMode` guard also enables tree-shaking of the `from()`
2476
- // function from the common bundle, as it's only used in server code.
2477
- typeof ngServerMode !== 'undefined' && ngServerMode && xhrFactory.ɵloadImpl
2478
- ? from(xhrFactory.ɵloadImpl())
2479
- : of(null);
2480
- return source.pipe(switchMap(() => {
2481
- // Everything happens on Observable subscription.
2482
- return new Observable((observer) => {
2483
- // Start by setting up the XHR object with request method, URL, and withCredentials
2484
- // flag.
2485
- const xhr = xhrFactory.build();
2486
- xhr.open(req.method, req.urlWithParams);
2487
- if (req.withCredentials) {
2488
- xhr.withCredentials = true;
2489
- }
2490
- // Add all the requested headers.
2491
- req.headers.forEach((name, values) => xhr.setRequestHeader(name, values.join(',')));
2492
- // Add an Accept header if one isn't present already.
2493
- if (!req.headers.has(ACCEPT_HEADER)) {
2494
- xhr.setRequestHeader(ACCEPT_HEADER, ACCEPT_HEADER_VALUE);
2495
- }
2496
- // Auto-detect the Content-Type header if one isn't present already.
2497
- if (!req.headers.has(CONTENT_TYPE_HEADER)) {
2498
- const detectedType = req.detectContentTypeHeader();
2499
- // Sometimes Content-Type detection fails.
2500
- if (detectedType !== null) {
2501
- xhr.setRequestHeader(CONTENT_TYPE_HEADER, detectedType);
2502
- }
2503
- }
2504
- if (req.timeout) {
2505
- xhr.timeout = req.timeout;
2506
- }
2507
- // Set the responseType if one was requested.
2508
- if (req.responseType) {
2509
- const responseType = req.responseType.toLowerCase();
2510
- // JSON responses need to be processed as text. This is because if the server
2511
- // returns an XSSI-prefixed JSON response, the browser will fail to parse it,
2512
- // xhr.response will be null, and xhr.responseText cannot be accessed to
2513
- // retrieve the prefixed JSON data in order to strip the prefix. Thus, all JSON
2514
- // is parsed by first requesting text and then applying JSON.parse.
2515
- xhr.responseType = (responseType !== 'json' ? responseType : 'text');
2516
- }
2517
- // Serialize the request body if one is present. If not, this will be set to null.
2518
- const reqBody = req.serializeBody();
2519
- // If progress events are enabled, response headers will be delivered
2520
- // in two events - the HttpHeaderResponse event and the full HttpResponse
2521
- // event. However, since response headers don't change in between these
2522
- // two events, it doesn't make sense to parse them twice. So headerResponse
2523
- // caches the data extracted from the response whenever it's first parsed,
2524
- // to ensure parsing isn't duplicated.
2525
- let headerResponse = null;
2526
- // partialFromXhr extracts the HttpHeaderResponse from the current XMLHttpRequest
2527
- // state, and memoizes it into headerResponse.
2528
- const partialFromXhr = () => {
2529
- if (headerResponse !== null) {
2530
- return headerResponse;
2531
- }
2532
- const statusText = xhr.statusText || 'OK';
2533
- // Parse headers from XMLHttpRequest - this step is lazy.
2534
- const headers = new HttpHeaders(xhr.getAllResponseHeaders());
2535
- // Read the response URL from the XMLHttpResponse instance and fall back on the
2536
- // request URL.
2537
- const url = getResponseUrl(xhr) || req.url;
2538
- // Construct the HttpHeaderResponse and memoize it.
2539
- headerResponse = new HttpHeaderResponse({ headers, status: xhr.status, statusText, url });
2540
- return headerResponse;
2541
- };
2542
- // Next, a few closures are defined for the various events which XMLHttpRequest can
2543
- // emit. This allows them to be unregistered as event listeners later.
2544
- // First up is the load event, which represents a response being fully available.
2545
- const onLoad = () => {
2546
- // Read response state from the memoized partial data.
2547
- let { headers, status, statusText, url } = partialFromXhr();
2548
- // The body will be read out if present.
2549
- let body = null;
2550
- if (status !== HTTP_STATUS_CODE_NO_CONTENT) {
2551
- // Use XMLHttpRequest.response if set, responseText otherwise.
2552
- body = typeof xhr.response === 'undefined' ? xhr.responseText : xhr.response;
2553
- }
2554
- // Normalize another potential bug (this one comes from CORS).
2555
- if (status === 0) {
2556
- status = !!body ? HTTP_STATUS_CODE_OK : 0;
2557
- }
2558
- // ok determines whether the response will be transmitted on the event or
2559
- // error channel. Unsuccessful status codes (not 2xx) will always be errors,
2560
- // but a successful status code can still result in an error if the user
2561
- // asked for JSON data and the body cannot be parsed as such.
2562
- let ok = status >= 200 && status < 300;
2563
- // Check whether the body needs to be parsed as JSON (in many cases the browser
2564
- // will have done that already).
2565
- if (req.responseType === 'json' && typeof body === 'string') {
2566
- // Save the original body, before attempting XSSI prefix stripping.
2567
- const originalBody = body;
2568
- body = body.replace(XSSI_PREFIX, '');
2569
- try {
2570
- // Attempt the parse. If it fails, a parse error should be delivered to the
2571
- // user.
2572
- body = body !== '' ? JSON.parse(body) : null;
2573
- }
2574
- catch (error) {
2575
- // Since the JSON.parse failed, it's reasonable to assume this might not have
2576
- // been a JSON response. Restore the original body (including any XSSI prefix)
2577
- // to deliver a better error response.
2578
- body = originalBody;
2579
- // If this was an error request to begin with, leave it as a string, it
2580
- // probably just isn't JSON. Otherwise, deliver the parsing error to the user.
2581
- if (ok) {
2582
- // Even though the response status was 2xx, this is still an error.
2583
- ok = false;
2584
- // The parse error contains the text of the body that failed to parse.
2585
- body = { error, text: body };
2586
- }
2587
- }
2588
- }
2589
- if (ok) {
2590
- // A successful response is delivered on the event stream.
2591
- observer.next(new HttpResponse({
2592
- body,
2593
- headers,
2594
- status,
2595
- statusText,
2596
- url: url || undefined,
2597
- }));
2598
- // The full body has been received and delivered, no further events
2599
- // are possible. This request is complete.
2600
- observer.complete();
2601
- }
2602
- else {
2603
- // An unsuccessful request is delivered on the error channel.
2582
+ else if (req.responseType !== 'json') {
2583
+ throw new _RuntimeError(2811 /* RuntimeErrorCode.JSONP_WRONG_RESPONSE_TYPE */, ngDevMode && JSONP_ERR_WRONG_RESPONSE_TYPE);
2584
+ }
2585
+ // Check the request headers. JSONP doesn't support headers and
2586
+ // cannot set any that were supplied.
2587
+ if (req.headers.keys().length > 0) {
2588
+ throw new _RuntimeError(2812 /* RuntimeErrorCode.JSONP_HEADERS_NOT_SUPPORTED */, ngDevMode && JSONP_ERR_HEADERS_NOT_SUPPORTED);
2589
+ }
2590
+ // Everything else happens inside the Observable boundary.
2591
+ return new Observable((observer) => {
2592
+ // The first step to make a request is to generate the callback name, and replace the
2593
+ // callback placeholder in the URL with the name. Care has to be taken here to ensure
2594
+ // a trailing &, if matched, gets inserted back into the URL in the correct place.
2595
+ const callback = this.nextCallback();
2596
+ const url = req.urlWithParams.replace(/=JSONP_CALLBACK(&|$)/, `=${callback}$1`);
2597
+ // Construct the <script> tag and point it at the URL.
2598
+ const node = this.document.createElement('script');
2599
+ node.src = url;
2600
+ // A JSONP request requires waiting for multiple callbacks. These variables
2601
+ // are closed over and track state across those callbacks.
2602
+ // The response object, if one has been received, or null otherwise.
2603
+ let body = null;
2604
+ // Whether the response callback has been called.
2605
+ let finished = false;
2606
+ // Set the response callback in this.callbackMap (which will be the window
2607
+ // object in the browser. The script being loaded via the <script> tag will
2608
+ // eventually call this callback.
2609
+ this.callbackMap[callback] = (data) => {
2610
+ // Data has been received from the JSONP script. Firstly, delete this callback.
2611
+ delete this.callbackMap[callback];
2612
+ // Set state to indicate data was received.
2613
+ body = data;
2614
+ finished = true;
2615
+ };
2616
+ // cleanup() is a utility closure that removes the <script> from the page and
2617
+ // the response callback from the window. This logic is used in both the
2618
+ // success, error, and cancellation paths, so it's extracted out for convenience.
2619
+ const cleanup = () => {
2620
+ node.removeEventListener('load', onLoad);
2621
+ node.removeEventListener('error', onError);
2622
+ // Remove the <script> tag if it's still on the page.
2623
+ node.remove();
2624
+ // Remove the response callback from the callbackMap (window object in the
2625
+ // browser).
2626
+ delete this.callbackMap[callback];
2627
+ };
2628
+ // onLoad() is the success callback which runs after the response callback
2629
+ // if the JSONP script loads successfully. The event itself is unimportant.
2630
+ // If something went wrong, onLoad() may run without the response callback
2631
+ // having been invoked.
2632
+ const onLoad = () => {
2633
+ // We wrap it in an extra Promise, to ensure the microtask
2634
+ // is scheduled after the loaded endpoint has executed any potential microtask itself,
2635
+ // which is not guaranteed in Internet Explorer and EdgeHTML. See issue #39496
2636
+ this.resolvedPromise.then(() => {
2637
+ // Cleanup the page.
2638
+ cleanup();
2639
+ // Check whether the response callback has run.
2640
+ if (!finished) {
2641
+ // It hasn't, something went wrong with the request. Return an error via
2642
+ // the Observable error path. All JSONP errors have status 0.
2604
2643
  observer.error(new HttpErrorResponse({
2605
- // The error in this case is the response body (error from the server).
2606
- error: body,
2607
- headers,
2608
- status,
2609
- statusText,
2610
- url: url || undefined,
2644
+ url,
2645
+ status: 0,
2646
+ statusText: 'JSONP Error',
2647
+ error: new Error(JSONP_ERR_NO_CALLBACK),
2611
2648
  }));
2649
+ return;
2612
2650
  }
2613
- };
2614
- // The onError callback is called when something goes wrong at the network level.
2615
- // Connection timeout, DNS error, offline, etc. These are actual errors, and are
2616
- // transmitted on the error channel.
2617
- const onError = (error) => {
2618
- const { url } = partialFromXhr();
2619
- const res = new HttpErrorResponse({
2620
- error,
2621
- status: xhr.status || 0,
2622
- statusText: xhr.statusText || 'Unknown Error',
2623
- url: url || undefined,
2624
- });
2625
- observer.error(res);
2626
- };
2627
- let onTimeout = onError;
2628
- if (req.timeout) {
2629
- onTimeout = (_) => {
2630
- const { url } = partialFromXhr();
2631
- const res = new HttpErrorResponse({
2632
- error: new DOMException('Request timed out', 'TimeoutError'),
2633
- status: xhr.status || 0,
2634
- statusText: xhr.statusText || 'Request timeout',
2635
- url: url || undefined,
2636
- });
2637
- observer.error(res);
2638
- };
2639
- }
2640
- // The sentHeaders flag tracks whether the HttpResponseHeaders event
2641
- // has been sent on the stream. This is necessary to track if progress
2642
- // is enabled since the event will be sent on only the first download
2643
- // progress event.
2644
- let sentHeaders = false;
2645
- // The download progress event handler, which is only registered if
2646
- // progress events are enabled.
2647
- const onDownProgress = (event) => {
2648
- // Send the HttpResponseHeaders event if it hasn't been sent already.
2649
- if (!sentHeaders) {
2650
- observer.next(partialFromXhr());
2651
- sentHeaders = true;
2652
- }
2653
- // Start building the download progress event to deliver on the response
2654
- // event stream.
2655
- let progressEvent = {
2656
- type: HttpEventType.DownloadProgress,
2657
- loaded: event.loaded,
2658
- };
2659
- // Set the total number of bytes in the event if it's available.
2660
- if (event.lengthComputable) {
2661
- progressEvent.total = event.total;
2662
- }
2663
- // If the request was for text content and a partial response is
2664
- // available on XMLHttpRequest, include it in the progress event
2665
- // to allow for streaming reads.
2666
- if (req.responseType === 'text' && !!xhr.responseText) {
2667
- progressEvent.partialText = xhr.responseText;
2668
- }
2669
- // Finally, fire the event.
2670
- observer.next(progressEvent);
2671
- };
2672
- // The upload progress event handler, which is only registered if
2673
- // progress events are enabled.
2674
- const onUpProgress = (event) => {
2675
- // Upload progress events are simpler. Begin building the progress
2676
- // event.
2677
- let progress = {
2678
- type: HttpEventType.UploadProgress,
2679
- loaded: event.loaded,
2680
- };
2681
- // If the total number of bytes being uploaded is available, include
2682
- // it.
2683
- if (event.lengthComputable) {
2684
- progress.total = event.total;
2685
- }
2686
- // Send the event.
2687
- observer.next(progress);
2688
- };
2689
- // By default, register for load and error events.
2690
- xhr.addEventListener('load', onLoad);
2691
- xhr.addEventListener('error', onError);
2692
- xhr.addEventListener('timeout', onTimeout);
2693
- xhr.addEventListener('abort', onError);
2694
- // Progress events are only enabled if requested.
2695
- if (req.reportProgress) {
2696
- // Download progress is always enabled if requested.
2697
- xhr.addEventListener('progress', onDownProgress);
2698
- // Upload progress depends on whether there is a body to upload.
2699
- if (reqBody !== null && xhr.upload) {
2700
- xhr.upload.addEventListener('progress', onUpProgress);
2701
- }
2651
+ // Success. body either contains the response body or null if none was
2652
+ // returned.
2653
+ observer.next(new HttpResponse({
2654
+ body,
2655
+ status: HTTP_STATUS_CODE_OK,
2656
+ statusText: 'OK',
2657
+ url,
2658
+ }));
2659
+ // Complete the stream, the response is over.
2660
+ observer.complete();
2661
+ });
2662
+ };
2663
+ // onError() is the error callback, which runs if the script returned generates
2664
+ // a Javascript error. It emits the error via the Observable error channel as
2665
+ // a HttpErrorResponse.
2666
+ const onError = (error) => {
2667
+ cleanup();
2668
+ // Wrap the error in a HttpErrorResponse.
2669
+ observer.error(new HttpErrorResponse({
2670
+ error,
2671
+ status: 0,
2672
+ statusText: 'JSONP Error',
2673
+ url,
2674
+ }));
2675
+ };
2676
+ // Subscribe to both the success (load) and error events on the <script> tag,
2677
+ // and add it to the page.
2678
+ node.addEventListener('load', onLoad);
2679
+ node.addEventListener('error', onError);
2680
+ this.document.body.appendChild(node);
2681
+ // The request has now been successfully sent.
2682
+ observer.next({ type: HttpEventType.Sent });
2683
+ // Cancellation handler.
2684
+ return () => {
2685
+ if (!finished) {
2686
+ this.removeListeners(node);
2702
2687
  }
2703
- // Fire the request, and notify the event stream that it was fired.
2704
- xhr.send(reqBody);
2705
- observer.next({ type: HttpEventType.Sent });
2706
- // This is the return from the Observable function, which is the
2707
- // request cancellation handler.
2708
- return () => {
2709
- // On a cancellation, remove all registered event listeners.
2710
- xhr.removeEventListener('error', onError);
2711
- xhr.removeEventListener('abort', onError);
2712
- xhr.removeEventListener('load', onLoad);
2713
- xhr.removeEventListener('timeout', onTimeout);
2714
- if (req.reportProgress) {
2715
- xhr.removeEventListener('progress', onDownProgress);
2716
- if (reqBody !== null && xhr.upload) {
2717
- xhr.upload.removeEventListener('progress', onUpProgress);
2718
- }
2719
- }
2720
- // Finally, abort the in-flight request.
2721
- if (xhr.readyState !== xhr.DONE) {
2722
- xhr.abort();
2723
- }
2724
- };
2725
- });
2726
- }));
2688
+ // And finally, clean up the page.
2689
+ cleanup();
2690
+ };
2691
+ });
2727
2692
  }
2728
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXhrBackend, deps: [{ token: XhrFactory }], target: i0.ɵɵFactoryTarget.Injectable });
2729
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXhrBackend });
2693
+ removeListeners(script) {
2694
+ // Issue #34818
2695
+ // Changing <script>'s ownerDocument will prevent it from execution.
2696
+ // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
2697
+ foreignDocument ??= this.document.implementation.createHTMLDocument();
2698
+ foreignDocument.adoptNode(script);
2699
+ }
2700
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpClientBackend, deps: [{ token: JsonpCallbackContext }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
2701
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpClientBackend });
2730
2702
  }
2731
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXhrBackend, decorators: [{
2703
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpClientBackend, decorators: [{
2732
2704
  type: Injectable
2733
- }], ctorParameters: () => [{ type: XhrFactory }] });
2705
+ }], ctorParameters: () => [{ type: JsonpCallbackContext }, { type: undefined, decorators: [{
2706
+ type: Inject,
2707
+ args: [DOCUMENT]
2708
+ }] }] });
2709
+ /**
2710
+ * Identifies requests with the method JSONP and shifts them to the `JsonpClientBackend`.
2711
+ */
2712
+ function jsonpInterceptorFn(req, next) {
2713
+ if (req.method === 'JSONP') {
2714
+ return inject(JsonpClientBackend).handle(req);
2715
+ }
2716
+ // Fall through for normal HTTP requests.
2717
+ return next(req);
2718
+ }
2719
+ /**
2720
+ * Identifies requests with the method JSONP and
2721
+ * shifts them to the `JsonpClientBackend`.
2722
+ *
2723
+ * @see {@link HttpInterceptor}
2724
+ *
2725
+ * @publicApi
2726
+ */
2727
+ class JsonpInterceptor {
2728
+ injector;
2729
+ constructor(injector) {
2730
+ this.injector = injector;
2731
+ }
2732
+ /**
2733
+ * Identifies and handles a given JSONP request.
2734
+ * @param initialRequest The outgoing request object to handle.
2735
+ * @param next The next interceptor in the chain, or the backend
2736
+ * if no interceptors remain in the chain.
2737
+ * @returns An observable of the event stream.
2738
+ */
2739
+ intercept(initialRequest, next) {
2740
+ return runInInjectionContext(this.injector, () => jsonpInterceptorFn(initialRequest, (downstreamRequest) => next.handle(downstreamRequest)));
2741
+ }
2742
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpInterceptor, deps: [{ token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
2743
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpInterceptor });
2744
+ }
2745
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: JsonpInterceptor, decorators: [{
2746
+ type: Injectable
2747
+ }], ctorParameters: () => [{ type: i0.EnvironmentInjector }] });
2734
2748
 
2735
- const XSRF_ENABLED = new InjectionToken(ngDevMode ? 'XSRF_ENABLED' : '');
2749
+ const XSRF_ENABLED = new InjectionToken(ngDevMode ? 'XSRF_ENABLED' : '', {
2750
+ factory: () => true,
2751
+ });
2736
2752
  const XSRF_DEFAULT_COOKIE_NAME = 'XSRF-TOKEN';
2737
2753
  const XSRF_COOKIE_NAME = new InjectionToken(ngDevMode ? 'XSRF_COOKIE_NAME' : '', {
2738
2754
  providedIn: 'root',
@@ -2743,13 +2759,6 @@ const XSRF_HEADER_NAME = new InjectionToken(ngDevMode ? 'XSRF_HEADER_NAME' : '',
2743
2759
  providedIn: 'root',
2744
2760
  factory: () => XSRF_DEFAULT_HEADER_NAME,
2745
2761
  });
2746
- /**
2747
- * Retrieves the current XSRF token to use with the next outgoing request.
2748
- *
2749
- * @publicApi
2750
- */
2751
- class HttpXsrfTokenExtractor {
2752
- }
2753
2762
  /**
2754
2763
  * `HttpXsrfTokenExtractor` which retrieves the token from a cookie.
2755
2764
  */
@@ -2779,10 +2788,11 @@ class HttpXsrfCookieExtractor {
2779
2788
  return this.lastToken;
2780
2789
  }
2781
2790
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfCookieExtractor, deps: [{ token: DOCUMENT }, { token: XSRF_COOKIE_NAME }], target: i0.ɵɵFactoryTarget.Injectable });
2782
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfCookieExtractor });
2791
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfCookieExtractor, providedIn: 'root' });
2783
2792
  }
2784
2793
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfCookieExtractor, decorators: [{
2785
- type: Injectable
2794
+ type: Injectable,
2795
+ args: [{ providedIn: 'root' }]
2786
2796
  }], ctorParameters: () => [{ type: undefined, decorators: [{
2787
2797
  type: Inject,
2788
2798
  args: [DOCUMENT]
@@ -2790,6 +2800,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
2790
2800
  type: Inject,
2791
2801
  args: [XSRF_COOKIE_NAME]
2792
2802
  }] }] });
2803
+ /**
2804
+ * Retrieves the current XSRF token to use with the next outgoing request.
2805
+ *
2806
+ * @publicApi
2807
+ */
2808
+ class HttpXsrfTokenExtractor {
2809
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfTokenExtractor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2810
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfTokenExtractor, providedIn: 'root', useExisting: HttpXsrfCookieExtractor });
2811
+ }
2812
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HttpXsrfTokenExtractor, decorators: [{
2813
+ type: Injectable,
2814
+ args: [{ providedIn: 'root', useExisting: HttpXsrfCookieExtractor }]
2815
+ }] });
2793
2816
  function xsrfInterceptorFn(req, next) {
2794
2817
  const lcUrl = req.url.toLowerCase();
2795
2818
  // Skip both non-mutating requests and absolute URLs.
@@ -2891,7 +2914,6 @@ function provideHttpClient(...features) {
2891
2914
  }
2892
2915
  const providers = [
2893
2916
  HttpClient,
2894
- HttpXhrBackend,
2895
2917
  HttpInterceptorHandler,
2896
2918
  { provide: HttpHandler, useExisting: HttpInterceptorHandler },
2897
2919
  {
@@ -2905,8 +2927,6 @@ function provideHttpClient(...features) {
2905
2927
  useValue: xsrfInterceptorFn,
2906
2928
  multi: true,
2907
2929
  },
2908
- { provide: XSRF_ENABLED, useValue: true },
2909
- { provide: HttpXsrfTokenExtractor, useClass: HttpXsrfCookieExtractor },
2910
2930
  ];
2911
2931
  for (const feature of features) {
2912
2932
  providers.push(...feature.ɵproviders);