summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js
init commit
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js496
1 files changed, 496 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js b/node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js
new file mode 100644
index 0000000..f04a8e1
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/search/search-results-fetching.js
@@ -0,0 +1,496 @@
+/**
+ * Data Fetching for Search Results for:
+ * - Initial search requests
+ * - Content pagination requests
+ */
+import { isNothing } from "@jet/environment/types/optional";
+import * as models from "../../api/models";
+import { asBooleanOrFalse, asString, isDefinedNonNull, isDefinedNonNullNonEmpty, isNullOrEmpty, } from "../../foundation/json-parsing/server-data";
+import { defaultAdditionalPlatformsForClient, Request } from "../../foundation/media/data-fetching";
+import { metricsFromMediaApiObject, } from "../../foundation/media/data-structure";
+import { fetchData } from "../../foundation/media/network";
+import { buildURLFromRequest } from "../../foundation/media/url-builder";
+import * as constants from "../../foundation/util/constants";
+import * as client from "../../foundation/wrappers/client";
+import * as appPromotionsCommon from "../app-promotions/app-promotions-common";
+import * as categories from "../categories";
+import { addVariantParametersToRequestForItems, shouldFetchCustomAttributes, } from "../product-page/product-page-variants";
+import { setPreviewPlatform } from "../preview-platform";
+import { SponsoredSearchRequestData } from "./search-ads";
+import { searchMetricsDataSetID } from "./search-common";
+import { fetchSponsoredSearchNativeAdvertData } from "./sponsored-search-fetching";
+import { dateFlooredToHour } from "../../foundation/util/date-util";
+import { shouldUsePrerenderedIconArtwork } from "../content/content";
+import { AppEventsAttributes } from "../../gameservicesui/src/foundation/media-api/requests/recommendation-request-types";
+export async function fetchSegmentedSearchResults(objectGraph, options) {
+ var _a;
+ // Primary search request
+ const searchRequest = (_a = createSearchRequest(objectGraph, options)) === null || _a === void 0 ? void 0 : _a.addingQuery("groupBy[search]", "platform");
+ if (isNothing(searchRequest)) {
+ return null;
+ }
+ const fetchSearchResults = fetchData(objectGraph, searchRequest, undefined);
+ return await fetchSearchResults.then((catalogResponse) => {
+ renameDataSetIdKey(catalogResponse);
+ const data = {
+ catalogResponse: catalogResponse,
+ requestMetadata: {
+ requestDescriptor: options,
+ searchRequestUrl: buildURLFromRequest(objectGraph, searchRequest).toString(), // Searches are attributed to request url
+ },
+ categoriesFilterData: null,
+ sponsoredSearchRequestData: null,
+ sponsoredSearchAdvertData: null,
+ };
+ return data;
+ });
+}
+/**
+ * Fetch a collection of data for displaying a single, sequential set of search results.
+ * @param options Parameters for the search being performed.
+ * @returns `Promise` for aggregate search result data, or `null` if `options` was invalid.
+ */
+export async function fetchSequentialSearchResultsData(objectGraph, options) {
+ // Advert targeting data from client
+ const sponsoredSearchRequestData = new SponsoredSearchRequestData(options.targetingData, objectGraph.random.nextUUID());
+ // Primary search request
+ const searchRequest = createSearchRequest(objectGraph, options);
+ if (searchRequest === null) {
+ return null;
+ }
+ const fetchSearchResults = fetchData(objectGraph, searchRequest, createSearchFetchOptions(objectGraph, sponsoredSearchRequestData));
+ // Search History
+ if (objectGraph.bag.mediaAPISearchFocusEnabled) {
+ const historyItem = {
+ term: options.term.trim(),
+ entity: options.searchEntity,
+ };
+ // ** MAINTAINER'S NOTE **
+ // Fire and forget this request to reduce delay showing search results on persisting recent searches that are not visible at this time.
+ objectGraph.onDeviceSearchHistoryManager.saveRecentSearchWithLimit(historyItem, 30);
+ }
+ // Optional requests
+ const fetchSponsoredSearchAds = fetchSponsoredSearchNativeAdvertData(objectGraph, sponsoredSearchRequestData, options.term, fetchSearchResults);
+ const fetchCategoriesOrNull = fetchCategoryFiltersDataIfNeeded(objectGraph);
+ return await Promise.all([fetchSearchResults, fetchSponsoredSearchAds, fetchCategoriesOrNull]).then(([catalogResponse, sponsoredSearchAdvertData, categoriesFilterData]) => {
+ var _a, _b, _c, _d;
+ renameDataSetIdKey(catalogResponse);
+ // Remember the last time a natural language search was performed.
+ if ((_c = (_b = (_a = catalogResponse.meta) === null || _a === void 0 ? void 0 : _a.results) === null || _b === void 0 ? void 0 : _b.search) === null || _c === void 0 ? void 0 : _c.naturalLanguage) {
+ const previousNLSQueryDate = objectGraph.storage.retrieveString("lastNLSQueryDate");
+ const lastNLSQueryDate = dateFlooredToHour(new Date()).getTime().toString();
+ objectGraph.storage.storeString("lastNLSQueryDate", lastNLSQueryDate);
+ // Notify ODJ on change using AMSEngagement.
+ (_d = objectGraph.amsEngagement) === null || _d === void 0 ? void 0 : _d.enqueueData({
+ eventType: "lastNLSQueryDateChange",
+ app: "com.apple.AppStore",
+ oldState: previousNLSQueryDate,
+ newState: lastNLSQueryDate,
+ });
+ }
+ const data = {
+ catalogResponse: catalogResponse,
+ categoriesFilterData: categoriesFilterData,
+ sponsoredSearchRequestData: sponsoredSearchRequestData,
+ sponsoredSearchAdvertData: sponsoredSearchAdvertData,
+ requestMetadata: {
+ requestDescriptor: options,
+ searchRequestUrl: buildURLFromRequest(objectGraph, searchRequest).toString(), // Searches are attributed to request url
+ },
+ };
+ return data;
+ });
+}
+/**
+ * Fetch a collection of data for displaying several, grouped set of search results.
+ * @param options Parameters for the search being performed.
+ * @returns `Promise` for aggregate search result data, or `Promise` of `null` if `options` was invalid.
+ */
+export async function fetchPlatformGroupedSearchResultsData(objectGraph, options) {
+ // Primary search request
+ const searchRequest = createPlatformGroupedSearchRequest(objectGraph, options);
+ if (isNothing(searchRequest)) {
+ return null;
+ }
+ const fetchSearchResults = fetchData(objectGraph, searchRequest, createSearchFetchOptions(objectGraph));
+ // Optional requests
+ const fetchCategoriesOrNull = fetchCategoryFiltersDataIfNeeded(objectGraph);
+ return await Promise.all([fetchSearchResults, fetchCategoriesOrNull]).then(([catalogResponse, categoriesFilterData]) => {
+ renameDataSetIdKey(catalogResponse);
+ const data = {
+ catalogResponse: catalogResponse,
+ categoriesFilterData: categoriesFilterData,
+ sponsoredSearchRequestData: null,
+ sponsoredSearchAdvertData: null,
+ requestMetadata: {
+ requestDescriptor: options,
+ searchRequestUrl: buildURLFromRequest(objectGraph, searchRequest).toString(), // Searches are attributed to request url
+ },
+ };
+ return data;
+ });
+}
+/**
+ * Fetch a set of items for appearing in search results
+ * @param items Items to fetch.
+ */
+export async function fetchSearchResultItems(objectGraph, items) {
+ const request = createCatalogRequestForItems(objectGraph, items);
+ return await fetchData(objectGraph, request);
+}
+// endregion
+// region Request Header
+/**
+ * Create fetch options, annotating with `SponsoredSearchRequestData` header fields.
+ * @param adData Advert data to include in header.
+ */
+function createSearchFetchOptions(objectGraph, sponsoredSearchRequestData) {
+ const searchRequestHeader = {};
+ if (sponsoredSearchRequestData && sponsoredSearchRequestData.validAdRequest()) {
+ searchRequestHeader[constants.appStoreClientRequestIdName] = sponsoredSearchRequestData.appStoreClientRequestId;
+ searchRequestHeader[constants.iAdRequestDataHeaderName] = sponsoredSearchRequestData.sponsoredSearchRequestData;
+ searchRequestHeader[constants.iAdRoutingInfoHeaderName] = sponsoredSearchRequestData.routingInfo;
+ }
+ return {
+ headers: searchRequestHeader,
+ };
+}
+// endregion
+// region Search Results Request
+/**
+ * Set of relationships fetched as relation for search request / catalog requests
+ */
+const searchIncludeRelationships = ["apps", "top-apps"];
+/**
+ * Create the `Request` object to execute a search described by `options`
+ * @param options Options for search being executed.
+ * @returns `Request` configured for `options`, or `null` if `options` was invalid.
+ */
+export function createSearchRequest(objectGraph, options) {
+ var _a;
+ const term = (_a = options.term) === null || _a === void 0 ? void 0 : _a.trim();
+ if (isNullOrEmpty(term)) {
+ return null;
+ }
+ const origin = options.origin;
+ const source = options.source;
+ const searchEntityOrNull = options.searchEntity;
+ const facetsOrNull = options.facets;
+ const selectedFacetOptionsOrNull = options.selectedFacetOptions;
+ const spellCheckEnabled = options.spellCheckEnabled;
+ const excludedTermsOrNull = options.excludedTerms;
+ const clientIdentifier = objectGraph.host.clientIdentifier;
+ const searchRequest = new Request(objectGraph)
+ .withSearchTerm(term)
+ .includingAdditionalPlatforms(defaultAdditionalPlatformsForClient(objectGraph))
+ .includingAttributes(includeAttributesForSearchResults(objectGraph))
+ .includingScopedAttributes("editorial-items", ["showLabelInSearch"])
+ .includingRelationshipsForUpsell(true)
+ .includingMacOSCompatibleIOSAppsWhenSupported()
+ .usingCustomAttributes(shouldFetchCustomAttributes(objectGraph));
+ setPreviewPlatform(objectGraph, searchRequest);
+ if (!objectGraph.client.isWatch && !objectGraph.client.isVision) {
+ searchRequest.includingRelationships(searchIncludeRelationships);
+ }
+ if (objectGraph.client.isVision) {
+ // Temporarily increase the sparseLimit on Vision to workaround lack of filtering for bincompat.
+ searchRequest.addingQuery("sparseLimit[developers:top-apps]", "12");
+ }
+ if (appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
+ searchRequest.includingAssociateKeys("apps", ["app-events"]);
+ searchRequest.includingScopedAttributes("app-events", AppEventsAttributes);
+ searchRequest.includingScopedRelationships("editorial-items", ["primary-content"]);
+ }
+ /**
+ * Tinker Filter
+ */
+ if (asBooleanOrFalse(objectGraph.client.isTinkerWatch)) {
+ searchRequest.withFilter("contexts", "tinker");
+ }
+ /**
+ * Guided Search
+ */
+ if (objectGraph.host.isiOS) {
+ // Feature enablers
+ searchRequest.enablingFeature("guidedSearch");
+ searchRequest.enablingFeature("midScrollGuidedSearch");
+ // Selected facets, if any.
+ if (isDefinedNonNullNonEmpty(options.guidedSearchTokens)) {
+ searchRequest.addingQuery("selectedFacets", options.guidedSearchTokens.join(","));
+ }
+ // Optimization term, if any were on request.
+ if (isDefinedNonNullNonEmpty(options.guidedSearchOptimizationTerm)) {
+ searchRequest.addingQuery("finalTerm", options.guidedSearchOptimizationTerm);
+ }
+ if (objectGraph.bag.isLLMSearchTagsEnabled) {
+ searchRequest.includingAssociateKeys("results:apps", ["tags"]);
+ }
+ }
+ if (objectGraph.featureFlags.isEnabled("voyager_bundles_2025A")) {
+ searchRequest.includingScopedAttributes("apps", ["screenshotsByType"]);
+ }
+ /**
+ * Entities in results
+ */
+ if (searchEntityOrNull === "story") {
+ searchRequest.searchingOverTypes(["editorial-items"]);
+ }
+ else if (searchEntityOrNull === "developer") {
+ searchRequest.searchingOverTypes(["developers"]);
+ }
+ else if (searchEntityOrNull === "watch" || searchEntityOrNull === "arcade") {
+ searchRequest.searchingOverTypes(["apps"]).withFilter("contexts", searchEntityOrNull);
+ }
+ else if (objectGraph.client.isTV) {
+ searchRequest.searchingOverTypes(["apps", "developers", "groupings", "editorial-items"]);
+ }
+ else if (objectGraph.client.isVision) {
+ searchRequest.searchingOverTypes([
+ "apps",
+ "developers",
+ "editorial-items",
+ "app-bundles",
+ "in-apps",
+ "editorial-pages",
+ ]);
+ }
+ else {
+ searchRequest.searchingOverTypes([
+ "apps",
+ "developers",
+ "groupings",
+ "editorial-items",
+ "app-bundles",
+ "in-apps",
+ ]);
+ }
+ /**
+ * Signal Rosetta unavailability.
+ */
+ if (objectGraph.appleSilicon.isSupportEnabled && !objectGraph.appleSilicon.isRosettaAvailable) {
+ searchRequest.addingQuery("restrict", "!requiresRosetta");
+ }
+ /**
+ * Facets / filters
+ */
+ if (facetsOrNull) {
+ for (const key of Object.keys(facetsOrNull)) {
+ searchRequest.addingQuery(key, facetsOrNull[key]);
+ }
+ }
+ if (selectedFacetOptionsOrNull) {
+ for (const key of Object.keys(selectedFacetOptionsOrNull)) {
+ const requestValues = models.PageFacets.requestValuesForSelectedFacetOptions(selectedFacetOptionsOrNull[key]);
+ if (isDefinedNonNullNonEmpty(requestValues)) {
+ searchRequest.addingQuery(key, requestValues.value);
+ for (const additionalKey of Object.keys(requestValues.additionalKeyValuePairs)) {
+ searchRequest.addingQuery(additionalKey, requestValues.additionalKeyValuePairs[additionalKey]);
+ }
+ }
+ }
+ }
+ /**
+ * natural language search availability
+ */
+ const isNaturalLanguageSearchResultsEnabled = objectGraph.bag.isNaturalLanguageSearchEnabled || objectGraph.bag.isNaturalLanguageSearchResultsEnabled;
+ /**
+ * source attribution
+ */
+ const sourceKey = isNaturalLanguageSearchResultsEnabled ? "source" : "src";
+ if (origin === "hints") {
+ const hintSource = isNaturalLanguageSearchResultsEnabled && (source === null || source === void 0 ? void 0 : source.length) ? "hint:".concat(source) : "hint";
+ searchRequest.addingQuery(sourceKey, hintSource);
+ }
+ else if (origin === "recents") {
+ searchRequest.addingQuery(sourceKey, "recent");
+ }
+ else if (origin === "trending") {
+ searchRequest.addingQuery(sourceKey, "trending");
+ }
+ else if (origin === "undoSpellCorrection") {
+ searchRequest.addingQuery(sourceKey, "searchInstead");
+ }
+ else if (origin === "applySpellCorrection") {
+ searchRequest.addingQuery(sourceKey, "didYouMean");
+ }
+ else if (origin === "guidedToken") {
+ searchRequest.addingQuery(sourceKey, "facet");
+ }
+ /**
+ * contexts
+ */
+ switch (clientIdentifier) {
+ case client.watchIdentifier:
+ searchRequest.addingContext("watch");
+ break;
+ case client.messagesIdentifier:
+ searchRequest.addingContext("messages");
+ break;
+ case client.arcadeIdentifier:
+ searchRequest.addingContext("arcade");
+ break;
+ default:
+ break;
+ }
+ /**
+ * Advert limit
+ */
+ searchRequest.addingQuery("limit[ads-result]", objectGraph.bag.mediaAdvertRequestLimit.toString());
+ /**
+ * Ads locale metadata.
+ */
+ if (isDefinedNonNullNonEmpty(objectGraph.bag.adsOverrideLanguage)) {
+ searchRequest.enablingFeature("adsLocaleMetadata");
+ }
+ /**
+ * Feature: Spellcheck
+ */
+ if (spellCheckEnabled) {
+ searchRequest.enablingFeature("spellCheck");
+ }
+ /**
+ * Feature: Natural Language
+ */
+ if (isNaturalLanguageSearchResultsEnabled) {
+ searchRequest.enablingFeature("naturalLanguage");
+ }
+ /**
+ * Feature: Organic CPPs
+ */
+ if (objectGraph.host.isiOS) {
+ searchRequest.enablingFeature("searchResultCpps");
+ }
+ /**
+ * Excluded terms
+ */
+ if (excludedTermsOrNull) {
+ searchRequest.addingQuery("excludeTerms", excludedTermsOrNull.join(","));
+ }
+ return searchRequest;
+}
+/**
+ * Create the `Request` object to execute a search described by `options`, with results grouped by platform
+ * @param options Options for search being executed.
+ * @returns `Request` configured for `options`, or `null` if `options` was invalid.
+ */
+export function createPlatformGroupedSearchRequest(objectGraph, options) {
+ var _a;
+ return (_a = createSearchRequest(objectGraph, options)) === null || _a === void 0 ? void 0 : _a.addingQuery("groupBy[search]", "platform").includingMacOSCompatibleIOSAppsWhenSupported();
+}
+/**
+ * Create a request for fetching set of `items` for appearing in search tab. Used for pagination.
+ * @param items Items to fetch.
+ */
+function createCatalogRequestForItems(objectGraph, items) {
+ const shouldUseMixedCatalogRequest = objectGraph.bag.isLLMSearchTagsEnabled ||
+ asBooleanOrFalse(objectGraph.bag.supportedMixedMediaRequestUsecases["search"]);
+ const searchRequest = new Request(objectGraph, items, shouldUseMixedCatalogRequest, ["tags"])
+ .includingAdditionalPlatforms(defaultAdditionalPlatformsForClient(objectGraph))
+ .includingScopedAttributes("editorial-items", ["showLabelInSearch"])
+ .includingAttributes(includeAttributesForSearchResults(objectGraph))
+ .includingRelationshipsForUpsell(true)
+ .includingMacOSCompatibleIOSAppsWhenSupported()
+ .usingCustomAttributes(shouldFetchCustomAttributes(objectGraph));
+ addVariantParametersToRequestForItems(objectGraph, searchRequest, items);
+ if (!objectGraph.client.isWatch) {
+ searchRequest.includingRelationships(searchIncludeRelationships);
+ }
+ if (appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
+ searchRequest.includingAssociateKeys("apps", ["app-events"]);
+ searchRequest.includingScopedAttributes("app-events", AppEventsAttributes);
+ searchRequest.includingScopedRelationships("editorial-items", ["primary-content"]);
+ }
+ return searchRequest;
+}
+/**
+ * Returns the include attributes used for:
+ * 1. Initial search request
+ * 2. Subsequent catalog request for pagination
+ */
+function includeAttributesForSearchResults(objectGraph) {
+ const attributes = [
+ "screenshotsByType",
+ "messagesScreenshots",
+ "videoPreviewsByType",
+ "requiredCapabilities",
+ "editorialBadgeInfo",
+ "supportsFunCamera",
+ "minimumOSVersion",
+ "customScreenshotsByTypeForAd",
+ "customVideoPreviewsByTypeForAd",
+ "secondaryGenreShortDisplayNames",
+ "genreShortDisplayName",
+ "editorialVideo",
+ ];
+ if (objectGraph.appleSilicon.isSupportEnabled) {
+ attributes.push("macRequiredCapabilities");
+ }
+ if (objectGraph.client.isMac) {
+ attributes.push("hasMacIPAPackage");
+ }
+ if (objectGraph.client.isVision) {
+ attributes.push("compatibilityControllerRequirement");
+ }
+ if (objectGraph.bag.enableUpdatedAgeRatings) {
+ attributes.push("ageRating");
+ }
+ if (shouldUsePrerenderedIconArtwork(objectGraph)) {
+ attributes.push("iconArtwork");
+ }
+ /// We don't want to unnecessarily ask for clients that don't have metadata ribbon capability
+ if (objectGraph.host.isOSAtLeast(15, 5, 0)) {
+ attributes.push("remoteControllerRequirement");
+ }
+ // Keeping custom creatives out of prod.
+ if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
+ if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
+ attributes.push("adCreativeArtwork");
+ attributes.push("adCreativeVideo");
+ }
+ }
+ return attributes;
+}
+// endregion
+// region Categories Filter Request
+/**
+ * Fetches data to for category filters on devices that need them.
+ */
+async function fetchCategoryFiltersDataIfNeeded(objectGraph) {
+ const deviceType = objectGraph.client.deviceType;
+ if (deviceTypeSupportsCategoryFilters(deviceType)) {
+ const categoriesRequest = categories.createRequest(objectGraph, null, null, defaultAdditionalPlatformsForClient(objectGraph));
+ if (categoriesRequest) {
+ // Failable request.
+ return await fetchData(objectGraph, categoriesRequest).catch(() => null);
+ }
+ }
+ return null;
+}
+function deviceTypeSupportsCategoryFilters(deviceType) {
+ switch (deviceType) {
+ case "pad":
+ case "mac":
+ return true;
+ default:
+ return false;
+ }
+}
+// endregion
+// region Search Result Modification
+/**
+ * The search dataset id is set to the key `dataSetId` on `meta.metrics`, but needs to have `data.search.dataSetId` key.
+ * <rdar://problem/39920993> Metrics: metrics blob in search response has incorrect field name
+ */
+function renameDataSetIdKey(searchResponse) {
+ var _a;
+ const searchMetrics = metricsFromMediaApiObject(searchResponse);
+ const oldDataSetId = "dataSetId";
+ if (isDefinedNonNull(searchMetrics) &&
+ isDefinedNonNull(searchResponse.meta) &&
+ isDefinedNonNull((_a = searchResponse.meta) === null || _a === void 0 ? void 0 : _a.metrics)) {
+ searchResponse.meta.metrics[searchMetricsDataSetID] = asString(searchMetrics, oldDataSetId);
+ delete searchResponse.meta.metrics[oldDataSetId];
+ }
+}
+// endregion
+//# sourceMappingURL=search-results-fetching.js.map \ No newline at end of file