UNPKG

@angular/common

Version:

Angular - commonly needed directives and services

1,361 lines (1,352 loc) 125 kB
/** * @license Angular v19.2.12 * (c) 2010-2025 Google LLC. https://angular.io/ * License: MIT */ import * as i0 from '@angular/core'; import { ɵRuntimeError as _RuntimeError, Injectable, inject, NgZone, ApplicationRef, InjectionToken, ɵPendingTasksInternal as _PendingTasksInternal, PLATFORM_ID, ɵConsole as _Console, ɵformatRuntimeError as _formatRuntimeError, runInInjectionContext, Inject, makeEnvironmentProviders, NgModule } from '@angular/core'; import { concatMap, filter, map, finalize, switchMap } from 'rxjs/operators'; import { of, Observable, from } from 'rxjs'; import { isPlatformServer, XhrFactory, parseCookieValue } from './xhr-BfNfxNDv.mjs'; import { DOCUMENT } from './dom_tokens-rA0ACyx7.mjs'; /** * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a * `HttpResponse`. * * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the * `HttpBackend`. * * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain. * * @publicApi */ class HttpHandler { } /** * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend. * * Interceptors sit between the `HttpClient` interface and the `HttpBackend`. * * When injected, `HttpBackend` dispatches requests directly to the backend, without going * through the interceptor chain. * * @publicApi */ class HttpBackend { } /** * Represents the header configuration options for an HTTP request. * Instances are immutable. Modifying methods return a cloned * instance with the change. The original object is never changed. * * @publicApi */ class HttpHeaders { /** * Internal map of lowercase header names to values. */ headers; /** * Internal map of lowercased header names to the normalized * form of the name (the form seen first). */ normalizedNames = new Map(); /** * Complete the lazy initialization of this object (needed before reading). */ lazyInit; /** * Queued updates to be materialized the next initialization. */ lazyUpdate = null; /** Constructs a new HTTP header object with the given values.*/ constructor(headers) { if (!headers) { this.headers = new Map(); } else if (typeof headers === 'string') { this.lazyInit = () => { this.headers = new Map(); headers.split('\n').forEach((line) => { const index = line.indexOf(':'); if (index > 0) { const name = line.slice(0, index); const value = line.slice(index + 1).trim(); this.addHeaderEntry(name, value); } }); }; } else if (typeof Headers !== 'undefined' && headers instanceof Headers) { this.headers = new Map(); headers.forEach((value, name) => { this.addHeaderEntry(name, value); }); } else { this.lazyInit = () => { if (typeof ngDevMode === 'undefined' || ngDevMode) { assertValidHeaders(headers); } this.headers = new Map(); Object.entries(headers).forEach(([name, values]) => { this.setHeaderEntries(name, values); }); }; } } /** * Checks for existence of a given header. * * @param name The header name to check for existence. * * @returns True if the header exists, false otherwise. */ has(name) { this.init(); return this.headers.has(name.toLowerCase()); } /** * Retrieves the first value of a given header. * * @param name The header name. * * @returns The value string if the header exists, null otherwise */ get(name) { this.init(); const values = this.headers.get(name.toLowerCase()); return values && values.length > 0 ? values[0] : null; } /** * Retrieves the names of the headers. * * @returns A list of header names. */ keys() { this.init(); return Array.from(this.normalizedNames.values()); } /** * Retrieves a list of values for a given header. * * @param name The header name from which to retrieve values. * * @returns A string of values if the header exists, null otherwise. */ getAll(name) { this.init(); return this.headers.get(name.toLowerCase()) || null; } /** * Appends a new value to the existing set of values for a header * and returns them in a clone of the original instance. * * @param name The header name for which to append the values. * @param value The value to append. * * @returns A clone of the HTTP headers object with the value appended to the given header. */ append(name, value) { return this.clone({ name, value, op: 'a' }); } /** * Sets or modifies a value for a given header in a clone of the original instance. * If the header already exists, its value is replaced with the given value * in the returned object. * * @param name The header name. * @param value The value or values to set or override for the given header. * * @returns A clone of the HTTP headers object with the newly set header value. */ set(name, value) { return this.clone({ name, value, op: 's' }); } /** * Deletes values for a given header in a clone of the original instance. * * @param name The header name. * @param value The value or values to delete for the given header. * * @returns A clone of the HTTP headers object with the given value deleted. */ delete(name, value) { return this.clone({ name, value, op: 'd' }); } maybeSetNormalizedName(name, lcName) { if (!this.normalizedNames.has(lcName)) { this.normalizedNames.set(lcName, name); } } init() { if (!!this.lazyInit) { if (this.lazyInit instanceof HttpHeaders) { this.copyFrom(this.lazyInit); } else { this.lazyInit(); } this.lazyInit = null; if (!!this.lazyUpdate) { this.lazyUpdate.forEach((update) => this.applyUpdate(update)); this.lazyUpdate = null; } } } copyFrom(other) { other.init(); Array.from(other.headers.keys()).forEach((key) => { this.headers.set(key, other.headers.get(key)); this.normalizedNames.set(key, other.normalizedNames.get(key)); }); } clone(update) { const clone = new HttpHeaders(); clone.lazyInit = !!this.lazyInit && this.lazyInit instanceof HttpHeaders ? this.lazyInit : this; clone.lazyUpdate = (this.lazyUpdate || []).concat([update]); return clone; } applyUpdate(update) { const key = update.name.toLowerCase(); switch (update.op) { case 'a': case 's': let value = update.value; if (typeof value === 'string') { value = [value]; } if (value.length === 0) { return; } this.maybeSetNormalizedName(update.name, key); const base = (update.op === 'a' ? this.headers.get(key) : undefined) || []; base.push(...value); this.headers.set(key, base); break; case 'd': const toDelete = update.value; if (!toDelete) { this.headers.delete(key); this.normalizedNames.delete(key); } else { let existing = this.headers.get(key); if (!existing) { return; } existing = existing.filter((value) => toDelete.indexOf(value) === -1); if (existing.length === 0) { this.headers.delete(key); this.normalizedNames.delete(key); } else { this.headers.set(key, existing); } } break; } } addHeaderEntry(name, value) { const key = name.toLowerCase(); this.maybeSetNormalizedName(name, key); if (this.headers.has(key)) { this.headers.get(key).push(value); } else { this.headers.set(key, [value]); } } setHeaderEntries(name, values) { const headerValues = (Array.isArray(values) ? values : [values]).map((value) => value.toString()); const key = name.toLowerCase(); this.headers.set(key, headerValues); this.maybeSetNormalizedName(name, key); } /** * @internal */ forEach(fn) { this.init(); Array.from(this.normalizedNames.keys()).forEach((key) => fn(this.normalizedNames.get(key), this.headers.get(key))); } } /** * Verifies that the headers object has the right shape: the values * must be either strings, numbers or arrays. Throws an error if an invalid * header value is present. */ function assertValidHeaders(headers) { for (const [key, value] of Object.entries(headers)) { if (!(typeof value === 'string' || typeof value === 'number') && !Array.isArray(value)) { throw new Error(`Unexpected value of the \`${key}\` header provided. ` + `Expecting either a string, a number or an array, but got: \`${value}\`.`); } } } /** * Provides encoding and decoding of URL parameter and query-string values. * * Serializes and parses URL parameter keys and values to encode and decode them. * If you pass URL query parameters without encoding, * the query parameters can be misinterpreted at the receiving end. * * * @publicApi */ class HttpUrlEncodingCodec { /** * Encodes a key name for a URL parameter or query-string. * @param key The key name. * @returns The encoded key name. */ encodeKey(key) { return standardEncoding(key); } /** * Encodes the value of a URL parameter or query-string. * @param value The value. * @returns The encoded value. */ encodeValue(value) { return standardEncoding(value); } /** * Decodes an encoded URL parameter or query-string key. * @param key The encoded key name. * @returns The decoded key name. */ decodeKey(key) { return decodeURIComponent(key); } /** * Decodes an encoded URL parameter or query-string value. * @param value The encoded value. * @returns The decoded value. */ decodeValue(value) { return decodeURIComponent(value); } } function paramParser(rawParams, codec) { const map = new Map(); if (rawParams.length > 0) { // The `window.location.search` can be used while creating an instance of the `HttpParams` class // (e.g. `new HttpParams({ fromString: window.location.search })`). The `window.location.search` // may start with the `?` char, so we strip it if it's present. const params = rawParams.replace(/^\?/, '').split('&'); params.forEach((param) => { const eqIdx = param.indexOf('='); const [key, val] = eqIdx == -1 ? [codec.decodeKey(param), ''] : [codec.decodeKey(param.slice(0, eqIdx)), codec.decodeValue(param.slice(eqIdx + 1))]; const list = map.get(key) || []; list.push(val); map.set(key, list); }); } return map; } /** * Encode input string with standard encodeURIComponent and then un-encode specific characters. */ const STANDARD_ENCODING_REGEX = /%(\d[a-f0-9])/gi; const STANDARD_ENCODING_REPLACEMENTS = { '40': '@', '3A': ':', '24': '$', '2C': ',', '3B': ';', '3D': '=', '3F': '?', '2F': '/', }; function standardEncoding(v) { return encodeURIComponent(v).replace(STANDARD_ENCODING_REGEX, (s, t) => STANDARD_ENCODING_REPLACEMENTS[t] ?? s); } function valueToString(value) { return `${value}`; } /** * An HTTP request/response body that represents serialized parameters, * per the MIME type `application/x-www-form-urlencoded`. * * This class is immutable; all mutation operations return a new instance. * * @publicApi */ class HttpParams { map; encoder; updates = null; cloneFrom = null; constructor(options = {}) { this.encoder = options.encoder || new HttpUrlEncodingCodec(); if (options.fromString) { if (options.fromObject) { throw new _RuntimeError(2805 /* RuntimeErrorCode.CANNOT_SPECIFY_BOTH_FROM_STRING_AND_FROM_OBJECT */, ngDevMode && 'Cannot specify both fromString and fromObject.'); } this.map = paramParser(options.fromString, this.encoder); } else if (!!options.fromObject) { this.map = new Map(); Object.keys(options.fromObject).forEach((key) => { const value = options.fromObject[key]; // convert the values to strings const values = Array.isArray(value) ? value.map(valueToString) : [valueToString(value)]; this.map.set(key, values); }); } else { this.map = null; } } /** * Reports whether the body includes one or more values for a given parameter. * @param param The parameter name. * @returns True if the parameter has one or more values, * false if it has no value or is not present. */ has(param) { this.init(); return this.map.has(param); } /** * Retrieves the first value for a parameter. * @param param The parameter name. * @returns The first value of the given parameter, * or `null` if the parameter is not present. */ get(param) { this.init(); const res = this.map.get(param); return !!res ? res[0] : null; } /** * Retrieves all values for a parameter. * @param param The parameter name. * @returns All values in a string array, * or `null` if the parameter not present. */ getAll(param) { this.init(); return this.map.get(param) || null; } /** * Retrieves all the parameters for this body. * @returns The parameter names in a string array. */ keys() { this.init(); return Array.from(this.map.keys()); } /** * Appends a new value to existing values for a parameter. * @param param The parameter name. * @param value The new value to add. * @return A new body with the appended value. */ append(param, value) { return this.clone({ param, value, op: 'a' }); } /** * Constructs a new body with appended values for the given parameter name. * @param params parameters and values * @return A new body with the new value. */ appendAll(params) { const updates = []; Object.keys(params).forEach((param) => { const value = params[param]; if (Array.isArray(value)) { value.forEach((_value) => { updates.push({ param, value: _value, op: 'a' }); }); } else { updates.push({ param, value: value, op: 'a' }); } }); return this.clone(updates); } /** * Replaces the value for a parameter. * @param param The parameter name. * @param value The new value. * @return A new body with the new value. */ set(param, value) { return this.clone({ param, value, op: 's' }); } /** * Removes a given value or all values from a parameter. * @param param The parameter name. * @param value The value to remove, if provided. * @return A new body with the given value removed, or with all values * removed if no value is specified. */ delete(param, value) { return this.clone({ param, value, op: 'd' }); } /** * Serializes the body to an encoded string, where key-value pairs (separated by `=`) are * separated by `&`s. */ toString() { this.init(); return (this.keys() .map((key) => { const eKey = this.encoder.encodeKey(key); // `a: ['1']` produces `'a=1'` // `b: []` produces `''` // `c: ['1', '2']` produces `'c=1&c=2'` return this.map.get(key) .map((value) => eKey + '=' + this.encoder.encodeValue(value)) .join('&'); }) // filter out empty values because `b: []` produces `''` // which results in `a=1&&c=1&c=2` instead of `a=1&c=1&c=2` if we don't .filter((param) => param !== '') .join('&')); } clone(update) { const clone = new HttpParams({ encoder: this.encoder }); clone.cloneFrom = this.cloneFrom || this; clone.updates = (this.updates || []).concat(update); return clone; } init() { if (this.map === null) { this.map = new Map(); } if (this.cloneFrom !== null) { this.cloneFrom.init(); this.cloneFrom.keys().forEach((key) => this.map.set(key, this.cloneFrom.map.get(key))); this.updates.forEach((update) => { switch (update.op) { case 'a': case 's': const base = (update.op === 'a' ? this.map.get(update.param) : undefined) || []; base.push(valueToString(update.value)); this.map.set(update.param, base); break; case 'd': if (update.value !== undefined) { let base = this.map.get(update.param) || []; const idx = base.indexOf(valueToString(update.value)); if (idx !== -1) { base.splice(idx, 1); } if (base.length > 0) { this.map.set(update.param, base); } else { this.map.delete(update.param); } } else { this.map.delete(update.param); break; } } }); this.cloneFrom = this.updates = null; } } } /** * A token used to manipulate and access values stored in `HttpContext`. * * @publicApi */ class HttpContextToken { defaultValue; constructor(defaultValue) { this.defaultValue = defaultValue; } } /** * Http context stores arbitrary user defined values and ensures type safety without * actually knowing the types. It is backed by a `Map` and guarantees that keys do not clash. * * This context is mutable and is shared between cloned requests unless explicitly specified. * * @usageNotes * * ### Usage Example * * ```ts * // inside cache.interceptors.ts * export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false); * * export class CacheInterceptor implements HttpInterceptor { * * intercept(req: HttpRequest<any>, delegate: HttpHandler): Observable<HttpEvent<any>> { * if (req.context.get(IS_CACHE_ENABLED) === true) { * return ...; * } * return delegate.handle(req); * } * } * * // inside a service * * this.httpClient.get('/api/weather', { * context: new HttpContext().set(IS_CACHE_ENABLED, true) * }).subscribe(...); * ``` * * @publicApi */ class HttpContext { map = new Map(); /** * Store a value in the context. If a value is already present it will be overwritten. * * @param token The reference to an instance of `HttpContextToken`. * @param value The value to store. * * @returns A reference to itself for easy chaining. */ set(token, value) { this.map.set(token, value); return this; } /** * Retrieve the value associated with the given token. * * @param token The reference to an instance of `HttpContextToken`. * * @returns The stored value or default if one is defined. */ get(token) { if (!this.map.has(token)) { this.map.set(token, token.defaultValue()); } return this.map.get(token); } /** * Delete the value associated with the given token. * * @param token The reference to an instance of `HttpContextToken`. * * @returns A reference to itself for easy chaining. */ delete(token) { this.map.delete(token); return this; } /** * Checks for existence of a given token. * * @param token The reference to an instance of `HttpContextToken`. * * @returns True if the token exists, false otherwise. */ has(token) { return this.map.has(token); } /** * @returns a list of tokens currently stored in the context. */ keys() { return this.map.keys(); } } /** * Determine whether the given HTTP method may include a body. */ function mightHaveBody(method) { switch (method) { case 'DELETE': case 'GET': case 'HEAD': case 'OPTIONS': case 'JSONP': return false; default: return true; } } /** * Safely assert whether the given value is an ArrayBuffer. * * In some execution environments ArrayBuffer is not defined. */ function isArrayBuffer(value) { return typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer; } /** * Safely assert whether the given value is a Blob. * * In some execution environments Blob is not defined. */ function isBlob(value) { return typeof Blob !== 'undefined' && value instanceof Blob; } /** * Safely assert whether the given value is a FormData instance. * * In some execution environments FormData is not defined. */ function isFormData(value) { return typeof FormData !== 'undefined' && value instanceof FormData; } /** * Safely assert whether the given value is a URLSearchParams instance. * * In some execution environments URLSearchParams is not defined. */ function isUrlSearchParams(value) { return typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams; } /** * `Content-Type` is an HTTP header used to indicate the media type * (also known as MIME type) of the resource being sent to the client * or received from the server. */ const CONTENT_TYPE_HEADER = 'Content-Type'; /** * The `Accept` header is an HTTP request header that indicates the media types * (or content types) the client is willing to receive from the server. */ const ACCEPT_HEADER = 'Accept'; /** * `X-Request-URL` is a custom HTTP header used in older browser versions, * including Firefox (< 32), Chrome (< 37), Safari (< 8), and Internet Explorer, * to include the full URL of the request in cross-origin requests. */ const X_REQUEST_URL_HEADER = 'X-Request-URL'; /** * `text/plain` is a content type used to indicate that the content being * sent is plain text with no special formatting or structured data * like HTML, XML, or JSON. */ const TEXT_CONTENT_TYPE = 'text/plain'; /** * `application/json` is a content type used to indicate that the content * being sent is in the JSON format. */ const JSON_CONTENT_TYPE = 'application/json'; /** * `application/json, text/plain, *\/*` is a content negotiation string often seen in the * Accept header of HTTP requests. It indicates the types of content the client is willing * to accept from the server, with a preference for `application/json` and `text/plain`, * but also accepting any other type (*\/*). */ const ACCEPT_HEADER_VALUE = `${JSON_CONTENT_TYPE}, ${TEXT_CONTENT_TYPE}, */*`; /** * An outgoing HTTP request with an optional typed body. * * `HttpRequest` represents an outgoing request, including URL, method, * headers, body, and other request configuration options. Instances should be * assumed to be immutable. To modify a `HttpRequest`, the `clone` * method should be used. * * @publicApi */ class HttpRequest { url; /** * The request body, or `null` if one isn't set. * * Bodies are not enforced to be immutable, as they can include a reference to any * user-defined data type. However, interceptors should take care to preserve * idempotence by treating them as such. */ body = null; /** * Outgoing headers for this request. */ headers; /** * Shared and mutable context that can be used by interceptors */ context; /** * Whether this request should be made in a way that exposes progress events. * * Progress events are expensive (change detection runs on each event) and so * they should only be requested if the consumer intends to monitor them. * * Note: The `FetchBackend` doesn't support progress report on uploads. */ reportProgress = false; /** * Whether this request should be sent with outgoing credentials (cookies). */ withCredentials = false; /** * The expected response type of the server. * * This is used to parse the response appropriately before returning it to * the requestee. */ responseType = 'json'; /** * The outgoing HTTP request method. */ method; /** * Outgoing URL parameters. * * To pass a string representation of HTTP parameters in the URL-query-string format, * the `HttpParamsOptions`' `fromString` may be used. For example: * * ```ts * new HttpParams({fromString: 'angular=awesome'}) * ``` */ params; /** * The outgoing URL with all URL parameters set. */ urlWithParams; /** * The HttpTransferCache option for the request */ transferCache; constructor(method, url, third, fourth) { this.url = url; this.method = method.toUpperCase(); // Next, need to figure out which argument holds the HttpRequestInit // options, if any. let options; // Check whether a body argument is expected. The only valid way to omit // the body argument is to use a known no-body method like GET. if (mightHaveBody(this.method) || !!fourth) { // Body is the third argument, options are the fourth. this.body = third !== undefined ? third : null; options = fourth; } else { // No body required, options are the third argument. The body stays null. options = third; } // If options have been passed, interpret them. if (options) { // Normalize reportProgress and withCredentials. this.reportProgress = !!options.reportProgress; this.withCredentials = !!options.withCredentials; // Override default response type of 'json' if one is provided. if (!!options.responseType) { this.responseType = options.responseType; } // Override headers if they're provided. if (!!options.headers) { this.headers = options.headers; } if (!!options.context) { this.context = options.context; } if (!!options.params) { this.params = options.params; } // We do want to assign transferCache even if it's falsy (false is valid value) this.transferCache = options.transferCache; } // If no headers have been passed in, construct a new HttpHeaders instance. this.headers ??= new HttpHeaders(); // If no context have been passed in, construct a new HttpContext instance. this.context ??= new HttpContext(); // If no parameters have been passed in, construct a new HttpUrlEncodedParams instance. if (!this.params) { this.params = new HttpParams(); this.urlWithParams = url; } else { // Encode the parameters to a string in preparation for inclusion in the URL. const params = this.params.toString(); if (params.length === 0) { // No parameters, the visible URL is just the URL given at creation time. this.urlWithParams = url; } else { // Does the URL already have query parameters? Look for '?'. const qIdx = url.indexOf('?'); // There are 3 cases to handle: // 1) No existing parameters -> append '?' followed by params. // 2) '?' exists and is followed by existing query string -> // append '&' followed by params. // 3) '?' exists at the end of the url -> append params directly. // This basically amounts to determining the character, if any, with // which to join the URL and parameters. const sep = qIdx === -1 ? '?' : qIdx < url.length - 1 ? '&' : ''; this.urlWithParams = url + sep + params; } } } /** * Transform the free-form body into a serialized format suitable for * transmission to the server. */ serializeBody() { // If no body is present, no need to serialize it. if (this.body === null) { return null; } // Check whether the body is already in a serialized form. If so, // it can just be returned directly. if (typeof this.body === 'string' || isArrayBuffer(this.body) || isBlob(this.body) || isFormData(this.body) || isUrlSearchParams(this.body)) { return this.body; } // Check whether the body is an instance of HttpUrlEncodedParams. if (this.body instanceof HttpParams) { return this.body.toString(); } // Check whether the body is an object or array, and serialize with JSON if so. if (typeof this.body === 'object' || typeof this.body === 'boolean' || Array.isArray(this.body)) { return JSON.stringify(this.body); } // Fall back on toString() for everything else. return this.body.toString(); } /** * Examine the body and attempt to infer an appropriate MIME type * for it. * * If no such type can be inferred, this method will return `null`. */ detectContentTypeHeader() { // An empty body has no content type. if (this.body === null) { return null; } // FormData bodies rely on the browser's content type assignment. if (isFormData(this.body)) { return null; } // Blobs usually have their own content type. If it doesn't, then // no type can be inferred. if (isBlob(this.body)) { return this.body.type || null; } // Array buffers have unknown contents and thus no type can be inferred. if (isArrayBuffer(this.body)) { return null; } // Technically, strings could be a form of JSON data, but it's safe enough // to assume they're plain strings. if (typeof this.body === 'string') { return TEXT_CONTENT_TYPE; } // `HttpUrlEncodedParams` has its own content-type. if (this.body instanceof HttpParams) { return 'application/x-www-form-urlencoded;charset=UTF-8'; } // Arrays, objects, boolean and numbers will be encoded as JSON. if (typeof this.body === 'object' || typeof this.body === 'number' || typeof this.body === 'boolean') { return JSON_CONTENT_TYPE; } // No type could be inferred. return null; } clone(update = {}) { // For method, url, and responseType, take the current value unless // it is overridden in the update hash. const method = update.method || this.method; const url = update.url || this.url; const responseType = update.responseType || this.responseType; // Carefully handle the transferCache to differentiate between // `false` and `undefined` in the update args. const transferCache = update.transferCache ?? this.transferCache; // The body is somewhat special - a `null` value in update.body means // whatever current body is present is being overridden with an empty // body, whereas an `undefined` value in update.body implies no // override. const body = update.body !== undefined ? update.body : this.body; // Carefully handle the boolean options to differentiate between // `false` and `undefined` in the update args. const withCredentials = update.withCredentials ?? this.withCredentials; const reportProgress = update.reportProgress ?? this.reportProgress; // Headers and params may be appended to if `setHeaders` or // `setParams` are used. let headers = update.headers || this.headers; let params = update.params || this.params; // Pass on context if needed const context = update.context ?? this.context; // Check whether the caller has asked to add headers. if (update.setHeaders !== undefined) { // Set every requested header. headers = Object.keys(update.setHeaders).reduce((headers, name) => headers.set(name, update.setHeaders[name]), headers); } // Check whether the caller has asked to set params. if (update.setParams) { // Set every requested param. params = Object.keys(update.setParams).reduce((params, param) => params.set(param, update.setParams[param]), params); } // Finally, construct the new HttpRequest using the pieces from above. return new HttpRequest(method, url, body, { params, headers, context, reportProgress, responseType, withCredentials, transferCache, }); } } /** * Type enumeration for the different kinds of `HttpEvent`. * * @publicApi */ var HttpEventType; (function (HttpEventType) { /** * The request was sent out over the wire. */ HttpEventType[HttpEventType["Sent"] = 0] = "Sent"; /** * An upload progress event was received. * * Note: The `FetchBackend` doesn't support progress report on uploads. */ HttpEventType[HttpEventType["UploadProgress"] = 1] = "UploadProgress"; /** * The response status code and headers were received. */ HttpEventType[HttpEventType["ResponseHeader"] = 2] = "ResponseHeader"; /** * A download progress event was received. */ HttpEventType[HttpEventType["DownloadProgress"] = 3] = "DownloadProgress"; /** * The full response including the body was received. */ HttpEventType[HttpEventType["Response"] = 4] = "Response"; /** * A custom event from an interceptor or a backend. */ HttpEventType[HttpEventType["User"] = 5] = "User"; })(HttpEventType || (HttpEventType = {})); /** * Base class for both `HttpResponse` and `HttpHeaderResponse`. * * @publicApi */ class HttpResponseBase { /** * All response headers. */ headers; /** * Response status code. */ status; /** * Textual description of response status code, defaults to OK. * * Do not depend on this. */ statusText; /** * URL of the resource retrieved, or null if not available. */ url; /** * Whether the status code falls in the 2xx range. */ ok; /** * Type of the response, narrowed to either the full response or the header. */ type; /** * Super-constructor for all responses. * * The single parameter accepted is an initialization hash. Any properties * of the response passed there will override the default values. */ constructor(init, defaultStatus = 200, defaultStatusText = 'OK') { // If the hash has values passed, use them to initialize the response. // Otherwise use the default values. this.headers = init.headers || new HttpHeaders(); this.status = init.status !== undefined ? init.status : defaultStatus; this.statusText = init.statusText || defaultStatusText; this.url = init.url || null; // Cache the ok value to avoid defining a getter. this.ok = this.status >= 200 && this.status < 300; } } /** * A partial HTTP response which only includes the status and header data, * but no response body. * * `HttpHeaderResponse` is a `HttpEvent` available on the response * event stream, only when progress events are requested. * * @publicApi */ class HttpHeaderResponse extends HttpResponseBase { /** * Create a new `HttpHeaderResponse` with the given parameters. */ constructor(init = {}) { super(init); } type = HttpEventType.ResponseHeader; /** * Copy this `HttpHeaderResponse`, overriding its contents with the * given parameter hash. */ clone(update = {}) { // Perform a straightforward initialization of the new HttpHeaderResponse, // overriding the current parameters with new ones if given. return new HttpHeaderResponse({ headers: update.headers || this.headers, status: update.status !== undefined ? update.status : this.status, statusText: update.statusText || this.statusText, url: update.url || this.url || undefined, }); } } /** * A full HTTP response, including a typed response body (which may be `null` * if one was not returned). * * `HttpResponse` is a `HttpEvent` available on the response event * stream. * * @publicApi */ class HttpResponse extends HttpResponseBase { /** * The response body, or `null` if one was not returned. */ body; /** * Construct a new `HttpResponse`. */ constructor(init = {}) { super(init); this.body = init.body !== undefined ? init.body : null; } type = HttpEventType.Response; clone(update = {}) { return new HttpResponse({ body: update.body !== undefined ? update.body : this.body, headers: update.headers || this.headers, status: update.status !== undefined ? update.status : this.status, statusText: update.statusText || this.statusText, url: update.url || this.url || undefined, }); } } /** * A response that represents an error or failure, either from a * non-successful HTTP status, an error while executing the request, * or some other failure which occurred during the parsing of the response. * * Any error returned on the `Observable` response stream will be * wrapped in an `HttpErrorResponse` to provide additional context about * the state of the HTTP layer when the error occurred. The error property * will contain either a wrapped Error object or the error response returned * from the server. * * @publicApi */ class HttpErrorResponse extends HttpResponseBase { name = 'HttpErrorResponse'; message; error; /** * Errors are never okay, even when the status code is in the 2xx success range. */ ok = false; constructor(init) { // Initialize with a default status of 0 / Unknown Error. super(init, 0, 'Unknown Error'); // If the response was successful, then this was a parse error. Otherwise, it was // a protocol-level failure of some sort. Either the request failed in transit // or the server returned an unsuccessful status code. if (this.status >= 200 && this.status < 300) { this.message = `Http failure during parsing for ${init.url || '(unknown url)'}`; } else { this.message = `Http failure response for ${init.url || '(unknown url)'}: ${init.status} ${init.statusText}`; } this.error = init.error || null; } } /** * We use these constant to prevent pulling the whole HttpStatusCode enum * Those are the only ones referenced directly by the framework */ const HTTP_STATUS_CODE_OK = 200; const HTTP_STATUS_CODE_NO_CONTENT = 204; /** * Http status codes. * As per https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * @publicApi */ var HttpStatusCode; (function (HttpStatusCode) { HttpStatusCode[HttpStatusCode["Continue"] = 100] = "Continue"; HttpStatusCode[HttpStatusCode["SwitchingProtocols"] = 101] = "SwitchingProtocols"; HttpStatusCode[HttpStatusCode["Processing"] = 102] = "Processing"; HttpStatusCode[HttpStatusCode["EarlyHints"] = 103] = "EarlyHints"; HttpStatusCode[HttpStatusCode["Ok"] = 200] = "Ok"; HttpStatusCode[HttpStatusCode["Created"] = 201] = "Created"; HttpStatusCode[HttpStatusCode["Accepted"] = 202] = "Accepted"; HttpStatusCode[HttpStatusCode["NonAuthoritativeInformation"] = 203] = "NonAuthoritativeInformation"; HttpStatusCode[HttpStatusCode["NoContent"] = 204] = "NoContent"; HttpStatusCode[HttpStatusCode["ResetContent"] = 205] = "ResetContent"; HttpStatusCode[HttpStatusCode["PartialContent"] = 206] = "PartialContent"; HttpStatusCode[HttpStatusCode["MultiStatus"] = 207] = "MultiStatus"; HttpStatusCode[HttpStatusCode["AlreadyReported"] = 208] = "AlreadyReported"; HttpStatusCode[HttpStatusCode["ImUsed"] = 226] = "ImUsed"; HttpStatusCode[HttpStatusCode["MultipleChoices"] = 300] = "MultipleChoices"; HttpStatusCode[HttpStatusCode["MovedPermanently"] = 301] = "MovedPermanently"; HttpStatusCode[HttpStatusCode["Found"] = 302] = "Found"; HttpStatusCode[HttpStatusCode["SeeOther"] = 303] = "SeeOther"; HttpStatusCode[HttpStatusCode["NotModified"] = 304] = "NotModified"; HttpStatusCode[HttpStatusCode["UseProxy"] = 305] = "UseProxy"; HttpStatusCode[HttpStatusCode["Unused"] = 306] = "Unused"; HttpStatusCode[HttpStatusCode["TemporaryRedirect"] = 307] = "TemporaryRedirect"; HttpStatusCode[HttpStatusCode["PermanentRedirect"] = 308] = "PermanentRedirect"; HttpStatusCode[HttpStatusCode["BadRequest"] = 400] = "BadRequest"; HttpStatusCode[HttpStatusCode["Unauthorized"] = 401] = "Unauthorized"; HttpStatusCode[HttpStatusCode["PaymentRequired"] = 402] = "PaymentRequired"; HttpStatusCode[HttpStatusCode["Forbidden"] = 403] = "Forbidden"; HttpStatusCode[HttpStatusCode["NotFound"] = 404] = "NotFound"; HttpStatusCode[HttpStatusCode["MethodNotAllowed"] = 405] = "MethodNotAllowed"; HttpStatusCode[HttpStatusCode["NotAcceptable"] = 406] = "NotAcceptable"; HttpStatusCode[HttpStatusCode["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; HttpStatusCode[HttpStatusCode["RequestTimeout"] = 408] = "RequestTimeout"; HttpStatusCode[HttpStatusCode["Conflict"] = 409] = "Conflict"; HttpStatusCode[HttpStatusCode["Gone"] = 410] = "Gone"; HttpStatusCode[HttpStatusCode["LengthRequired"] = 411] = "LengthRequired"; HttpStatusCode[HttpStatusCode["PreconditionFailed"] = 412] = "PreconditionFailed"; HttpStatusCode[HttpStatusCode["PayloadTooLarge"] = 413] = "PayloadTooLarge"; HttpStatusCode[HttpStatusCode["UriTooLong"] = 414] = "UriTooLong"; HttpStatusCode[HttpStatusCode["UnsupportedMediaType"] = 415] = "UnsupportedMediaType"; HttpStatusCode[HttpStatusCode["RangeNotSatisfiable"] = 416] = "RangeNotSatisfiable"; HttpStatusCode[HttpStatusCode["ExpectationFailed"] = 417] = "ExpectationFailed"; HttpStatusCode[HttpStatusCode["ImATeapot"] = 418] = "ImATeapot"; HttpStatusCode[HttpStatusCode["MisdirectedRequest"] = 421] = "MisdirectedRequest"; HttpStatusCode[HttpStatusCode["UnprocessableEntity"] = 422] = "UnprocessableEntity"; HttpStatusCode[HttpStatusCode["Locked"] = 423] = "Locked"; HttpStatusCode[HttpStatusCode["FailedDependency"] = 424] = "FailedDependency"; HttpStatusCode[HttpStatusCode["TooEarly"] = 425] = "TooEarly"; HttpStatusCode[HttpStatusCode["UpgradeRequired"] = 426] = "UpgradeRequired"; HttpStatusCode[HttpStatusCode["PreconditionRequired"] = 428] = "PreconditionRequired"; HttpStatusCode[HttpStatusCode["TooManyRequests"] = 429] = "TooManyRequests"; HttpStatusCode[HttpStatusCode["RequestHeaderFieldsTooLarge"] = 431] = "RequestHeaderFieldsTooLarge"; HttpStatusCode[HttpStatusCode["UnavailableForLegalReasons"] = 451] = "UnavailableForLegalReasons"; HttpStatusCode[HttpStatusCode["InternalServerError"] = 500] = "InternalServerError"; HttpStatusCode[HttpStatusCode["NotImplemented"] = 501] = "NotImplemented"; HttpStatusCode[HttpStatusCode["BadGateway"] = 502] = "BadGateway"; HttpStatusCode[HttpStatusCode["ServiceUnavailable"] = 503] = "ServiceUnavailable"; HttpStatusCode[HttpStatusCode["GatewayTimeout"] = 504] = "GatewayTimeout"; HttpStatusCode[HttpStatusCode["HttpVersionNotSupported"] = 505] = "HttpVersionNotSupported"; HttpStatusCode[HttpStatusCode["VariantAlsoNegotiates"] = 506] = "VariantAlsoNegotiates"; HttpStatusCode[HttpStatusCode["InsufficientStorage"] = 507] = "InsufficientStorage"; HttpStatusCode[HttpStatusCode["LoopDetected"] = 508] = "LoopDetected"; HttpStatusCode[HttpStatusCode["NotExtended"] = 510] = "NotExtended"; HttpStatusCode[HttpStatusCode["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired"; })(HttpStatusCode || (HttpStatusCode = {})); /** * Constructs an instance of `HttpRequestOptions<T>` from a source `HttpMethodOptions` and * the given `body`. This function clones the object and adds the body. * * Note that the `responseType` *options* value is a String that identifies the * single data type of the response. * A single overload version of the method handles each response type. * The value of `responseType` cannot be a union, as the combined signature could imply. * */ function addBody(options, body) { return { body, headers: options.headers, context: options.context, observe: options.observe, params: options.params, reportProgress: options.reportProgress, responseType: options.responseType, withCredentials: options.withCredentials, transferCache: options.transferCache, }; } /** * Performs HTTP requests. * This service is available as an injectable class, with methods to perform HTTP requests. * Each request method has multiple signatures, and the return type varies based on * the signature that is called (mainly the values of `observe` and `responseType`). * * Note that the `responseType` *options* value is a String that identifies the * single data type of the response. * A single overload version of the method handles each response type. * The value of `responseType` cannot be a union, as the combined signature could imply. * * @usageNotes * * ### HTTP Request Example * * ```ts * // GET heroes whose name contains search term * searchHeroes(term: string): observable<Hero[]>{ * * const params = new HttpParams({fromString: 'name=term'}); * return this.httpClient.request('GET', this.heroesUrl, {responseType:'json', params}); * } * ``` * * Alternatively, the parameter string can be used without invoking HttpParams * by directly joining to the URL. * ```ts * this.httpClient.request('GET', this.heroesUrl + '?' + 'name=term', {responseType:'json'}); * ``` * * * ### JSONP Example * ```ts * requestJsonp(url, callback = 'callback') { * return this.httpClient.jsonp(this.heroesURL, callback); * } * ``` * * ### PATCH Example * ```ts * // PATCH one of the heroes' name * patchHero (id: number, heroName: string): Observable<{}> { * const url = `${this.heroesUrl}/${id}`; // PATCH api/heroes/42 * return this.httpClient.patch(url, {name: heroName}, httpOptions) * .pipe(catchError(this.handleError('patchHero'))); * } * ``` * * @see [HTTP Guide](guide/http) * @see [HTTP Request](api/common/http/HttpRequest) * * @publicApi */ class HttpClient { handler; constructor(handler) { this.handler = handler; } /** * Constructs an observable for a generic HTTP request that, when subscribed, * fires the request through the chain of registered interceptors and on to the * server. * * You can pass an `HttpRequest` directly as the only parameter. In this case, * the call returns an observable of the raw `HttpEvent` stream. * * Alternatively you can pass an HTTP method as the first parameter, * a URL string as the second, and an options hash containing the request body as the third. * See `addBody()`. In this case, the specified `responseType` and `observe` options determine the * type of returned observable. * * The `responseType` value determines how a successful response body is parsed.