UNPKG

@angular/common

Version:

Angular - commonly needed directives and services

1 lines 66.3 kB
{"version":3,"file":"http.mjs","sources":["../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/common/http/src/transfer_cache.ts","../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/common/http/src/resource.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n APP_BOOTSTRAP_LISTENER,\n ApplicationRef,\n inject,\n InjectionToken,\n makeStateKey,\n Provider,\n StateKey,\n TransferState,\n ɵformatRuntimeError as formatRuntimeError,\n ɵperformanceMarkFeature as performanceMarkFeature,\n ɵtruncateMiddle as truncateMiddle,\n ɵRuntimeError as RuntimeError,\n} from '@angular/core';\nimport {Observable, of} from 'rxjs';\nimport {tap} from 'rxjs/operators';\n\nimport {RuntimeErrorCode} from './errors';\nimport {HttpHeaders} from './headers';\nimport {HTTP_ROOT_INTERCEPTOR_FNS, HttpHandlerFn} from './interceptor';\nimport {HttpRequest} from './request';\nimport {HttpEvent, HttpResponse} from './response';\nimport {HttpParams} from './params';\n\n/**\n * Options to configure how TransferCache should be used to cache requests made via HttpClient.\n *\n * @param includeHeaders Specifies which headers should be included into cached responses. No\n * headers are included by default.\n * @param filter A function that receives a request as an argument and returns a boolean to indicate\n * whether a request should be included into the cache.\n * @param includePostRequests Enables caching for POST requests. By default, only GET and HEAD\n * requests are cached. This option can be enabled if POST requests are used to retrieve data\n * (for example using GraphQL).\n * @param includeRequestsWithAuthHeaders Enables caching of requests containing `Authorization`,\n * `Proxy-Authorization`, or `Cookie` headers. By default, these requests are excluded from\n * caching. Requests sent using `withCredentials` are also excluded by default.\n *\n * @see [Configuring the caching options](guide/ssr#configuring-the-caching-options)\n *\n * @publicApi\n */\nexport type HttpTransferCacheOptions = {\n includeHeaders?: string[];\n filter?: (req: HttpRequest<unknown>) => boolean;\n includePostRequests?: boolean;\n includeRequestsWithAuthHeaders?: boolean;\n};\n\n/**\n * If your application uses different HTTP origins to make API calls (via `HttpClient`) on the server and\n * on the client, the `HTTP_TRANSFER_CACHE_ORIGIN_MAP` token allows you to establish a mapping\n * between those origins, so that `HttpTransferCache` feature can recognize those requests as the same\n * ones and reuse the data cached on the server during hydration on the client.\n *\n * IMPORTANT: The `HTTP_TRANSFER_CACHE_ORIGIN_MAP` token should *only* be provided in\n * the *server* code of your application (typically in the `app.server.config.ts` script). Angular throws an\n * error if it detects that the token is defined while running on the client.\n *\n * @usageNotes\n *\n * When the same API endpoint is accessed via `http://internal-domain.com:8080` on the server and\n * via `https://external-domain.com` on the client, you can use the following configuration:\n * ```ts\n * // in app.server.config.ts\n * {\n * provide: HTTP_TRANSFER_CACHE_ORIGIN_MAP,\n * useValue: {\n * 'http://internal-domain.com:8080': 'https://external-domain.com'\n * }\n * }\n * ```\n *\n * @publicApi\n */\nexport const HTTP_TRANSFER_CACHE_ORIGIN_MAP = new InjectionToken<Record<string, string>>(\n typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_TRANSFER_CACHE_ORIGIN_MAP' : '',\n);\n\n/**\n * Keys within cached response data structure.\n */\n\nexport const BODY = 'b';\nexport const HEADERS = 'h';\nexport const STATUS = 's';\nexport const STATUS_TEXT = 'st';\nexport const REQ_URL = 'u';\nexport const RESPONSE_TYPE = 'rt';\n\ninterface TransferHttpResponse {\n /** body */\n [BODY]: any;\n /** headers */\n [HEADERS]: Record<string, string[]>;\n /** status */\n [STATUS]: number;\n /** statusText */\n [STATUS_TEXT]: string;\n /** url */\n [REQ_URL]: string;\n /** responseType */\n [RESPONSE_TYPE]: HttpRequest<unknown>['responseType'];\n}\n\ninterface CacheOptions extends HttpTransferCacheOptions {\n isCacheActive: boolean;\n}\n\nexport const CACHE_OPTIONS = new InjectionToken<CacheOptions>(\n typeof ngDevMode !== 'undefined' && ngDevMode ? 'HTTP_TRANSFER_STATE_CACHE_OPTIONS' : '',\n);\n\n/**\n * A list of allowed HTTP methods to cache.\n */\nconst ALLOWED_METHODS = ['GET', 'HEAD'];\n\nfunction canUseOrCacheRequest(req: HttpRequest<unknown>, options: CacheOptions): boolean {\n const {isCacheActive, ...globalOptions} = options;\n const {transferCache: requestOptions, method: requestMethod} = req;\n\n if (\n !isCacheActive ||\n requestOptions === false ||\n // Do not cache requests sent with credentials.\n req.withCredentials ||\n // POST requests are allowed either globally or at request level\n (requestMethod === 'POST' && !globalOptions.includePostRequests && !requestOptions) ||\n (requestMethod !== 'POST' && !ALLOWED_METHODS.includes(requestMethod)) ||\n // Do not cache requests with authentication or cookie headers unless explicitly enabled.\n (!globalOptions.includeRequestsWithAuthHeaders && hasAuthHeaders(req)) ||\n globalOptions.filter?.(req) === false\n ) {\n return false;\n }\n\n return true;\n}\n\nfunction getHeadersToInclude(\n options: CacheOptions,\n requestOptions: HttpTransferCacheOptions | boolean | undefined,\n): string[] | undefined {\n // Request-specific config takes precedence over the global config.\n return typeof requestOptions === 'object' && requestOptions.includeHeaders\n ? requestOptions.includeHeaders\n : options.includeHeaders;\n}\n\n/**\n * Retrieves the cached response for a given request.\n * @param req The request to retrieve the cached response for.\n * @param options The caching options.\n * @param transferState The transfer state to retrieve the cached response from.\n * @param originMap The origin map to map the request URL to the origin. (Not needed when `storeKey` is provided).\n * @param storeKey The key to use to store the cached response in the transfer state. (If not provided, it will be computed from the request and originMap).\n * @param skipUseCacheChecks Whether to skip the use cache checks. (Only disable when the checks have been performed beforehand).\n */\nexport function retrieveStateFromCache(\n req: HttpRequest<unknown>,\n options: CacheOptions,\n transferState: TransferState,\n originMap: Record<string, string> | null,\n storeKey?: StateKey<TransferHttpResponse>,\n skipUseCacheChecks = false,\n): HttpResponse<unknown> | null {\n if (!skipUseCacheChecks && !canUseOrCacheRequest(req, options)) {\n return null;\n }\n\n if (typeof ngServerMode !== 'undefined' && !ngServerMode && originMap) {\n throw new RuntimeError(\n RuntimeErrorCode.HTTP_ORIGIN_MAP_USED_IN_CLIENT,\n ngDevMode &&\n 'Angular detected that the `HTTP_TRANSFER_CACHE_ORIGIN_MAP` token is configured and ' +\n 'present in the client side code. Please ensure that this token is only provided in the ' +\n 'server code of the application.',\n );\n }\n\n if (!storeKey) {\n const requestUrl =\n typeof ngServerMode !== 'undefined' && ngServerMode && originMap\n ? mapRequestOriginUrl(req.url, originMap)\n : req.url;\n\n storeKey = makeCacheKey(req, requestUrl);\n }\n\n const response = transferState.get(storeKey, null);\n\n if (!response) {\n return null;\n }\n\n const {\n [BODY]: undecodedBody,\n [RESPONSE_TYPE]: responseType,\n [HEADERS]: httpHeaders,\n [STATUS]: status,\n [STATUS_TEXT]: statusText,\n [REQ_URL]: url,\n } = response;\n // Request found in cache. Respond using it.\n let body: ArrayBuffer | Blob | string | undefined = undecodedBody;\n\n switch (responseType) {\n case 'arraybuffer':\n body = fromBase64(undecodedBody);\n break;\n case 'blob':\n body = new Blob([fromBase64(undecodedBody)]);\n break;\n }\n\n // We want to warn users accessing a header provided from the cache\n // That HttpTransferCache alters the headers\n // The warning will be logged a single time by HttpHeaders instance\n let headers = new HttpHeaders(httpHeaders);\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n // Append extra logic in dev mode to produce a warning when a header\n // that was not transferred to the client is accessed in the code via `get`\n // and `has` calls.\n const {transferCache: requestOptions} = req;\n const headersToInclude = getHeadersToInclude(options, requestOptions);\n headers = appendMissingHeadersDetection(req.url, headers, headersToInclude ?? []);\n }\n\n return new HttpResponse({\n body,\n headers,\n status,\n statusText,\n url,\n });\n}\n\nexport function transferCacheInterceptorFn(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn,\n): Observable<HttpEvent<unknown>> {\n const options = inject(CACHE_OPTIONS);\n if (!canUseOrCacheRequest(req, options)) {\n return next(req);\n }\n\n const transferState = inject(TransferState);\n const originMap = inject(HTTP_TRANSFER_CACHE_ORIGIN_MAP, {optional: true});\n const requestUrl =\n typeof ngServerMode !== 'undefined' && ngServerMode && originMap\n ? mapRequestOriginUrl(req.url, originMap)\n : req.url;\n const storeKey = makeCacheKey(req, requestUrl);\n\n const cachedResponse = retrieveStateFromCache(\n req,\n options,\n transferState,\n /** originMap */ null,\n storeKey,\n /** skipUseCacheChecks */ true,\n );\n\n if (cachedResponse) {\n return of(cachedResponse);\n }\n\n const event$ = next(req);\n if (typeof ngServerMode !== 'undefined' && ngServerMode) {\n // Request not found in cache. Make the request and cache it if on the server.\n return event$.pipe(\n tap((event: HttpEvent<unknown>) => {\n if (event instanceof HttpResponse) {\n const {headers, body, status, statusText} = event;\n\n // Only cache successful HTTP responses that do not have Cache-Control\n // directives that forbid shared caching (no-store or private).\n if (hasUncacheableCacheControl(headers)) {\n return;\n }\n\n const {transferCache: requestOptions, responseType} = req;\n const headersToInclude = getHeadersToInclude(options, requestOptions);\n\n transferState.set<TransferHttpResponse>(storeKey, {\n [BODY]:\n responseType === 'arraybuffer' || responseType === 'blob' ? toBase64(body) : body,\n [HEADERS]: getFilteredHeaders(headers, headersToInclude),\n [STATUS]: status,\n [STATUS_TEXT]: statusText,\n [REQ_URL]: requestUrl,\n [RESPONSE_TYPE]: responseType,\n });\n }\n }),\n );\n }\n\n return event$;\n}\n\n/** @returns true when the request contains authentication or cookie headers. */\nfunction hasAuthHeaders(req: HttpRequest<unknown>): boolean {\n const headers = req.headers;\n\n return (\n headers.has('authorization') || headers.has('proxy-authorization') || headers.has('cookie')\n );\n}\n\nconst UNCACHEABLE_CACHE_CONTROL_DIRECTIVES = new Set(['no-store', 'private', 'no-cache']);\n\nfunction hasUncacheableCacheControl(headers: HttpHeaders): boolean {\n const cacheControl = headers.get('cache-control');\n\n if (!cacheControl) {\n return false;\n }\n\n return cacheControl.split(',').some((directive) => {\n const directiveName = directive.split('=', 1)[0].trim().toLowerCase();\n\n return UNCACHEABLE_CACHE_CONTROL_DIRECTIVES.has(directiveName);\n });\n}\n\nfunction isNonCacheableRequest(cache: RequestCache): boolean {\n return cache === 'no-cache' || cache === 'no-store';\n}\n\nfunction hasOutgoingCredentials(req: HttpRequest<unknown>): boolean {\n const {withCredentials, credentials} = req;\n\n return withCredentials || credentials === 'include' || credentials === 'same-origin';\n}\nfunction getFilteredHeaders(\n headers: HttpHeaders,\n includeHeaders: string[] | undefined,\n): Record<string, string[]> {\n if (!includeHeaders) {\n return {};\n }\n\n const headersMap: Record<string, string[]> = {};\n for (const key of includeHeaders) {\n const values = headers.getAll(key);\n if (values !== null) {\n headersMap[key] = values;\n }\n }\n\n return headersMap;\n}\n\nfunction sortAndConcatParams(params: HttpParams | URLSearchParams): string {\n return [...params.keys()]\n .sort()\n .map((k) => `${k}=${params.getAll(k)}`)\n .join('&');\n}\n\nfunction makeCacheKey(\n request: HttpRequest<any>,\n mappedRequestUrl: string,\n): StateKey<TransferHttpResponse> {\n // make the params encoded same as a url so it's easy to identify\n const {params, method, responseType} = request;\n const encodedParams = sortAndConcatParams(params);\n\n let serializedBody = request.serializeBody();\n if (serializedBody instanceof URLSearchParams) {\n serializedBody = sortAndConcatParams(serializedBody);\n } else if (typeof serializedBody !== 'string') {\n serializedBody = '';\n }\n\n const key = [method, responseType, mappedRequestUrl, serializedBody, encodedParams].join('|');\n const hash = generateHash(key);\n\n return makeStateKey(hash);\n}\n\nfunction toBase64(buffer: unknown): string {\n //TODO: replace with when is Baseline widely available\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64\n const bytes = new Uint8Array(buffer as ArrayBufferLike);\n\n const CHUNK_SIZE = 0x8000; // 32,768 bytes (~32 KB) per chunk, to avoid stack overflow\n\n let binaryString = '';\n\n for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + CHUNK_SIZE);\n binaryString += String.fromCharCode.apply(null, chunk as unknown as number[]);\n }\n return btoa(binaryString);\n}\n\nfunction fromBase64(base64: string): ArrayBuffer {\n const binary = atob(base64);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return bytes.buffer;\n}\n\n/**\n * Returns the DI providers needed to enable HTTP transfer cache.\n *\n * By default, when using server rendering, requests are performed twice: once on the server and\n * other one on the browser.\n *\n * When these providers are added, requests performed on the server are cached and reused during the\n * bootstrapping of the application in the browser thus avoiding duplicate requests and reducing\n * load time.\n *\n * @see [Caching data when using HttpClient](guide/ssr#configuring-the-caching-options)\n *\n */\nexport function withHttpTransferCache(cacheOptions: HttpTransferCacheOptions): Provider[] {\n return [\n {\n provide: CACHE_OPTIONS,\n useFactory: (): CacheOptions => {\n performanceMarkFeature('NgHttpTransferCache');\n return {isCacheActive: true, ...cacheOptions};\n },\n },\n {\n provide: HTTP_ROOT_INTERCEPTOR_FNS,\n useValue: transferCacheInterceptorFn,\n multi: true,\n },\n {\n provide: APP_BOOTSTRAP_LISTENER,\n multi: true,\n useFactory: () => {\n const appRef = inject(ApplicationRef);\n const cacheState = inject(CACHE_OPTIONS);\n\n return () => {\n appRef.whenStable().then(() => {\n cacheState.isCacheActive = false;\n });\n };\n },\n },\n ];\n}\n\n/**\n * This function will add a proxy to an HttpHeader to intercept calls to get/has\n * and log a warning if the header entry requested has been removed\n */\nfunction appendMissingHeadersDetection(\n url: string,\n headers: HttpHeaders,\n headersToInclude: string[],\n): HttpHeaders {\n const warningProduced = new Set();\n return new Proxy<HttpHeaders>(headers, {\n get(target: HttpHeaders, prop: keyof HttpHeaders): unknown {\n const value = Reflect.get(target, prop);\n const methods: Set<keyof HttpHeaders> = new Set(['get', 'has', 'getAll']);\n\n if (typeof value !== 'function' || !methods.has(prop)) {\n return value;\n }\n\n return (headerName: string) => {\n // We log when the key has been removed and a warning hasn't been produced for the header\n const key = (prop + ':' + headerName).toLowerCase(); // e.g. `get:cache-control`\n if (!headersToInclude.includes(headerName) && !warningProduced.has(key)) {\n warningProduced.add(key);\n const truncatedUrl = truncateMiddle(url);\n\n console.warn(\n formatRuntimeError(\n RuntimeErrorCode.HEADERS_ALTERED_BY_TRANSFER_CACHE,\n `Angular detected that the \\`${headerName}\\` header is accessed, but the value of the header ` +\n `was not transferred from the server to the client by the HttpTransferCache. ` +\n `To include the value of the \\`${headerName}\\` header for the \\`${truncatedUrl}\\` request, ` +\n `use the \\`includeHeaders\\` list. The \\`includeHeaders\\` can be defined either ` +\n `on a request level by adding the \\`transferCache\\` parameter, or on an application ` +\n `level by adding the \\`httpCacheTransfer.includeHeaders\\` argument to the ` +\n `\\`provideClientHydration()\\` call. `,\n ),\n );\n }\n\n // invoking the original method\n return (value as Function).apply(target, [headerName]);\n };\n },\n });\n}\n\nfunction mapRequestOriginUrl(url: string, originMap: Record<string, string>): string {\n const origin = new URL(url, 'resolve://').origin;\n const mappedOrigin = originMap[origin];\n if (!mappedOrigin) {\n return url;\n }\n\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n verifyMappedOrigin(mappedOrigin);\n }\n\n return url.replace(origin, mappedOrigin);\n}\n\nfunction verifyMappedOrigin(url: string): void {\n if (new URL(url, 'resolve://').pathname !== '/') {\n throw new RuntimeError(\n RuntimeErrorCode.HTTP_ORIGIN_MAP_CONTAINS_PATH,\n 'Angular detected a URL with a path segment in the value provided for the ' +\n `\\`HTTP_TRANSFER_CACHE_ORIGIN_MAP\\` token: ${url}. The map should only contain origins ` +\n 'without any other segments.',\n );\n }\n}\n\n/**\n * SHA-256 Constants (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):\n */\nconst SHA256_ROUND_CONSTANTS = /* @__PURE__ */ new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nlet textEncoder: TextEncoder | undefined;\n\n/**\n * Generates a SHA-256 hash representation of a string.\n *\n * Note: A custom synchronous SHA-256 implementation is used here because the Web Crypto API\n * (`crypto.subtle.digest`) is strictly asynchronous (Promise-based), whereas the transfer cache\n * state lookup and interceptor flow must operate synchronously due to the HttpResource API.\n *\n * The previous DJB2 hashing logic was vulnerable to pre-image and second-preimage attacks due to\n * its small 64-bit keyspace and mathematical simplicity. An attacker could craft colliding request\n * inputs to poison the cache, potentially causing a CDN or the application to serve the wrong\n * cached response to legitimate users. SHA-256 provides strong cryptographic collision resistance,\n * preventing cache key collision attacks.\n */\nexport function generateHash(value: string): string {\n textEncoder ??= new TextEncoder();\n const inputBytes = textEncoder.encode(value);\n\n // Initial hash values (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):\n let hashState0 = 0x6a09e667;\n let hashState1 = 0xbb67ae85;\n let hashState2 = 0x3c6ef372;\n let hashState3 = 0xa54ff53a;\n let hashState4 = 0x510e527f;\n let hashState5 = 0x9b05688c;\n let hashState6 = 0x1f83d9ab;\n let hashState7 = 0x5be0cd19;\n\n // Pre-processing (Padding):\n const messageLengthInBits = inputBytes.length * 8;\n\n // The total length of the padded message must be a multiple of 64 bytes (512 bits)\n const paddedLengthInBytes = (((inputBytes.length + 8) >> 6) + 1) << 6;\n const paddedBytes = new Uint8Array(paddedLengthInBytes);\n paddedBytes.set(inputBytes);\n paddedBytes[inputBytes.length] = 0x80; // Append a single '1' bit (0x80 byte)\n\n const paddedBytesView = new DataView(paddedBytes.buffer);\n const lowBits = messageLengthInBits >>> 0;\n const highBits = (messageLengthInBits / 0x100000000) >>> 0;\n paddedBytesView.setUint32(paddedLengthInBytes - 8, highBits, false);\n paddedBytesView.setUint32(paddedLengthInBytes - 4, lowBits, false);\n\n // Process the message in successive 64-byte chunks:\n const messageSchedule = new Uint32Array(64);\n for (let chunkOffset = 0; chunkOffset < paddedLengthInBytes; chunkOffset += 64) {\n // Initialize first 16 words of the message schedule:\n for (let i = 0; i < 16; i++) {\n messageSchedule[i] = paddedBytesView.getUint32(chunkOffset + i * 4, false);\n }\n\n // Extend to 64 words:\n for (let i = 16; i < 64; i++) {\n const prevWord15 = messageSchedule[i - 15];\n const sigma0 =\n (((prevWord15 >>> 7) | (prevWord15 << 25)) ^\n ((prevWord15 >>> 18) | (prevWord15 << 14)) ^\n (prevWord15 >>> 3)) >>>\n 0;\n\n const prevWord2 = messageSchedule[i - 2];\n const sigma1 =\n (((prevWord2 >>> 17) | (prevWord2 << 15)) ^\n ((prevWord2 >>> 19) | (prevWord2 << 13)) ^\n (prevWord2 >>> 10)) >>>\n 0;\n\n messageSchedule[i] =\n (messageSchedule[i - 16] + sigma0 + messageSchedule[i - 7] + sigma1) >>> 0;\n }\n\n // Initialize working variables to current hash values:\n let workingStateA = hashState0;\n let workingStateB = hashState1;\n let workingStateC = hashState2;\n let workingStateD = hashState3;\n let workingStateE = hashState4;\n let workingStateF = hashState5;\n let workingStateG = hashState6;\n let workingStateH = hashState7;\n\n // Compression function main loop:\n for (let i = 0; i < 64; i++) {\n const capitalSigma1 =\n (((workingStateE >>> 6) | (workingStateE << 26)) ^\n ((workingStateE >>> 11) | (workingStateE << 21)) ^\n ((workingStateE >>> 25) | (workingStateE << 7))) >>>\n 0;\n const chFunction = ((workingStateE & workingStateF) ^ (~workingStateE & workingStateG)) >>> 0;\n const temp1 =\n (workingStateH +\n capitalSigma1 +\n chFunction +\n SHA256_ROUND_CONSTANTS[i] +\n messageSchedule[i]) >>>\n 0;\n\n const capitalSigma0 =\n (((workingStateA >>> 2) | (workingStateA << 30)) ^\n ((workingStateA >>> 13) | (workingStateA << 19)) ^\n ((workingStateA >>> 22) | (workingStateA << 10))) >>>\n 0;\n const majFunction =\n ((workingStateA & workingStateB) ^\n (workingStateA & workingStateC) ^\n (workingStateB & workingStateC)) >>>\n 0;\n const temp2 = (capitalSigma0 + majFunction) >>> 0;\n\n workingStateH = workingStateG;\n workingStateG = workingStateF;\n workingStateF = workingStateE;\n workingStateE = (workingStateD + temp1) >>> 0;\n workingStateD = workingStateC;\n workingStateC = workingStateB;\n workingStateB = workingStateA;\n workingStateA = (temp1 + temp2) >>> 0;\n }\n\n // Update intermediate hash state:\n hashState0 = (hashState0 + workingStateA) >>> 0;\n hashState1 = (hashState1 + workingStateB) >>> 0;\n hashState2 = (hashState2 + workingStateC) >>> 0;\n hashState3 = (hashState3 + workingStateD) >>> 0;\n hashState4 = (hashState4 + workingStateE) >>> 0;\n hashState5 = (hashState5 + workingStateF) >>> 0;\n hashState6 = (hashState6 + workingStateG) >>> 0;\n hashState7 = (hashState7 + workingStateH) >>> 0;\n }\n\n // Produce the final 64-character hexadecimal hash:\n return [\n hashState0,\n hashState1,\n hashState2,\n hashState3,\n hashState4,\n hashState5,\n hashState6,\n hashState7,\n ]\n .map((x) => x.toString(16).padStart(8, '0'))\n .join('');\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n assertInInjectionContext,\n computed,\n ɵencapsulateResourceError as encapsulateResourceError,\n inject,\n Injector,\n linkedSignal,\n ɵResourceImpl as ResourceImpl,\n type ResourceParamsContext,\n ResourceStreamItem,\n Signal,\n signal,\n TransferState,\n type ValueEqualityFn,\n ɵRuntimeError,\n ɵRuntimeErrorCode,\n} from '@angular/core';\nimport type {Subscription} from 'rxjs';\n\nimport {HttpClient} from './client';\nimport {HttpHeaders} from './headers';\nimport {HttpParams} from './params';\nimport {HttpRequest} from './request';\nimport {HttpResourceOptions, HttpResourceRef, HttpResourceRequest} from './resource_api';\nimport {HttpErrorResponse, HttpEventType, HttpProgressEvent} from './response';\nimport {\n CACHE_OPTIONS,\n HTTP_TRANSFER_CACHE_ORIGIN_MAP,\n retrieveStateFromCache,\n} from './transfer_cache';\n\n/**\n * Type for the `httpRequest` top-level function, which includes the call signatures for the JSON-\n * based `httpRequest` as well as sub-functions for `ArrayBuffer`, `Blob`, and `string` type\n * requests.\n *\n * @publicApi 22.0\n */\nexport interface HttpResourceFn {\n /**\n * Create a `Resource` that fetches data with an HTTP GET request to the given URL.\n *\n * The resource will update when the URL changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed as JSON by default - use a sub-function of\n * `httpResource`, such as `httpResource.text()`, to parse the response differently.\n *\n * @publicApi 22.0\n */\n <TResult = unknown>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options: HttpResourceOptions<TResult, unknown> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n /**\n * Create a `Resource` that fetches data with an HTTP GET request to the given URL.\n *\n * The resource will update when the URL changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed as JSON by default - use a sub-function of\n * `httpResource`, such as `httpResource.text()`, to parse the response differently.\n *\n * @publicApi 22.0\n */\n <TResult = unknown>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options?: HttpResourceOptions<TResult, unknown>,\n ): HttpResourceRef<TResult | undefined>;\n\n /**\n * Create a `Resource` that fetches data with the configured HTTP request.\n *\n * The resource will update when the request changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed as JSON by default - use a sub-function of\n * `httpResource`, such as `httpResource.text()`, to parse the response differently.\n *\n * @publicApi 22.0\n */\n <TResult = unknown>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options: HttpResourceOptions<TResult, unknown> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n /**\n * Create a `Resource` that fetches data with the configured HTTP request.\n *\n * The resource will update when the request changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed as JSON by default - use a sub-function of\n * `httpResource`, such as `httpResource.text()`, to parse the response differently.\n *\n * @publicApi 22.0\n */\n <TResult = unknown>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options?: HttpResourceOptions<TResult, unknown>,\n ): HttpResourceRef<TResult | undefined>;\n\n /**\n * Create a `Resource` that fetches data with the configured HTTP request.\n *\n * The resource will update when the URL or request changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed into an `ArrayBuffer`.\n *\n * @publicApi 22.0\n */\n arrayBuffer: {\n <TResult = ArrayBuffer>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options: HttpResourceOptions<TResult, ArrayBuffer> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n <TResult = ArrayBuffer>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options?: HttpResourceOptions<TResult, ArrayBuffer>,\n ): HttpResourceRef<TResult | undefined>;\n\n <TResult = ArrayBuffer>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options: HttpResourceOptions<TResult, ArrayBuffer> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n <TResult = ArrayBuffer>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options?: HttpResourceOptions<TResult, ArrayBuffer>,\n ): HttpResourceRef<TResult | undefined>;\n };\n\n /**\n * Create a `Resource` that fetches data with the configured HTTP request.\n *\n * The resource will update when the URL or request changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed into a `Blob`.\n *\n * @publicApi 22.0\n */\n blob: {\n <TResult = Blob>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options: HttpResourceOptions<TResult, Blob> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n <TResult = Blob>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options?: HttpResourceOptions<TResult, Blob>,\n ): HttpResourceRef<TResult | undefined>;\n\n <TResult = Blob>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options: HttpResourceOptions<TResult, Blob> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n <TResult = Blob>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options?: HttpResourceOptions<TResult, Blob>,\n ): HttpResourceRef<TResult | undefined>;\n };\n\n /**\n * Create a `Resource` that fetches data with the configured HTTP request.\n *\n * The resource will update when the URL or request changes via signals.\n *\n * Uses `HttpClient` to make requests and supports interceptors, testing, and the other features\n * of the `HttpClient` API. Data is parsed as a `string`.\n *\n * @publicApi 22.0\n */\n text: {\n <TResult = string>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options: HttpResourceOptions<TResult, string> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n <TResult = string>(\n url: (ctx: ResourceParamsContext) => string | undefined,\n options?: HttpResourceOptions<TResult, string>,\n ): HttpResourceRef<TResult | undefined>;\n\n <TResult = string>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options: HttpResourceOptions<TResult, string> & {defaultValue: NoInfer<TResult>},\n ): HttpResourceRef<TResult>;\n\n <TResult = string>(\n request: (ctx: ResourceParamsContext) => HttpResourceRequest | undefined,\n options?: HttpResourceOptions<TResult, string>,\n ): HttpResourceRef<TResult | undefined>;\n };\n}\n\n/**\n * `httpResource` makes a reactive HTTP request and exposes the request status and response value as\n * a `WritableResource`. By default, it assumes that the backend will return JSON data. To make a\n * request that expects a different kind of data, you can use a sub-constructor of `httpResource`,\n * such as `httpResource.text`.\n *\n * @publicApi 22.0\n * @initializerApiFunction\n */\nexport const httpResource: HttpResourceFn = (() => {\n const jsonFn = makeHttpResourceFn<unknown>('json') as HttpResourceFn;\n jsonFn.arrayBuffer = makeHttpResourceFn<ArrayBuffer>('arraybuffer');\n jsonFn.blob = makeHttpResourceFn('blob');\n jsonFn.text = makeHttpResourceFn('text');\n return jsonFn;\n})();\n\n/**\n * The expected response type of the server.\n *\n * This is used to parse the response appropriately before returning it to\n * the requestee.\n */\ntype ResponseType = 'arraybuffer' | 'blob' | 'json' | 'text';\ntype RawRequestType =\n | ((ctx: ResourceParamsContext) => string | undefined)\n | ((ctx: ResourceParamsContext) => HttpResourceRequest | undefined);\n\nfunction makeHttpResourceFn<TRaw>(responseType: ResponseType) {\n return function httpResource<TResult = TRaw>(\n request: RawRequestType,\n options?: HttpResourceOptions<TResult, TRaw>,\n ): HttpResourceRef<TResult> {\n if (ngDevMode && !options?.injector) {\n assertInInjectionContext(httpResource);\n }\n const injector = options?.injector ?? inject(Injector);\n\n const cacheOptions = injector.get(CACHE_OPTIONS, null, {optional: true});\n const transferState = injector.get(TransferState, null, {optional: true});\n const originMap = injector.get(HTTP_TRANSFER_CACHE_ORIGIN_MAP, null, {optional: true});\n\n const getInitialStream = (req: HttpRequest<unknown> | undefined) => {\n if (cacheOptions && transferState && req) {\n const cachedResponse = retrieveStateFromCache(req, cacheOptions, transferState, originMap);\n if (cachedResponse) {\n try {\n const body = cachedResponse.body as TRaw;\n const parsed = options?.parse ? options.parse(body) : (body as unknown as TResult);\n return signal({value: parsed});\n } catch (e) {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n console.warn(\n `Angular detected an error while parsing the cached response for the httpResource at \\`${req.url}\\`. ` +\n `The resource will fall back to its default value and try again asynchronously.`,\n e,\n );\n }\n }\n }\n }\n return undefined;\n };\n\n return new HttpResourceImpl(\n injector,\n (ctx: ResourceParamsContext) => normalizeRequest(ctx, request, responseType),\n options?.defaultValue,\n options?.debugName,\n options?.parse as (value: unknown) => TResult,\n options?.equal as ValueEqualityFn<unknown>,\n getInitialStream,\n ) as HttpResourceRef<TResult>;\n };\n}\n\nfunction normalizeRequest(\n ctx: ResourceParamsContext,\n request: RawRequestType,\n responseType: ResponseType,\n): HttpRequest<unknown> | undefined {\n let unwrappedRequest = typeof request === 'function' ? request(ctx) : request;\n if (unwrappedRequest === undefined) {\n return undefined;\n } else if (typeof unwrappedRequest === 'string') {\n unwrappedRequest = {url: unwrappedRequest};\n }\n\n const headers =\n unwrappedRequest.headers instanceof HttpHeaders\n ? unwrappedRequest.headers\n : new HttpHeaders(\n unwrappedRequest.headers as\n | Record<string, string | number | Array<string | number>>\n | undefined,\n );\n\n const params =\n unwrappedRequest.params instanceof HttpParams\n ? unwrappedRequest.params\n : new HttpParams({fromObject: unwrappedRequest.params});\n\n return new HttpRequest(\n unwrappedRequest.method ?? 'GET',\n unwrappedRequest.url,\n unwrappedRequest.body ?? null,\n {\n headers,\n params,\n reportProgress: unwrappedRequest.reportProgress,\n withCredentials: unwrappedRequest.withCredentials,\n keepalive: unwrappedRequest.keepalive,\n cache: unwrappedRequest.cache as RequestCache,\n priority: unwrappedRequest.priority as RequestPriority,\n mode: unwrappedRequest.mode as RequestMode,\n redirect: unwrappedRequest.redirect as RequestRedirect,\n responseType,\n context: unwrappedRequest.context,\n transferCache: unwrappedRequest.transferCache,\n credentials: unwrappedRequest.credentials as RequestCredentials,\n referrer: unwrappedRequest.referrer,\n referrerPolicy: unwrappedRequest.referrerPolicy as ReferrerPolicy,\n integrity: unwrappedRequest.integrity,\n timeout: unwrappedRequest.timeout,\n },\n );\n}\nclass HttpResourceImpl<T>\n extends ResourceImpl<T, HttpRequest<unknown> | undefined>\n implements HttpResourceRef<T>\n{\n private client!: HttpClient;\n private _headers = linkedSignal({\n source: this.extRequest,\n computation: () => undefined as HttpHeaders | undefined,\n });\n private _progress = linkedSignal({\n source: this.extRequest,\n computation: () => undefined as HttpProgressEvent | undefined,\n });\n private _statusCode = linkedSignal({\n source: this.extRequest,\n computation: () => undefined as number | undefined,\n });\n\n readonly headers = computed(() =>\n this.status() === 'resolved' || this.status() === 'error' ? this._headers() : undefined,\n );\n readonly progress = this._progress.asReadonly();\n readonly statusCode = this._statusCode.asReadonly();\n\n constructor(\n injector: Injector,\n request: (ctx: ResourceParamsContext) => HttpRequest<T> | undefined,\n defaultValue: T,\n debugName?: string,\n parse?: (value: unknown) => T,\n equal?: ValueEqualityFn<unknown>,\n getInitialStream?: (\n request: HttpRequest<unknown> | undefined,\n ) => Signal<ResourceStreamItem<T>> | undefined,\n ) {\n super(\n request,\n ({params: request, abortSignal}) => {\n let sub: Subscription | undefined;\n // In the unlikely case the request returns synchronously we want to make sure the observable\n // is subscribe even if it isn't initialized yet.\n let aborted = false;\n\n // Track the abort listener so it can be removed if the Observable completes (as a memory\n // optimization).\n const onAbort = () => {\n aborted = true;\n sub?.unsubscribe();\n };\n abortSignal.addEventListener('abort', onAbort);\n\n // Start off stream as undefined.\n const stream = signal<ResourceStreamItem<T>>({value: undefined as T});\n let resolve: ((value: Signal<ResourceStreamItem<T>>) => void) | undefined;\n const promise = new Promise<Signal<ResourceStreamItem<T>>>((r) => (resolve = r));\n\n const send = (value: ResourceStreamItem<T>): void => {\n stream.set(value);\n resolve?.(stream);\n resolve = undefined;\n };\n\n sub = this.client.request(request!).subscribe({\n next: (event) => {\n switch (event.type) {\n case HttpEventType.Response:\n this._headers.set(event.headers);\n this._statusCode.set(event.status);\n try {\n send({value: parse ? parse(event.body) : (event.body as T)});\n } catch (error) {\n send({error: encapsulateResourceError(error)});\n }\n break;\n case HttpEventType.DownloadProgress:\n this._progress.set(event);\n break;\n }\n },\n error: (error) => {\n if (error instanceof HttpErrorResponse) {\n this._headers.set(error.headers);\n this._statusCode.set(error.status);\n }\n\n send({error});\n abortSignal.removeEventListener('abort', onAbort);\n },\n complete: () => {\n if (resolve) {\n send({\n error: new ɵRuntimeError(\n ɵRuntimeErrorCode.RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE,\n ngDevMode && 'Resource completed before producing a value',\n ),\n });\n }\n abortSignal.removeEventListener('abort', onAbort);\n },\n });\n\n if (aborted) {\n sub.unsubscribe();\n }\n\n return promise;\n },\n defaultValue,\n equal,\n debugName,\n injector,\n undefined,\n getInitialStream,\n );\n this.client = injector.get(HttpClient);\n }\n\n override set(value: T): void {\n super.set(value);\n\n this._headers.set(undefined);\n this._progress.set(undefined);\n this._statusCode.set(undefined);\n }\n\n // This is a type only override of the method\n declare hasValue: () => this is HttpResourceRef<Exclude<T, undefined>>;\n}\n"],"names":["HTTP_TRANSFER_CACHE_ORIGIN_MAP","InjectionToken","ngDevMode","BODY","HEADERS","STATUS","STATUS_TEXT","REQ_URL","RESPONSE_TYPE","CACHE_OPTIONS","ALLOWED_METHODS","canUseOrCacheRequest","req","options","isCacheActive","globalOptions","transferCache","requestOptions","method","requestMethod","withCredentials","includePostRequests","includes","includeRequestsWithAuthHeaders","hasAuthHeaders","filter","getHeadersToInclude","includeHeaders","retrieveStateFromCache","transferState","originMap","storeKey","skipUseCacheChecks","ngServerMode","RuntimeError","requestUrl","mapRequestOriginUrl","url","makeCacheKey","response","get","undecodedBody","responseType","httpHeaders","status","statusText","body","fromBase64","Blob","headers","HttpHeaders","headersToInclude","appendMissingHeadersDetection","HttpResponse","transferCacheInterceptorFn","next","inject","TransferState","optional","cachedResponse","of","event$","pipe","tap","event","hasUncacheableCacheControl","set","toBase64","getFilteredHeaders","has","UNCACHEABLE_CACHE_CONTROL_DIRECTIVES","Set","cacheControl","split","some","directive","directiveName","trim","toLowerCase","headersMap","key","values","getAll","sortAndConcatParams","params","keys","sort","map","k","join","request","mappedRequestUrl","encodedParams","serializedBody","serializeBody","URLSearchParams","hash","generateHash","makeStateKey","buffer","bytes","Uint8Array","CHUNK_SIZE","binaryString","i","length","chunk","subarray","String","fromCharCode","apply","btoa","base64","binary","atob","from","c","charCodeAt","withHttpTransferCache","cacheOptions","provide","useFactory","performanceMarkFeature","HTTP_ROOT_INTERCEPTOR_FNS","useValue","multi","APP_BOOTSTRAP_LISTENER","appRef","ApplicationRef","cacheState","whenStable","then","warningProduced","Proxy","target","prop","value","Reflect","methods","headerName","add","truncatedUrl","truncateMiddle","console","warn","formatRuntimeError","origin","URL","mappedOrigin","verifyMappedOrigin","replace","pathname","SHA256_ROUND_CONSTANTS","Uint32Array","textEncoder","TextEncoder","inputBytes","encode","hashState0","hashState1","hashState2","hashState3","hashState4","hashState5","hashState6","hashState7","messageLengthInBits","paddedLengthInBytes","paddedBytes","paddedBytesView","DataView","lowBits","highBits","setUint32","messageSchedule","chunkOffset","getUint32","prevWord15","sigma0","prevWord2","sigma1","workingStateA","workingStateB","workingStateC","workingStateD","workingStateE","workingStateF","workingStateG","workingStateH","capitalSigma1","chFunction","temp1","capitalSigma0","majFunction","temp2","x","toString","padStart","httpResource","jsonFn","makeHttpResourceFn","arrayBuffer","blob","text","injector","assertInInjectionContext","Injector","getInitialStream","parsed","parse","signal","e","undefined","HttpResourceImpl","ctx","normalizeRequest","defaultValue","debugName","equal","unwrappedRequest","HttpParams","fromObject","HttpRequest","reportProgress","keepalive","cache","priority","mode","redirect","context","credentials","referrer","referrerPolicy","integrity","timeout","ResourceImpl","client","_headers","linkedSignal","source","extRequest","computation","_progress","_statusCode","computed","progress","asReadonly","statusCode","constructor","abortSignal","sub","aborted","onAbort","unsubscribe","addEventListener","stream","resolve","promise","Promise","r","send","subscribe","type","HttpEventType","Response","error","encapsulateResourceError","DownloadProgress","HttpErrorResponse","removeEventListener","complete","ɵRuntimeError","HttpClient"],"mappings":"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