diff options
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/foundation/media/data-fetching.js')
| -rw-r--r-- | node_modules/@jet-app/app-store/tmp/src/foundation/media/data-fetching.js | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/data-fetching.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/data-fetching.js new file mode 100644 index 0000000..445f42f --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/data-fetching.js @@ -0,0 +1,631 @@ +import { isNothing, isSome } from "@jet/environment/types/optional"; +import * as serverData from "../json-parsing/server-data"; +import * as client from "../wrappers/client"; +export function allPlatforms(objectGraph, platformsToExclude) { + const platforms = new Set(); + platforms.add("iphone"); + platforms.add("ipad"); + platforms.add("appletv"); + platforms.add("mac"); + platforms.add("watch"); + if (objectGraph.client.isVision || objectGraph.bag.enableVisionPlatform) { + platforms.add("realityDevice"); + } + if (isSome(platformsToExclude)) { + for (const platform of platformsToExclude) { + platforms.delete(platform); + } + } + return Array.from(platforms); +} +export function defaultPlatformForClient(objectGraph) { + if (objectGraph.client.isCompanionVisionApp) { + // The Vision companion app always prefers visionOS assets + return "realityDevice"; + } + switch (objectGraph.client.deviceType) { + case "phone": + return "iphone"; + case "pad": + return "ipad"; + case "tv": + return "appletv"; + case "mac": + return "mac"; + case "watch": + return "watch"; + case "vision": + return "realityDevice"; + case "web": + return "web"; + default: + return null; + } +} +/** + * Returns the layout size controlling the number of rows to display. + * @param {AppStoreObjectGraph} objectGraph Object graph to get the client device type. + * @returns {number} Layout size. + */ +export function defaultLayoutSize(objectGraph) { + return objectGraph.client.isPhone ? 2 : 1; +} +/** + * Returns the sparse count controlling the number of shelves to hydrate. + * @param {AppStoreObjectGraph} objectGraph Object graph to get the client device type. + * @returns {number} Sparse count. + */ +export function defaultSparseCountForClient(objectGraph) { + switch (objectGraph.client.deviceType) { + case "phone": + return 4; + case "pad": + return 5; + case "tv": + return 6; + case "mac": + return 5; + case "watch": + return 10; + default: + return null; + } +} +/** + * Returns the sparse limit controlling the number of items to hydrate per shelve. + * @param {AppStoreObjectGraph} objectGraph Object graph to get the client device type. + * @returns {number} Sparse limit. + */ +export function defaultSparseLimitForClient(objectGraph) { + switch (objectGraph.client.deviceType) { + case "phone": + return 9; + case "pad": + return 12; + case "tv": + return 15; + case "mac": + return 15; + case "watch": + return 3; + case "web": + return 12; + default: + return null; + } +} +/** + * Get the list of all platforms excluding the current platform + * @returns {Platform[]} + */ +export function defaultAdditionalPlatformsForClient(objectGraph) { + switch (objectGraph.host.clientIdentifier) { + case "com.apple.TVAppStore.AppStoreTopShelfExtension": + case "com.apple.Arcade.ArcadeTopShelfExtension": + case "com.apple.AppStore.Widgets": + // Skip additional platforms for top shelf extension due to memory constraint. + return []; + default: { + const currentPlatform = defaultPlatformForClient(objectGraph); + return allPlatforms(objectGraph, isSome(currentPlatform) ? new Set([currentPlatform]) : undefined); + } + } +} +/** + * Get the product page reviews limit for a client. + * @returns {number} The limit to use. + */ +export function defaultProductPageReviewsLimitForClient(objectGraph) { + switch (objectGraph.client.deviceType) { + case "phone": + return 6; + case "pad": + return 10; + case "mac": + return 12; + case "vision": + return 10; + default: + return 8; + } +} +/// Returns the context param to use for a given grouping request. +/// This is leveraged for switching grouping tab root in specific configurations. +export function defaultGroupingContextForClient(objectGraph) { + let context = null; + if (objectGraph.host.clientIdentifier === client.messagesIdentifier) { + // Messages Grouping (iOS) + context = "messages"; + } + else if (objectGraph.host.clientIdentifier === client.watchIdentifier) { + // Bridge App Store (iOS) + context = "watch"; + } + else if (objectGraph.client.isWatch && objectGraph.client.isTinkerWatch) { + // Tinker App Store (watchOS) + context = "tinker"; + } + return context; +} +// Nothing on the App Store is rated 1000+, so a value this high essentially +// indicates that content restrictions are disabled. +export const ageRestrictionsDisabledThreshold = 1000; +export class Request { + constructor(objectGraph, param, enableMixedCatalog, supplementaryMetadataAssociations) { + var _a; + /// The resource types used in a mixed contents call + this.contentsResourceTypes = new Set(); + /// The ID(s) associated with the resource type + this.ids = new Set(); + /// IDs associated with specific resource types, used for mixed catalog requests + this.idsByResourceType = new Map(); + /// The original ordering of the ids in this reqeust if there is a mixed catalog request + this.originalOrdering = []; + this.relationshipIncludes = new Set(); + // Contents of `extend` param + this.attributeIncludes = new Set(); + this.platform = null; + /// The paths to additional metadata that need to be fetched for mixed media requests + this.supplementaryMetadataAssociations = []; + this.additionalPlatforms = new Set(); + this.additionalQuery = {}; + this.relationshipLimits = {}; + this.searchTerm = null; + this.searchTypes = []; + /// Whether we are searching watch or messages store. + this.context = null; + /// Whether or not to use custom extend attributes, instead of using `attributeIncludes` as-is. + this.useCustomAttributes = false; + /// An optional country code override for constructing the URL. Used in very specific regulatory scenarios where the bag country code cannot be relied upon. + this.countryCodeOverride = undefined; + this.objectGraph = objectGraph; + this.platform = defaultPlatformForClient(objectGraph); + this.isMixedMediaRequest = enableMixedCatalog !== null && enableMixedCatalog !== void 0 ? enableMixedCatalog : false; + this.supplementaryMetadataAssociations = supplementaryMetadataAssociations !== null && supplementaryMetadataAssociations !== void 0 ? supplementaryMetadataAssociations : []; + this.includeAppBinaryTraitsAttribute = objectGraph.client.isiOS; + // By default, the `platform` for web defaults to whatever the `activeIntent` is (e.g. "mac"), + // but this can be overridden when calling `setPreviewPlatform` with the request, which + // will set the `platform` to `web` and `previewPlatform` will be the `activeIntent` platform. + if ((_a = objectGraph.activeIntent) === null || _a === void 0 ? void 0 : _a.platform) { + this.platform = objectGraph.activeIntent.platform; + } + if (serverData.isNullOrEmpty(param)) { + return; + } + if (typeof param === "string") { + this.href = param; + } + else if (Array.isArray(param)) { + this.withDataItems(param, supplementaryMetadataAssociations, enableMixedCatalog); + } + } + /** + * Adds a data object to an idsByResourceType mapping. + * @param data The data object + * @returns An updated {resource-type: IDs} map, which includes the data object + */ + addDataToIDsByResourceType(data) { + const resourceType = data.type; + const id = data.id; + let ids = this.idsByResourceType.get(resourceType); + if (isNothing(ids)) { + ids = new Set(); + } + ids.add(id); + this.idsByResourceType.set(resourceType, ids); + } + forType(type) { + this.resourceType = type; + return this; + } + /** + * This method is used to add data items to the request. It is used to build the request for the mixed catalog / catalog + * + * @param dataItems The data items to add to the request + * @param supplementaryMetadataAssociations The metadata paths to add to the request + * @param enableMixedCatalog Whether to allow mixed catalog requests + * @returns The modified Request + */ + withDataItems(dataItems, supplementaryMetadataAssociations, enableMixedCatalog) { + if (dataItems.length === 0) { + return this; + } + this.isMixedMediaRequest = this.isMixedMediaRequest || (enableMixedCatalog !== null && enableMixedCatalog !== void 0 ? enableMixedCatalog : false); + for (const data of dataItems) { + // Track all IDs in a single set, for the case where we use the contents endpoint. + this.ids.add(data.id); + // Track all IDs by resource type, for either the case where we ultimately have just + // one resource type, or where we used the mixed catalog endpoint. + this.addDataToIDsByResourceType(data); + // If we have any additional metadata paths that need fetching, we now add them + // as additional resource types for the mixed catalog endpoint. + if (isSome(enableMixedCatalog) && + enableMixedCatalog && + isSome(supplementaryMetadataAssociations) && + supplementaryMetadataAssociations.length > 0) { + for (const association of supplementaryMetadataAssociations) { + const metadataItems = extractMetaAssociationFromData(association, data); + if (isSome(metadataItems) && metadataItems.length > 0) { + metadataItems.forEach((metadataItem) => { + this.addDataToIDsByResourceType(metadataItem); + }); + } + } + } + } + // Now that we have collated all IDs and resource types, we can assign them to different + // properties depending on the number of resource types we need. + if (this.idsByResourceType.size === 1) { + // We only have one list of IDs for a single resource type, so we just take the first one + this.resourceType = this.idsByResourceType.keys().next().value; + this.isMixedMediaRequest = false; + } + else if (this.idsByResourceType.size > 1 && !this.isMixedMediaRequest) { + this.resourceType = "contents"; + this.contentsResourceTypes = new Set(Array.from(this.idsByResourceType.keys())); + } + this.originalOrdering.push(...[...dataItems]); + return this; + } + /** + * Add a single id to this request, this should only be used when fetching a single resource + * @param id The single ID to add to the request + * @param type The type of the resource + * @returns The modified Request + */ + withIdOfType(id, type) { + return this.withDataItems([{ id, type }]); + } + /** + * Add a list of ids to this request, this will add these ids to the mapping of resource types + * @param ids The IDs to add to the request + * @param type The type of the resource + * @returns The modified Request + */ + withIdsOfType(ids, type) { + return this.withDataItems(ids.map((id) => ({ id, type }))); + } + includingRelationships(relationships) { + for (const relationship of relationships) { + this.relationshipIncludes.add(relationship); + } + return this; + } + includingScopedRelationships(scopedDataType, relationshipsToAdd) { + // Lazy init top-level property + if (!this.scopedRelationshipIncludes) { + this.scopedRelationshipIncludes = new Map(); + } + // Retrieve existing scoped relationship. Lazy init if needed. + let scopedRelationship = this.scopedRelationshipIncludes.get(scopedDataType); + if (!scopedRelationship) { + scopedRelationship = new Set(); + } + // Update + for (const newRelationship of relationshipsToAdd) { + scopedRelationship.add(newRelationship); + } + this.scopedRelationshipIncludes.set(scopedDataType, scopedRelationship); + return this; + } + includingMetaKeys(scopedDataType, keysToAdd) { + // Lazy init top-level property + if (!this.metaIncludes) { + this.metaIncludes = new Map(); + } + // Retrieve existing inclusion. Lazy init if needed. + let scopedMeta = this.metaIncludes.get(scopedDataType); + if (!scopedMeta) { + scopedMeta = new Set(); + } + // Update + for (const newKey of keysToAdd) { + scopedMeta.add(newKey); + } + this.metaIncludes.set(scopedDataType, scopedMeta); + return this; + } + includingViews(viewsToAdd) { + // Lazy init top-level property + if (!this.viewsIncludes) { + this.viewsIncludes = new Set(); + } + for (const view of viewsToAdd) { + this.viewsIncludes.add(view); + } + return this; + } + includingKindsKeys(scopedDataType, keysToAdd) { + // Lazy init top-level property + if (!this.kindIncludes) { + this.kindIncludes = new Map(); + } + // Retrieve existing inclusion. Lazy init if needed. + let scopedMeta = this.kindIncludes.get(scopedDataType); + if (!scopedMeta) { + scopedMeta = new Set(); + } + // Update + for (const newKey of keysToAdd) { + scopedMeta.add(newKey); + } + this.kindIncludes.set(scopedDataType, scopedMeta); + return this; + } + includingAssociateKeys(scopedDataType, keysToAdd) { + // Lazy init top-level property + if (!this.associateIncludes) { + this.associateIncludes = new Map(); + } + // Retrieve existing inclusion. Lazy init if needed. + let scopedAssociate = this.associateIncludes.get(scopedDataType); + if (!scopedAssociate) { + scopedAssociate = new Set(); + } + // Update + for (const newKey of keysToAdd) { + scopedAssociate.add(newKey); + } + this.associateIncludes.set(scopedDataType, scopedAssociate); + return this; + } + /** + * Include the relationships needed for an upsell. + * @param requiresScopedInclude A flag indicating whether the include should be "scoped". This will need to be the + * case when the relationships we are including is on a request for a separate primary resource type. For example, + * if we are looking to have the upsell relationship included in an EI (Today card, for example), this needs to be + * scoped. However, if we are fetching a grouping directly and need the upsell included, this should *not* be scoped. + */ + includingRelationshipsForUpsell(requiresScopedRelationshipInclude) { + const relationship = "marketing-items"; + if (requiresScopedRelationshipInclude) { + // Lazy init top-level property + if (!this.scopedRelationshipIncludes) { + this.scopedRelationshipIncludes = new Map(); + } + // Retrieve existing scoped relationship. Lazy init if needed. + let scopedRelationship = this.scopedRelationshipIncludes.get("editorial-items"); + if (!scopedRelationship) { + scopedRelationship = new Set(); + } + // Update + scopedRelationship.add(relationship); + this.scopedRelationshipIncludes.set("editorial-items", scopedRelationship); + } + else { + this.relationshipIncludes.add(relationship); + } + // In order to get metrics metadata stiched in to the marketing item relationship, we have to include it in our + // request. + if (relationship === "marketing-items") { + if (!this.metaIncludes) { + this.metaIncludes = new Map(); + } + // Retrieve existing inclusion. Lazy init if needed. + let scopedMeta = this.metaIncludes.get("marketing-items"); + if (!scopedMeta) { + scopedMeta = new Set(); + } + scopedMeta.add("metrics"); + this.metaIncludes.set("marketing-items", scopedMeta); + } + return this; + } + includingAttributes(attributes) { + for (const attribute of attributes) { + this.attributeIncludes.add(attribute); + } + return this; + } + includingScopedAttributes(resourceType, attributesToAdd) { + // Lazy init top-level property + if (!this.scopedAttributeIncludes) { + this.scopedAttributeIncludes = new Map(); + } + // Retrieve existing scoped relationship. Lazy init if needed. + let attributesForResourceType = this.scopedAttributeIncludes.get(resourceType); + if (!attributesForResourceType) { + attributesForResourceType = new Set(); + } + // Update + for (const attribute of attributesToAdd) { + attributesForResourceType.add(attribute); + } + this.scopedAttributeIncludes.set(resourceType, attributesForResourceType); + return this; + } + /** + * Adds an age restriction to the request. ManagedConfiguration provides + * this value on the client, which maps to JRPurpleRating.clientIdentifier. + * + * @returns {Request} The updated request + */ + includingAgeRestrictions() { + const maxAppContentRating = this.objectGraph.client.maxAppContentRating; + if (maxAppContentRating < ageRestrictionsDisabledThreshold) { + this.ageRestriction = maxAppContentRating; + } + return this; + } + includingAdditionalPlatforms(additionalPlatforms) { + for (const platform of additionalPlatforms) { + this.additionalPlatforms.add(platform); + } + return this; + } + includingScopedAvailableIn(resourceType, valuesToAdd) { + // Lazy init top-level property + if (!this.scopedAvailableInIncludes) { + this.scopedAvailableInIncludes = new Map(); + } + // Retrieve existing scoped relationship. Lazy init if needed. + let valuesForResourceType = this.scopedAvailableInIncludes.get(resourceType); + if (!valuesForResourceType) { + valuesForResourceType = new Set(); + } + // Update + for (const value of valuesToAdd) { + valuesForResourceType.add(value); + } + this.scopedAvailableInIncludes.set(resourceType, valuesForResourceType); + return this; + } + /** + * Include the sparse limit needed for a specific resource type. + * @param {media.Type} resourceType Resource type to use for the sparse limit. + * @param {number} value Value to set as the sparse limit. + * @returns {Request} Updated request. + */ + includingScopedSparseLimit(resourceType, value) { + // Lazy init top-level property + if (!this.scopedSparseLimit) { + this.scopedSparseLimit = new Map(); + } + this.scopedSparseLimit.set(resourceType, value); + return this; + } + addingQuery(key, value) { + if (isSome(value)) { + this.additionalQuery[key] = value; + } + else { + delete this.addingQuery[key]; + } + return this; + } + /** + * @param query The additional query values to add + * @returns The modified Reqeust + */ + addingQueryValues(query) { + this.additionalQuery = { + ...this.addingQuery, + ...query, + }; + return this; + } + addingRelationshipLimit(relationship, limit) { + this.relationshipLimits[relationship] = limit; + return this; + } + withSearchTerm(term) { + this.searchTerm = term; + return this; + } + searchingOverTypes(types) { + for (const type of types) { + this.searchTypes.push(type); + } + return this; + } + addingContext(context) { + this.context = context; + return this; + } + includingMacOSCompatibleIOSAppsWhenSupported(verifiedBadgeOnly = false) { + if (this.objectGraph.appleSilicon.isSupportEnabled) { + if (!verifiedBadgeOnly) { + this.enablingFeature("macOSCompatibleIOSApps"); + } + this.includingScopedAttributes("apps", ["isVerifiedForAppleSiliconMac"]); + } + return this; + } + /// Mark a request to include (or exclude) appBinaryTraits attributes. + includingAppBinaryTraitsAttribute(includeAppBinaryTraitsAttribute = true) { + this.includeAppBinaryTraitsAttribute = includeAppBinaryTraitsAttribute; + return this; + } + /** + * Mark a request to use custom attributes. + * This triggers usage of `customXYZ` extend attributes in url-builder for specific extend params + * e.g. `artwork`, `customArtwork`. + */ + usingCustomAttributes(useCustomAttributes) { + this.useCustomAttributes = useCustomAttributes; + return this; + } + alwaysUseIdsAsQueryParam(value) { + this.useIdsAsQueryParam = value; + return this; + } + attributingTo(canonicalUrl) { + this.canonicalUrl = canonicalUrl; + return this; + } + withFilter(type, value) { + this.filterType = type; + this.filterValue = value; + return this; + } + withLimit(limit) { + this.limit = limit; + return this; + } + withSparseLimit(sparseLimit) { + if (sparseLimit !== null) { + this.sparseLimit = sparseLimit; + } + return this; + } + withSparseCount(sparseCount) { + if (sparseCount !== null) { + this.sparseCount = sparseCount; + } + return this; + } + enablingFeature(feature) { + if (!this.enabledFeatures) { + this.enabledFeatures = []; + } + this.enabledFeatures.push(feature); + return this; + } + enablingFeatures(features) { + if (!this.enabledFeatures) { + this.enabledFeatures = []; + } + this.enabledFeatures.push(...features); + return this; + } + /** + * Limit the request to include only certain fields. + * This should only be used in very select use-cases, where: + * - There is no possibility of data being used for other purposes, e.g. sidpack + * - All consumer of this data use it in the same narrow scope, e.g. requesting a set of icons for displaying without metadata only. + * @param fields Set of fields to limit to + */ + asPartialResponseLimitedToFields(fields) { + this.fields = fields; + return this; + } + includesResourceType(resourceType) { + if (this.resourceType === resourceType) { + return true; + } + if (serverData.isDefinedNonNull(this.contentsResourceTypes)) { + return this.contentsResourceTypes.has(resourceType); + } + return false; + } + withCountryCodeOverride(countryCodeOverride) { + this.countryCodeOverride = countryCodeOverride; + return this; + } +} +/** + * Extracts the metadata association from the data. + * @param association The association to extract + * @param data The data to extract from + */ +export function extractMetaAssociationFromData(association, data) { + if (serverData.isNullOrEmpty(data)) { + return null; + } + const associationData = serverData.asArrayOrEmpty(data, `meta.associations.${association}.data`); + if (isNothing(associationData)) { + return null; + } + return [...associationData]; +} +//# sourceMappingURL=data-fetching.js.map
\ No newline at end of file |
