From bce557cc2dc767628bed6aac87301a1be7c5431b Mon Sep 17 00:00:00 2001 From: rxliuli Date: Tue, 4 Nov 2025 05:03:50 +0800 Subject: init commit --- src/jet/dependencies/net.ts | 117 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/jet/dependencies/net.ts (limited to 'src/jet/dependencies/net.ts') diff --git a/src/jet/dependencies/net.ts b/src/jet/dependencies/net.ts new file mode 100644 index 0000000..dd7fdb9 --- /dev/null +++ b/src/jet/dependencies/net.ts @@ -0,0 +1,117 @@ +import type { Network, FetchRequest, FetchResponse } from '@jet/environment'; +import { fromEntries } from '@amp/web-apps-utils'; + +import { + shouldUseSearchJWT, + makeSearchJWTAuthorizationHeader, +} from '~/config/media-api'; + +const CORRELATION_KEY_HEADER = 'x-apple-jingle-correlation-key'; + +type FetchFunction = typeof window.fetch; + +// TODO: these URLs are also referenced in `bag` definition; we should have a single +// source-of-truth for these domains +const MEDIA_API_ORIGINS = [ + 'https://amp-api.apps.apple.com', + 'https://amp-api-edge.apps.apple.com', + 'https://amp-api-search-edge.apps.apple.com', +]; + +export interface FeaturesCallbacks { + getITFEValues(): string | undefined; +} + +export class Net implements Network { + private readonly underlyingFetch: FetchFunction; + private readonly getITFEValues: () => string | undefined = () => undefined; + + constructor( + underlyingFetch: FetchFunction, + featuresCallbacks?: FeaturesCallbacks, + ) { + this.underlyingFetch = underlyingFetch; + this.getITFEValues = + featuresCallbacks?.getITFEValues ?? this.getITFEValues; + } + + async fetch(request: FetchRequest): Promise { + const requestStartTime = getTimestampMs(); + const requestURL = new URL(request.url); + + request.headers = request.headers ?? {}; + + if (MEDIA_API_ORIGINS.includes(requestURL.origin)) { + // Need to fake this for the server due to Kong origin checks. + // Has no effect clientside. + request.headers['origin'] = 'https://apps.apple.com'; + + const itfe = this.getITFEValues?.(); + + if (itfe) { + // Add ITFE value as query string when set + requestURL.searchParams.set('itfe', itfe); + } + } + + // The App Store Client will have already injected the JWT from the + // `media-token-service` ObjectGraph dependency into the headers. However, + // some endpoints need a different JWT. Here we determine if that's the + // case and override the existing JWT if necessary. + if (shouldUseSearchJWT(requestURL)) { + request.headers = { + ...request.headers, + ...makeSearchJWTAuthorizationHeader(), + }; + } + + // TODO: rdar://78158575: timeout + const response = await this.underlyingFetch(requestURL.toString(), { + ...request, + cache: request.cache ?? undefined, + credentials: 'include', + headers: request.headers ?? undefined, + method: request.method ?? undefined, + }); + + const responseStartTime = getTimestampMs(); + + const { ok, redirected, status, statusText, url } = response; + + const headers = fromEntries(response.headers); + const body = await response.text(); + + const responseEndTime = getTimestampMs(); + + return { + ok, + headers, + redirected, + status, + statusText, + url, + body, + // TODO: rdar://78158575: redirect: 'manual' to get all metrics? + metrics: [ + { + clientCorrelationKey: response.headers.get( + CORRELATION_KEY_HEADER, + ), + pageURL: response.url, + requestStartTime, + responseStartTime, + responseEndTime, + // TODO: rdar://78158575: responseWasCached? + // TODO: rdar://78158575: parseStartTime/parseEndTime + }, + ], + }; + } +} + +/** + * Returns the current UTC timestamp in milliseconds. + */ +function getTimestampMs(): number { + return Date.now(); +} -- cgit v1.2.3