diff options
| author | rxliuli <rxliuli@gmail.com> | 2025-11-04 05:03:50 +0800 |
|---|---|---|
| committer | rxliuli <rxliuli@gmail.com> | 2025-11-04 05:03:50 +0800 |
| commit | bce557cc2dc767628bed6aac87301a1be7c5431b (patch) | |
| tree | b51a051228d01fe3306cd7626d4a96768aadb944 /node_modules/@jet-app/app-store/tmp/src/foundation | |
init commit
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/foundation')
55 files changed, 8266 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/amp-localization/amp-localization.js b/node_modules/@jet-app/app-store/tmp/src/foundation/amp-localization/amp-localization.js new file mode 100644 index 0000000..df04983 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/amp-localization/amp-localization.js @@ -0,0 +1,371 @@ +/** + * A type encapsulating localization logic for strings + * returned by localization server. + * + * Replacement for 'its' namespace from @onyx/localization package. + */ +export class AmpLocalization { + constructor() { + // region Properties + /** + * Localization JSON dictionary loaded from localization file. + */ + this.locData = {}; + /** + * The 2-letter code for current device language. + */ + this.language = "en"; + // endregion + } + // endregion + // region API + /** + * Updates the localization data for the device. + * @param localizations A JSON dictionary representing the localized strings. + * @param language Language code for current device language. + */ + updateLocalizationData(localizations, language) { + this.locData = localizations; + this.language = language; + } + /** + * Localizes a string replacing placehoders in key with values in the parameters dictionary + * @param key The loc key to look up + * @param params Parameters to replace in the loc string + * @return The localized string + */ + localize(key, params) { + let value = this.locData[key]; + if (value === undefined || typeof value !== "string") { + value = key; + } + if (params) { + value = this.replaceTokens(value, params); + } + value = this.replaceMarkupTokens(value, params); + return value; + } + /** + * Localize with appropriate plural form based on the count. + * + * Some languages have more plural forms than others. + * The full set of categories is "zero", "one", "two", "few", "many", and "other". + * + * The base loc key is used for "other" (the default). + * Otherwise the category is appended to the base loc key with a "." separator. + * + * http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html + * http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories + * http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-numbers.html#Language_Plural_Rules + * + * English loc keys + * key = "@@count@@ dogs"; // default, aka "plural" + * key.zero = "no dogs"; // used when count is 0 + * key.one = "@@count@@ dog"; // used when count is 1, aka "singular" + * + * @param key base loc key + * @param count number used to determine the plural form + * @param params (optional) substitution keys and values, "count" will be added if it's not already present + * @return localized string + */ + localizeWithCount(objectGraph, key, count, params) { + let keyToUse = null; + let pluralFormKey; + let category; + if (count === 0) { + // Special case zero for all languages so we can have strings like + // "you have no messages" instead of "you have 0 messages". + pluralFormKey = `${key}.zero`; + if (this.isLocalized(objectGraph, pluralFormKey)) { + keyToUse = pluralFormKey; + } + } + if (keyToUse === null) { + keyToUse = key; + category = this.pluralCategory(objectGraph, count); + if (category !== "other") { + pluralFormKey = `${key}.${category}`; + if (this.isLocalized(objectGraph, pluralFormKey)) { + keyToUse = pluralFormKey; + } + } + } + if (!params) { + params = {}; + } + // @@count@@ is the standard, but @@number@@ is frequently used too. + if (params.count === undefined) { + params.count = this.formatNumber(count.toString()); + } + if (params.number === undefined) { + params.number = this.formatNumber(count.toString()); + } + return this.localize(keyToUse !== null && keyToUse !== void 0 ? keyToUse : key, params); + } + // endregion + // region Private Methods + replaceTokens(text, values) { + Object.entries(values).forEach(([key, value]) => { + const subKey = "@@" + key + "@@"; + text = this.replaceSubstring(text, subKey, value); + }); + return text; + } + replaceMarkupTokens(text, values) { + if (text.indexOf("##") <= -1) { + return text; + } + // Resolve token properties. + let markupParams; + if (values) { + // Create a copy to avoid mutating defaults. + markupParams = { ...AmpLocalization.MARKUP_PARAMS }; + Object.entries(values).forEach(([key, value]) => { + markupParams[key] = value; + }); + } + else { + markupParams = AmpLocalization.MARKUP_PARAMS; + } + Object.entries(markupParams).forEach(([key, value]) => { + const token = "##" + key + "##"; + text = text.replace(new RegExp(token, "gi"), value); + }); + // Replace any remaining standard markup tags like <br> etc. + text = text.replace(/##([^##]+)##/gi, "<$1>"); + return text; + } + /** + * Searches "str" for "substr" and replaces each occurrence with "replacement" + * @param str input string + * @param substr the string to search for in the input string + * @param replacement value to use for the replacement + * + * JavaScript String.replace has a misfeature where "$" in the replacement is always interpreted as a meta char, + * i.e. you have to use "$$" in the replacement to get a single "$" in the resulting string + * which lead to <rdar://problem/16848063> tv episode title containing "$&" not displaying correctly + * Using split and join is faster than substituting $$ for $ in the replacement. + */ + replaceSubstring(str, substr, replacement) { + return str.split(substr).join(replacement); + } + formatNumber(value) { + let decimalSeparator = this.locData["_decimalSeparator"]; + if (decimalSeparator === undefined || typeof decimalSeparator !== "string") { + decimalSeparator = "."; + } + let thousandsSeparator = this.locData["_thousandsSeparator"]; + if (thousandsSeparator === undefined || typeof thousandsSeparator !== "string") { + thousandsSeparator = "."; + } + const parts = parseFloat(value).toString().split("."); + const chars = parts[0].split(""); + for (let i = chars.length - 3; i > 0; i -= 3) { + chars.splice(i, 0, thousandsSeparator); + } + parts[0] = chars.join(""); + return parts.join(decimalSeparator); + } + /** + * Check whether the given key is localized or not. + * @param key The key to check. + */ + isLocalized(objectGraph, key) { + const value = this.locData[key]; + if (value === undefined || typeof value !== "string") { + return false; + } + else if (key.indexOf(".") === -1) { + // Simple localization keys such as "OK". + return true; + } + else if (value === key || (value.indexOf("**") === 0 && value.lastIndexOf("**") === value.length - 2)) { + objectGraph.console.error("Unlocalized key in keys dictionary", key); + return false; + } + return true; + } + /** + * Returns localization plural category for given number. + * @param num The number to return plural category for. + * @returns Plural category for specified number or "other" + * if there's no plural category function available for current language. + * + * @see: + * - http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html + * - http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories + * - http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-numbers.html#Language_Plural_Rules + */ + pluralCategory(objectGraph, num) { + const categoryFn = AmpLocalization.pluralCategoryFnByLanguage[this.language]; + if (categoryFn !== undefined) { + return categoryFn(num); + } + else { + objectGraph.console.warn("Missing plural category function for: " + this.language); + return "other"; + } + } +} +/** + * Markup parameters used for replacing markup tokens in text. + */ +AmpLocalization.MARKUP_PARAMS = { nbsp: " ", gt: ">", lt: "<", copy: "\u00a9" }; +// endregion +// regions Plurals +AmpLocalization.pluralCategoryDefault = function (num) { + return "other"; +}; +AmpLocalization.pluralCategoryOne = function (num) { + if (num === 1) { + return "one"; + } + return "other"; +}; +AmpLocalization.pluralCategoryArabic = function (num) { + const n = num >> 0; + if (n !== num) { + // non integer + return "other"; + } + if (n === 0) { + return "zero"; + } + if (n === 1) { + return "one"; + } + if (n === 2) { + return "two"; + } + const m100 = n % 100; + if (m100 >= 11) { + // n mod 100 in 11..99 + return "many"; + } + if (m100 >= 3) { + // n mod 100 in 3..10 + return "few"; + } + return "other"; +}; +AmpLocalization.pluralCategoryFrench = function (num) { + // Do not check for integer, French includes fractional values! + if (num < 2 && num >= 0) { + return "one"; + } + return "other"; +}; +AmpLocalization.pluralCategoryHebrew = function (num) { + const n = num >> 0; + if (n !== num) { + // non integer + return "other"; + } + if (n === 1) { + return "one"; + } + if (n === 2) { + return "two"; + } + const m10 = n % 10; + if (m10 === 0 && n > 10) { + // n mod 10 is 0 and n != 0..10 + return "many"; + } + return "other"; +}; +AmpLocalization.pluralCategoryPolish = function (num) { + const n = num >> 0; + if (n !== num) { + // non integer + return "other"; + } + if (n === 1) { + return "one"; + } + const m10 = n % 10; + if (m10 <= 4 && m10 >= 2) { + const m100 = n % 100; + if (m100 > 14 || m100 < 12) { + // n mod 10 in 2..4 and n mod 100 not in 12..14 + return "few"; + } + } + return "many"; +}; +AmpLocalization.pluralCategoryRomanian = function (num) { + const n = num >> 0; + if (n !== num) { + // non integer + return "few"; + } + if (n === 0) { + return "few"; + } + if (n === 1) { + return "one"; + } + const m100 = num % 100; + if (m100 <= 19 && m100 >= 1) { + // n mod 100 in 1..19 + return "few"; + } + return "other"; +}; +AmpLocalization.pluralCategoryRussian = function (num) { + const n = num >> 0; + if (n !== num) { + // non integer + return "other"; + } + const m10 = n % 10; + if (m10 >= 5 || m10 === 0) { + // n mod 10 is 0 or n mod 10 in 5..9 + return "many"; + } + const m100 = n % 100; + if (m100 <= 14 && m100 >= 11) { + // n mod 100 in 11..14 + return "many"; + } + if (m10 === 1) { + // n mod 10 is 1 and n mod 100 is not 11 + return "one"; + } + // n mod 10 in 2..4 and n mod 100 not in 12..14 + return "few"; +}; +/** + * Mapping language code to plural category function. + */ +AmpLocalization.pluralCategoryFnByLanguage = { + zh: AmpLocalization.pluralCategoryDefault, + id: AmpLocalization.pluralCategoryDefault, + ja: AmpLocalization.pluralCategoryDefault, + ko: AmpLocalization.pluralCategoryDefault, + ms: AmpLocalization.pluralCategoryDefault, + th: AmpLocalization.pluralCategoryDefault, + vi: AmpLocalization.pluralCategoryDefault, + en: AmpLocalization.pluralCategoryOne, + ca: AmpLocalization.pluralCategoryOne, + da: AmpLocalization.pluralCategoryOne, + nl: AmpLocalization.pluralCategoryOne, + de: AmpLocalization.pluralCategoryOne, + el: AmpLocalization.pluralCategoryOne, + fi: AmpLocalization.pluralCategoryOne, + hu: AmpLocalization.pluralCategoryOne, + it: AmpLocalization.pluralCategoryOne, + nb: AmpLocalization.pluralCategoryOne, + no: AmpLocalization.pluralCategoryOne, + pt: AmpLocalization.pluralCategoryOne, + es: AmpLocalization.pluralCategoryOne, + sv: AmpLocalization.pluralCategoryOne, + tr: AmpLocalization.pluralCategoryOne, + ar: AmpLocalization.pluralCategoryArabic, + fr: AmpLocalization.pluralCategoryFrench, + iw: AmpLocalization.pluralCategoryHebrew, + pl: AmpLocalization.pluralCategoryPolish, + ro: AmpLocalization.pluralCategoryRomanian, + ru: AmpLocalization.pluralCategoryRussian, // Russian +}; +//# sourceMappingURL=amp-localization.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/active-intent.js b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/active-intent.js new file mode 100644 index 0000000..5270dd4 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/active-intent.js @@ -0,0 +1,121 @@ +import { isSome } from "@jet/environment/types/optional"; +import { makeMetatype } from "@jet/environment/util/metatype"; +import { unreachable } from "../util/errors"; +export const ActiveIntentMetaType = makeMetatype("app-store:active-intent"); +/** + * Executes {@linkcode callback} with an {@linkcode AppStoreObjectGraph} that has been prepared with an + * {@linkcode ActiveIntent} dependency + * + * This will allow code running within {@linkcode callback} to access meta-data about the active + * {@linkcode Intent} without needing that information to be passed through every layer of the code + */ +export function withActiveIntent(objectGraph, intent, callback) { + const objectGraphWithActiveIntent = objectGraph.addingActiveIntent({ + previewPlatform: intent.platform, + }); + return callback(objectGraphWithActiveIntent); +} +/** + * Ephemeral storage of `Intent`-specific details that need to be accessible + * throughout the process of building that `Intent`'s result + */ +export class ActiveIntent { + constructor(implementation) { + this.implementation = implementation; + this.inferredPreviewPlatform = undefined; + } + /** + * Explicitly set a {@linkcode PreviewPlatform} for the active intent + * + * This might be a value derrived from an API response, in cases where the original `Intent` + * was not specific about which platform to display + */ + setInferredPreviewPlatform(platform) { + this.inferredPreviewPlatform = platform; + } + /** + * The {@linkcode PreviewPlatform} value of the current {@linkcode Intent} + */ + get previewPlatform() { + var _a; + return (_a = this.inferredPreviewPlatform) !== null && _a !== void 0 ? _a : this.implementation.previewPlatform; + } + /** + * The {@linkcode Platform} equivalent of the active {@linkcode PreviewPlatform} + * + * This translates the user-facing {@linkcode PreviewPlatform} into the "internal" + * Media API {@linkcode Platform} + */ + get platform() { + if (isSome(this.previewPlatform)) { + switch (this.previewPlatform) { + case "ipad": + case "iphone": + case "mac": + case "watch": + return this.previewPlatform; + case "tv": + return "appletv"; + case "vision": + return "realityDevice"; + default: + unreachable(this.previewPlatform); + } + } + return undefined; + } + /** + * The {@linkcode AttributePlatform} equivalent of the active {@linkcode PreviewPlatform} + * + * This translates the user-facing {@linkcode PreviewPlatform} into the "internal" + * {@linkcode AttributePlatform} + */ + get attributePlatform() { + if (isSome(this.previewPlatform)) { + switch (this.previewPlatform) { + case "iphone": + case "ipad": + return "ios"; + case "mac": + return "osx"; + case "tv": + return "appletvos"; + case "vision": + return "xros"; + case "watch": + return "watch"; + default: + unreachable(this.previewPlatform); + } + } + return undefined; + } + /** + * The {@linkcode AppPlatform} equivalent of the active {@linkcode PreviewPlatform} + * + * This translates the user-facing {@linkcode PreviewPlatform} into the "internal" + * {@linkcode AppPlatform} + */ + get appPlatform() { + if (isSome(this.previewPlatform)) { + switch (this.previewPlatform) { + case "ipad": + return "pad"; + case "iphone": + return "phone"; + case "mac": + return "mac"; + case "tv": + return "tv"; + case "vision": + return "vision"; + case "watch": + return "watch"; + default: + unreachable(this.previewPlatform); + } + } + return undefined; + } +} +//# sourceMappingURL=active-intent.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale-from-bag.js b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale-from-bag.js new file mode 100644 index 0000000..7790008 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale-from-bag.js @@ -0,0 +1,41 @@ +export class LocaleFromBag { + constructor(objectGraph) { + this.objectGraph = objectGraph; + } + /** + * The current {@link NormalizedStorefront | storefront} for the client + * + * This reads directly from the `Bag`, as that is the source of truth + * for locale information in the "native" clients + */ + get activeStorefront() { + // The type-cast here reflects the implicit assumption that any values + // belonging to the `Bag` have already been normalized + return this.objectGraph.bag.mediaCountryCode; + } + /** + * The current {@link NormalizedLanguage | language} for the client + * + * This reads directly from the `Bag`, as that is the source of truth + * for locale information in the "native" clients + */ + get activeLanguage() { + // The type-cast here reflects the implicit assumption that any values + // belonging to the `Bag` have already been normalized + return this.objectGraph.bag.language; + } + setActiveLocale(_locale) { + // Intentional no-op; "native" clients that use the `Bag` as the source + // of "locale" information do not allow for mutation of the "locale" + } + normalize(_unnormalizedLocale) { + return { + storefront: this.activeStorefront, + language: this.activeLanguage, + }; + } + deriveLocaleForUrl(locale) { + return locale; + } +} +//# sourceMappingURL=locale-from-bag.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale.js b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale.js new file mode 100644 index 0000000..d00bacc --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale.js @@ -0,0 +1,3 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +export const LocaleMetaType = makeMetatype("app-store:locale"); +//# sourceMappingURL=locale.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/seo.js b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/seo.js new file mode 100644 index 0000000..9def945 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/seo.js @@ -0,0 +1,3 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +export const SEOMetaType = makeMetatype("app-store:seo"); +//# sourceMappingURL=seo.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/app-store-experiments.js b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/app-store-experiments.js new file mode 100644 index 0000000..e073481 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/app-store-experiments.js @@ -0,0 +1,7 @@ +import { ExperimentCache } from "./experiment-cache"; +export function currentTreatmentIdForArea(objectGraph, experimentAreaId) { + var _a; + const experimentCache = objectGraph.optional(ExperimentCache.metatype); + return (_a = experimentCache === null || experimentCache === void 0 ? void 0 : experimentCache.currentTreatmentForExperiment(experimentAreaId)) === null || _a === void 0 ? void 0 : _a.identifier; +} +//# sourceMappingURL=app-store-experiments.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-area-id.js b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-area-id.js new file mode 100644 index 0000000..435c196 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-area-id.js @@ -0,0 +1,10 @@ +export var ExperimentAreaId; +(function (ExperimentAreaId) { + ExperimentAreaId["ArcadeDownloadPackOnboarding"] = "Tf5Kjqz"; + ExperimentAreaId["CondensedTodayAds"] = "tBc9hUt"; + ExperimentAreaId["ProductPagePreloading"] = "m0henFo"; + ExperimentAreaId["ProductPageVariants"] = "fNPb5Km"; + ExperimentAreaId["ProductPageYMALRowCount"] = "isj11bm"; + ExperimentAreaId["SearchLandingPage"] = "WqjkRLH"; +})(ExperimentAreaId || (ExperimentAreaId = {})); +//# sourceMappingURL=experiment-area-id.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-cache.js b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-cache.js new file mode 100644 index 0000000..c0384ea --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-cache.js @@ -0,0 +1,63 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { ExperimentAreaId } from "./experiment-area-id"; +export class ExperimentCache { + constructor() { + this.cachedTreatments = {}; + this.cachedRawTreatments = {}; + } + async loadTreatments(objectGraph) { + const experimentAreas = this.experimentAreasForPlatform(objectGraph); + if (experimentAreas.length > 0) { + try { + this.cachedRawTreatments = await objectGraph.treatmentStore.treatmentsForAreas(experimentAreas); + for (const [experimentAreaId, treatment] of Object.entries(this.cachedRawTreatments)) { + /// AMS Adds some debug metadata after the treatment identifier in internal builds we do not want this + /// for app store usage + const rawIdentifier = treatment.identifier; + this.cachedTreatments[experimentAreaId] = { + ...treatment, + identifier: rawIdentifier.split(":")[0], + }; + } + } + catch (error) { + objectGraph.console.error("Failed to load treatments", error); + } + } + } + currentTreatmentForExperiment(experimentAreaId) { + return this.cachedTreatments[experimentAreaId]; + } + createAb2Data() { + const ab2Data = []; + for (const [experimentAreaId, treatment] of Object.entries(this.cachedRawTreatments)) { + ab2Data.push({ + areaId: experimentAreaId, + bucket: -2, + treatmentId: treatment.identifier, + }); + } + return ab2Data; + } + experimentAreasForPlatform(objectGraph) { + const experimentAreas = []; + switch (objectGraph.client.deviceType) { + case "phone": + case "pad": + experimentAreas.push(...[ + ExperimentAreaId.ArcadeDownloadPackOnboarding, + ExperimentAreaId.CondensedTodayAds, + ExperimentAreaId.ProductPagePreloading, + ExperimentAreaId.ProductPageVariants, + ExperimentAreaId.ProductPageYMALRowCount, + ExperimentAreaId.SearchLandingPage, + ]); + break; + default: + break; + } + return experimentAreas; + } +} +ExperimentCache.metatype = makeMetatype("app-store:experimentCache"); +//# sourceMappingURL=experiment-cache.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/product-page-experiments.js b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/product-page-experiments.js new file mode 100644 index 0000000..7af0b43 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/product-page-experiments.js @@ -0,0 +1,21 @@ +import * as appStoreExperiments from "./app-store-experiments"; +import { ExperimentAreaId } from "./experiment-area-id"; +export function shouldSkipProductPagePreload(objectGraph) { + const treatmentId = appStoreExperiments.currentTreatmentIdForArea(objectGraph, ExperimentAreaId.ProductPagePreloading); + return treatmentId === "3NepyQj01" /* ProductPagePreloadingExperimentTypes.SkipPreload */; +} +// --- Product Page Variants -- +/// Returns the treatment group id based on xp_ab value as string. +export function productVariantTreatmentId(objectGraph) { + return appStoreExperiments.currentTreatmentIdForArea(objectGraph, ExperimentAreaId.ProductPageVariants); +} +/** + * The number of rows that the YMAL shelf should use. + * @param objectGraph The object graph. + * @returns A number representing the number of rows in the YMAL shelf. + */ +export function ymalShelfNumberOfRows(objectGraph) { + const treatmentId = appStoreExperiments.currentTreatmentIdForArea(objectGraph, ExperimentAreaId.ProductPageYMALRowCount); + return treatmentId === "2S6U3Dq01" /* ProductPageYMALShelfRowNumberExperimentTypes.ThreeRow */ ? 3 : 2; +} +//# sourceMappingURL=product-page-experiments.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/search-results-experiments.js b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/search-results-experiments.js new file mode 100644 index 0000000..54654ee --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/search-results-experiments.js @@ -0,0 +1,10 @@ +import * as serverData from "../../foundation/json-parsing/server-data"; +/** + * Returns the current treatment for editorial collections based on the meta blob. + * @param meta Meta blob in initial response. + */ +export function currentEditorialCollectionTreatment(objectGraph, meta) { + const showGridCard = serverData.asBooleanOrFalse(meta, "experiments.showGridCard"); + return showGridCard ? 0 /* EditorialCollectionExperimentType.Card */ : 1 /* EditorialCollectionExperimentType.Swoosh */; +} +//# sourceMappingURL=search-results-experiments.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/today-ad-experiments.js b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/today-ad-experiments.js new file mode 100644 index 0000000..52468f9 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/today-ad-experiments.js @@ -0,0 +1,15 @@ +import * as appStoreExperiments from "./app-store-experiments"; +import { ExperimentAreaId } from "./experiment-area-id"; +/** + * Check whether the condensed treatment should be applied to the Today ad. + * @param objectGraph The object graph. + * @returns A boolean indicating if the treatment should be applied. + */ +export function shouldTodayAdBeCondensed(objectGraph) { + if (!objectGraph.client.isPhone) { + return false; + } + const treatmentId = appStoreExperiments.currentTreatmentIdForArea(objectGraph, ExperimentAreaId.CondensedTodayAds); + return treatmentId === "5pdfhju01" /* TodayAdCondensedDisplayExperimentTypes.Condensed */; +} +//# sourceMappingURL=today-ad-experiments.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/derived-data.js b/node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/derived-data.js new file mode 100644 index 0000000..fdf1f4e --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/derived-data.js @@ -0,0 +1,37 @@ +import * as validation from "@jet/environment/json/validation"; +import * as serverData from "./server-data"; +const DERIVED_CACHE_KEY = "_jet-internal:derived-data"; +export function value(data, key, generator) { + if (!data) { + return null; + } + return validation.context(key, () => { + let computedDataStore = data[DERIVED_CACHE_KEY]; + let returnValue = null; + if (!computedDataStore) { + computedDataStore = {}; + data[DERIVED_CACHE_KEY] = computedDataStore; + returnValue = generateValue(computedDataStore, key, generator); + } + else { + returnValue = serverData.traverse(computedDataStore, key); + if (!returnValue) { + returnValue = generateValue(computedDataStore, key, generator); + } + } + return returnValue; + }); +} +/** + * Computes an unknown value and saves it back into computedDataStore + * @param {JSONData} computedDataStore The dictionary in which to store the data + * @param {string} key The key to store it with + * @param {() => T} generator A function that will generate the value + * @returns {T} The value that was generated + */ +function generateValue(computedDataStore, key, generator) { + const generatedValue = generator(); + computedDataStore[key] = generatedValue; + return generatedValue; +} +//# sourceMappingURL=derived-data.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/server-data.js b/node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/server-data.js new file mode 100644 index 0000000..2ff6394 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/server-data.js @@ -0,0 +1,464 @@ +// +// server-data.ts +// AppStoreKit +// +// Created by Kevin MacWhinnie on 8/17/16. +// Copyright (c) 2016 Apple Inc. All rights reserved. +// +import * as validation from "@jet/environment/json/validation"; +import { isNothing } from "@jet/environment/types/optional"; +/** + * Returns the string representation of a given object path. + * @param path The object path to coerce to a string. + * @returns A string representation of `path`. + */ +export function objectPathToString(path) { + if (isNull(path)) { + return null; + } + else if (Array.isArray(path)) { + return path.join("."); + } + else { + return path; + } +} +const PARSED_PATH_CACHE = {}; +/** + * Traverse a nested JSON object structure, short-circuiting + * when finding `undefined` or `null` values. Usage: + * + * const object = {x: {y: {z: 42}}}; + * const meaningOfLife = serverData.traverse(object, 'x.y.z'); + * + * @param object The JSON object to traverse. + * @param path The path to search. If falsy, `object` will be returned without being traversed. + * @param defaultValue The object to return if the path search fails. + * @return The value at `path` if found; default value otherwise. + */ +export function traverse(object, path, defaultValue) { + if (object === undefined || object === null) { + return defaultValue; + } + if (!path) { + return object; + } + let components; + if (typeof path === "string") { + components = PARSED_PATH_CACHE[path]; + if (!components) { + // Fast Path: If the path contains only a single component, we can skip + // all of the work below here and speed up storefronts that + // don't have JIT compilation enabled. + if (!path.includes(".")) { + const value = object[path]; + if (value !== undefined && value !== null) { + return value; + } + else { + return defaultValue; + } + } + components = path.split("."); + PARSED_PATH_CACHE[path] = components; + } + } + else { + components = path; + } + let current = object; + for (const component of components) { + current = current[component]; + if (current === undefined || current === null) { + return defaultValue; + } + } + return current; +} +// endregion +// region Nullability +/** + * Returns a bool indicating whether or not a given object null or undefined. + * @param object The object to test. + * @return true if the object is null or undefined; false otherwise. + */ +export function isNull(object) { + return object === null || object === undefined; +} +/** + * Returns a bool indicating whether or not a given object is null or empty. + * @param object The object to test + * @return true if object is null or empty; false otherwise. + */ +export function isNullOrEmpty(object) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return isNull(object) || Object.keys(object).length === 0; +} +/** + * Returns a bool indicating whether or not a given object is non-null. + * @param object The object to test. + * @return true if the object is not null or undefined; false otherwise. + */ +export function isDefinedNonNull(object) { + return typeof object !== "undefined" && object !== null; +} +/** + * Returns a bool indicating whether or not a given object is non-null or empty. + * @param object The object to test. + * @return true if the object is not null or undefined and not empty; false otherwise. + */ +export function isDefinedNonNullNonEmpty(object) { + if (isNothing(object)) { + return false; + } + if (typeof object === "string") { + return object.length > 0; + } + else if (Array.isArray(object)) { + return object.length > 0; + } + else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return Object.keys(object).length !== 0; + } +} +/** + * Returns a bool indicating whether or not a given object is non-null or empty. + * @param object The object to test. + * @return true if the object is not null or undefined and not empty; false otherwise. + */ +export function isSetDefinedNonNullNonEmpty(object) { + return isDefinedNonNull(object) && object.size > 0; +} +/** + * Checks if the passed string or number is a number + * + * @param value The value to check + * @return True if the value is an number, false if not + */ +export function isNumber(value) { + if (isNull(value)) { + return false; + } + let valueToCheck; + if (typeof value === "string") { + valueToCheck = parseInt(value); + } + else { + valueToCheck = value; + } + return !Number.isNaN(valueToCheck); +} +/** + * Checks if the value is a string + * + * @param value The value to check + * @return True if the value is a string, false if not + */ +export function isString(value) { + if (isNothing(value)) { + return false; + } + return typeof value === "string"; +} +/** + * Returns a bool indicating whether or not a given object is defined but empty. + * @param object The object to test. + * @return true if the object is not null and empty; false otherwise. + */ +export function isArrayDefinedNonNullAndEmpty(object) { + return isDefinedNonNull(object) && object.length === 0; +} +// endregion +// region Defaulting Casts +/** + * Check that a given object is an array, substituting an empty array if not. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find an array. + * Omit this parameter if `object` is itself an array. + * @returns An untyped array. + */ +export function asArrayOrEmpty(object, path) { + var _a; + return (_a = asArray(object, path, [])) !== null && _a !== void 0 ? _a : []; +} +/** + * Check that a given object is a boolean, substituting the value `false` if not. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find a boolean. + * Omit this parameter if `object` is itself a boolean. + * @returns A boolean from `object`, or defaults to `false`. + */ +export function asBooleanOrFalse(object, path) { + return asBooleanWithDefault(object, false, path); +} +/** + * Check that a given object is a boolean, substituting the value `false` if not. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find a boolean. + * Omit this parameter if `object` is itself a boolean. + * @returns A boolean from `object`, or the provided default. + */ +export function asBooleanWithDefault(object, defaultValue, path) { + const target = traverse(object, path, null); + if (typeof target === "boolean") { + return target; + } + else { + if (!isNull(target)) { + validation.context("asBooleanWithDefault", () => { + validation.unexpectedType("defaultValue", "boolean", target, objectPathToString(path)); + }); + } + return defaultValue; + } +} +/** + * Safely coerce an object into a string. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find a string. + * Omit this parameter if `object` is itself a string. + * @param policy The validation policy to use when resolving this value + * @returns A string from `object`, or `null` if `object` is null. + */ +export function asString(object, path, policy = "coercible") { + const target = traverse(object, path, null); + if (isNull(target)) { + return target; + } + else if (typeof target === "string") { + return target; + } + else { + // We don't consider arbitrary objects as convertable to strings even through they will result in some value + const coercedValue = typeof target === "object" ? null : String(target); + switch (policy) { + case "strict": { + validation.context("asString", () => { + validation.unexpectedType("coercedValue", "string", target, objectPathToString(path)); + }); + break; + } + case "coercible": { + if (isNull(coercedValue)) { + validation.context("asString", () => { + validation.unexpectedType("coercedValue", "string", target, objectPathToString(path)); + }); + } + break; + } + case "none": + default: { + break; + } + } + return coercedValue; + } +} +/** + * Safely coerce an object into a number. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find a number. + * Omit this parameter if `object` is itself a number. + * @param policy The validation policy to use when resolving this value + * @returns A number from `object`, or `null` if `object` is null. + */ +export function asNumber(object, path, policy = "coercible") { + const target = traverse(object, path, null); + if (isNull(target) || typeof target === "number") { + return target; + } + else { + const coercedValue = Number(target); + switch (policy) { + case "strict": { + validation.context("asNumber", () => { + validation.unexpectedType("coercedValue", "number", target, objectPathToString(path)); + }); + break; + } + case "coercible": { + if (isNaN(coercedValue)) { + validation.context("asNumber", () => { + validation.unexpectedType("coercedValue", "number", target, objectPathToString(path)); + }); + return null; + } + break; + } + case "none": + default: { + break; + } + } + return coercedValue; + } +} +/** + * Safely coerce an object into a dictionary. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find the dictionary. + * Omit this parameter if `object` is itself a dictionary. + * @param defaultValue The object to return if the path search fails. + * @returns A sub-dictionary from `object`, or `null` if `object` is null. + */ +export function asDictionary(object, path, defaultValue) { + const target = traverse(object, path, null); + if (target instanceof Object && !Array.isArray(target)) { + // Note: It's too expensive to actually validate this is a dictionary of { string : Type } at run time + return target; + } + else { + if (!isNull(target)) { + validation.context("asDictionary", () => { + validation.unexpectedType("defaultValue", "object", target, objectPathToString(path)); + }); + } + if (isDefinedNonNull(defaultValue)) { + return defaultValue; + } + return null; + } +} +/** + * Coerce an object into an array + * @param object The object to coerce. + * @param path The path to traverse on `object` to find an array. + * Omit this parameter if `object` is itself an array. + * @returns An untyped array. + */ +export function asArray(object, path, defaultValue) { + const target = traverse(object, path, null); + if (Array.isArray(target)) { + // Note: This is kind of a nasty cast, but I don't think we want to validate that everything is of type T + return target; + } + else { + if (!isNull(target)) { + validation.context("asArray", () => { + validation.unexpectedType("defaultValue", "array", target, objectPathToString(path)); + }); + } + if (isDefinedNonNull(defaultValue)) { + return defaultValue; + } + return null; + } +} +/** + * Safely coerce an object into a given interface. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find a string. + * Omit this parameter if `object` is itself a string. + * @param defaultValue The object to return if the path search fails. + * @returns A sub-dictionary from `object`, or `null` if `object` is null. + */ +export function asInterface(object, path, defaultValue) { + return asDictionary(object, path, defaultValue); +} +/** + * Coerce an object into a boolean. + * @param object The object to coerce. + * @param path The path to traverse on `object` to find a boolean. + * Omit this parameter if `object` is itself a boolean. + * @param policy The validation policy to use when resolving this value + * @returns A boolean from `object`, or `null` if `object` is null. + * @note This is distinct from `asBooleanOrFalse` in that it doesn't default to false, + * and it tries to convert string boolean values into actual boolean types + */ +export function asBoolean(object, path, policy = "coercible") { + const target = traverse(object, path, null); + // Value was null + if (isNull(target)) { + return null; + } + // Value was boolean. + if (typeof target === "boolean") { + return target; + } + // Value was string. + if (typeof target === "string") { + if (target === "true") { + return true; + } + else if (target === "false") { + return false; + } + } + // Else coerce. + const coercedValue = Boolean(target); + switch (policy) { + case "strict": { + validation.context("asBoolean", () => { + validation.unexpectedType("coercedValue", "number", target, objectPathToString(path)); + }); + break; + } + case "coercible": { + if (isNull(coercedValue)) { + validation.context("asBoolean", () => { + validation.unexpectedType("coercedValue", "number", target, objectPathToString(path)); + }); + return null; + } + break; + } + case "none": + default: { + break; + } + } + return coercedValue; +} +/** + * Attempts to coerce the passed value to a JSONValue + * + * Note: due to performance concerns this does not perform a deep inspection of Objects or Arrays. + * + * @param value The value to coerce + * @return A JSONValue or null if value is not a valid JSONValue type + */ +export function asJSONValue(value) { + if (value === null || value === undefined) { + return null; + } + switch (typeof value) { + case "string": + case "number": + case "boolean": + return value; + case "object": + // Note: It's too expensive to actually validate this is an array of JSONValues at run time + if (Array.isArray(value)) { + return value; + } + // Note: It's too expensive to actually validate this is a dictionary of { string : JSONValue } at run time + return value; + default: + validation.context("asJSONValue", () => { + validation.unexpectedType("defaultValue", "JSONValue", typeof value); + }); + return null; + } +} +/** + * Attempts to coerce the passed value to JSONData + * + * @param value The value to coerce + * @return A JSONData or null if the value is not a valid JSONData object + */ +export function asJSONData(value) { + if (value === null || value === undefined) { + return null; + } + if (value instanceof Object && !Array.isArray(value)) { + // Note: It's too expensive to actually validate this is a dictionary of { string : Type } at run time + return value; + } + validation.context("asJSONValue", () => { + validation.unexpectedType("defaultValue", "object", typeof value); + }); + return null; +} +// endregion +//# sourceMappingURL=server-data.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/associations.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/associations.js new file mode 100644 index 0000000..7e2b088 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/associations.js @@ -0,0 +1,17 @@ +import * as serverData from "../json-parsing/server-data"; +/** + * @param data The media api data to find teh card in + * @returns The editorial-card data for this media api item, this will return the first associated card + */ +export function editorialCardsFromData(data) { + const editorialCards = serverData.asArrayOrEmpty(data, "meta.associations.editorial-cards.data"); + return editorialCards; +} +/** + * @param data The media api data to find teh card in + * @returns The editorial-card data for this media api item, this will return the first associated card + */ +export function editorialCardFromData(data) { + return serverData.asInterface(editorialCardsFromData(data)[0]); +} +//# sourceMappingURL=associations.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/attributes.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/attributes.js new file mode 100644 index 0000000..992a56e --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/attributes.js @@ -0,0 +1,149 @@ +import { isNothing } from "@jet/environment/types/optional"; +import * as serverData from "../json-parsing/server-data"; +// region Generic Attribute retrieval +// region Attribute retrieval +/** + * Retrieve the specified attribute from the data, coercing it to a JSONData dictionary + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @param defaultValue The object to return if the path search fails. + * @returns The dictionary of data + */ +export function attributeAsDictionary(data, attributePath, defaultValue) { + if (isNothing(data)) { + return null; + } + return serverData.asDictionary(data.attributes, attributePath, defaultValue); +} +/** + * Retrieve the specified attribute from the data, coercing it to an Interface + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @param defaultValue The object to return if the path search fails. + * @returns The dictionary of data as an interface + */ +export function attributeAsInterface(data, attributePath, defaultValue) { + return attributeAsDictionary(data, attributePath, defaultValue); +} +/** + * Retrieve the specified attribute from the data as an array, coercing to a JSONValue array + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @returns {any[]} The attribute value as an array. + */ +export function attributeAsArray(data, attributePath) { + if (serverData.isNull(data)) { + return []; + } + return serverData.asArray(data.attributes, attributePath); +} +/** + * Retrieve the specified attribute from the data as an array, coercing to an empty array if the object is not an array. + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @returns {any[]} The attribute value as an array. + */ +export function attributeAsArrayOrEmpty(data, attributePath) { + var _a; + return (_a = attributeAsArray(data, attributePath)) !== null && _a !== void 0 ? _a : []; +} +/** + * Retrieve the specified attribute from the data as a string. + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The object path for the attribute. + * @param policy The validation policy to use when resolving this value. + * @returns {string} The attribute value as a string. + */ +export function attributeAsString(data, attributePath, policy = "coercible") { + if (serverData.isNull(data)) { + return null; + } + return serverData.asString(data.attributes, attributePath, policy); +} +/** + * Retrieve the specified attribute from the data as a boolean. + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @param policy The validation policy to use when resolving this value. + * @returns {boolean} The attribute value as a boolean. + */ +export function attributeAsBoolean(data, attributePath, policy = "coercible") { + if (serverData.isNull(data)) { + return null; + } + return serverData.asBoolean(data.attributes, attributePath, policy); +} +/** + * Retrieve the specified attribute from the data as a boolean, which will be `false` if the attribute does not exist. + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @returns {boolean} The attribute value as a boolean, coercing to `false` if the value is not present.. + */ +export function attributeAsBooleanOrFalse(data, attributePath) { + if (serverData.isNull(data)) { + return false; + } + return serverData.asBooleanOrFalse(data.attributes, attributePath); +} +/** + * Retrieve the specified attribute from the data as a number. + * + * @param data The data from which to retrieve the attribute. + * @param attributePath The path of the attribute. + * @param policy The validation policy to use when resolving this value. + * @returns {boolean} The attribute value as a number. + */ +export function attributeAsNumber(data, attributePath, policy = "coercible") { + if (serverData.isNull(data)) { + return null; + } + return serverData.asNumber(data.attributes, attributePath, policy); +} +export function hasAttributes(data) { + return !serverData.isNull(serverData.asDictionary(data, "attributes")); +} +/** + * The canonical way to detect if an item from Media API is hydrated or not. + * + * @param data The data from which to retrieve the attributes. + */ +export function isNotHydrated(data) { + return !hasAttributes(data); +} +// region Custom Attributes +/** + * Performs conversion for a custom variant of given attribute, if any are available. + * @param attribute Attribute to get custom attribute key for, if any. + */ +export function attributeKeyAsCustomAttributeKey(attribute) { + return customAttributeMapping[attribute]; +} +/** + * Whether or not given custom attributes key allows fallback to default page with AB testing treatment within a nondefault page. + * This is to allow AB testing to affect only icons within custom product pages. + */ +export function attributeAllowsNonDefaultTreatmentInNonDefaultPage(customAttribute) { + return customAttribute === "customArtwork" || customAttribute === "customIconArtwork"; // Only the icon artwork. +} +/** + * Defines mapping of attribute to custom attribute. + */ +const customAttributeMapping = { + artwork: "customArtwork", + iconArtwork: "customIconArtwork", + screenshotsByType: "customScreenshotsByType", + promotionalText: "customPromotionalText", + videoPreviewsByType: "customVideoPreviewsByType", + customScreenshotsByTypeForAd: "customScreenshotsByTypeForAd", + customVideoPreviewsByTypeForAd: "customVideoPreviewsByTypeForAd", + customDeepLink: "customDeepLink", +}; +// endregion +//# sourceMappingURL=attributes.js.map
\ No newline at end of file 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 diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/data-structure.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/data-structure.js new file mode 100644 index 0000000..cafcce8 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/data-structure.js @@ -0,0 +1,84 @@ +/** + * Created by joel on 1/25/18. + */ +import * as serverData from "../json-parsing/server-data"; +import { ResponseMetadata } from "../network/network"; +export function dataFromDataContainer(objectGraph, dataContainer) { + const dataArray = serverData.asArrayOrEmpty(dataContainer, "data"); + if (dataArray.length > 1) { + objectGraph.console.warn("tried to extract data from container but more than one member present"); + } + if (dataArray.length !== 1) { + return null; + } + return dataArray[0]; +} +export function dataCollectionFromDataContainer(dataContainer) { + return serverData.asArrayOrEmpty(dataContainer, "data"); +} +/** + * Check whether or not a server vended `Data` object is hydrated or not. + * @param {Data} data to check if hydrated. + */ +export function isDataHydrated(data) { + return serverData.isDefinedNonNull(data.attributes); +} +/** + * Check whether or not a entire data collection contains elements that are fully hydrated. + * @param {Data[]} dataArray Data array to check. + */ +export function isDataCollectionHydrated(dataCollection) { + // Iterate from the back to determine if fully hydrated faster - unhydrated elements tend to be latter elements. + const lastIndex = dataCollection.length - 1; + for (let index = lastIndex; index >= 0; index--) { + const data = dataCollection[index]; + if (!isDataHydrated(data)) { + return false; + } + } + return true; +} +/** + * Check whether or not a entire data collection at least has 1 element that is fully hydrated. + * @param {Data[]} dataArray Data array to check. + */ +export function isDataCollectionPartiallyHydrated(dataCollection) { + for (const data of dataCollection) { + if (isDataHydrated(data)) { + return true; + } + } + return false; +} +/** + * Check whether or not a today data module represents a today page on the heuristic that: + * - The "Today" modules have labels with value 'TodayForApps' as opposed to `WhatYouMissed` + * - Date marker "Today" modules have a 'date' field. + */ +export function isModuleTodayForApps(todayModule) { + const todayModuleId = "TodayForApps"; + const date = serverData.traverse(todayModule, "date"); + return todayModule.label === todayModuleId || serverData.isDefinedNonNull(date); +} +/** + * Get chart results from a server response. Always returns chart segments with valid data + */ +export function chartResultsFromServerResponse(response) { + const resultsArray = serverData.asArrayOrEmpty(response, "results.apps"); + return resultsArray.filter((segment) => { + return !serverData.isNull(segment.data); + }); +} +export function dataCollectionFromResultsListContainer(resultsListContainer) { + return serverData.asArrayOrEmpty(resultsListContainer, "results.contents"); +} +/** + * Get the metrics dictionary included in the meta data of a mediaApi Data object. + * @param {MetaDataProviding} metaDataProvidingObject + * @returns {MapLike<string> | null} + */ +export function metricsFromMediaApiObject(metaDataProvidingObject) { + return serverData.asDictionary(metaDataProvidingObject, "meta.metrics"); +} +// endregion +//# sourceMappingURL=data-structure.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/network.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/network.js new file mode 100644 index 0000000..2b01d40 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/network.js @@ -0,0 +1,304 @@ +/** + * Created by joel on 20/2/2018. + * + * This `network.ts` is the Media API arm of the netwok fetch requests. + * It is built on `Network` object and provides standard functionality specific for MAPI. + * + * @see `src/network.ts` for fetching from non-Media API endpoints + */ +import { isNothing, isSome } from "@jet/environment/types/optional"; +import * as serverData from "../json-parsing/server-data"; +import { MetricsIdentifierType } from "../metrics/metrics-identifiers-cache"; +import { ResponseMetadata } from "../network/network"; +import * as urls from "../network/urls"; +import { extractMetaAssociationFromData } from "./data-fetching"; +import * as urlBuilder from "./url-builder"; +const secondaryUserIdHeaderName = "X-Apple-AppStore-UserId-Secondary"; +/** + * Implements the MAPI fetch, building URL from MAPI Request and opaquely managing initial token request and refreshes. + * + * @param {Request} request MAPI Request to fetch with. + * @param {FetchOptions} options? FetchOptions for the MAPI request. + * @returns {Promise<Type>} Promise resolving to some type for given MAPI request. + */ +export async function fetchData(objectGraph, request, options) { + var _a; + const startTime = Date.now(); + const token = await objectGraph.mediaToken.refreshToken(); + const fetchTimingMetricsBuilder = objectGraph.fetchTimingMetricsBuilder; + const requestOptions = options !== null && options !== void 0 ? options : {}; + const personalizationIdentifier = (_a = objectGraph.personalizationMetricsIdentifiersCache) === null || _a === void 0 ? void 0 : _a.getMetricsIdForType(MetricsIdentifierType.user); + if (isSome(personalizationIdentifier)) { + if (isSome(requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.headers)) { + requestOptions.headers[secondaryUserIdHeaderName] = personalizationIdentifier; + } + else { + requestOptions.headers = { + [secondaryUserIdHeaderName]: personalizationIdentifier, + }; + } + } + const response = await fetchWithToken(objectGraph, request, token, requestOptions, false, fetchTimingMetricsBuilder); + const endTime = Date.now(); + if (request.canonicalUrl) { + response[ResponseMetadata.requestedUrl] = request.canonicalUrl; + } + const roundTripTimeIncludingWaiting = endTime - startTime; + if (roundTripTimeIncludingWaiting > 500) { + const longFetchUrl = urlBuilder.buildURLFromRequest(objectGraph, request).toString(); + objectGraph.console.warn("Fetch took too long (" + roundTripTimeIncludingWaiting.toString() + "ms) " + longFetchUrl); + } + return response; +} +export function redirectParametersInUrl(objectGraph, url) { + const redirectURLParams = objectGraph.bag.redirectUrlWhitelistedQueryParams; + return redirectURLParams.filter((param) => { var _a; return serverData.isDefinedNonNull((_a = url.query) === null || _a === void 0 ? void 0 : _a[param]); }); +} +/** + * Given a built URL, token, and options, calls into native networking APIs to fetch content. + * + * @param {string} url URL to fetch data from. + * @param {string} request The original request used to build this url. + * @param {string} token MAPI token key. + * @param {FetchOptions} options Fetch options for MAPI requests. + * @param {boolean} isRetry flag indicating whether this is a fetch retry following a 401 request, and media token was refreshed. + * @returns {Promise<Type>} Promise resolving to some type for given MAPI request. + */ +async function fetchWithToken(objectGraph, request, token, options = {}, isRetry = false, fetchTimingMetricsBuilder) { + var _a, _b; + const originalUrl = urlBuilder.buildURLFromRequest(objectGraph, request).toString(); + // Removes all affiliate/redirect params for caching (https://connectme.apple.com/docs/DOC-577671) + const filteredURL = new urls.URL(originalUrl); + const redirectParameters = redirectParametersInUrl(objectGraph, filteredURL); + for (const param of redirectParameters) { + filteredURL.removeParam(param); + } + const filteredUrlString = filteredURL.toString(); + let headers = options.headers; + if (!headers) { + headers = {}; + } + headers["Authorization"] = "Bearer " + token; + const response = await objectGraph.network.fetch({ + url: filteredUrlString, + headers: headers, + method: options.method, + body: options.requestBodyString, + timeout: options.timeout, + }); + try { + if (response.status === 401 || response.status === 403) { + if (isRetry) { + throw Error("We refreshed the token but we still get 401 from the API"); + } + objectGraph.mediaToken.resetToken(); + return await objectGraph.mediaToken.refreshToken().then(async (newToken) => { + // Explicitly re-fetch with the original request so logging and metrics are correct + return await fetchWithToken(objectGraph, request, newToken, options, true, fetchTimingMetricsBuilder); + }); + } + else if (response.status === 404) { + // item is not available in this storefront or perhaps not at all + throw noContentError(); + } + else if (!response.ok) { + const error = new NetworkError(`Bad Status code ${response.status} for ${filteredUrlString}, original ${originalUrl}`); + error.statusCode = response.status; + throw error; + } + const parser = (resp) => { + var _a; + const parseStartTime = Date.now(); + let result; + if (serverData.isNull(resp.body) || resp.body === "") { + if (resp.status === 204) { + // 204 indicates a success, but the response will typically be empty + // Create a fake result so that we don't throw an error when JSON parsing + const emptyData = {}; + result = emptyData; + } + else { + throw noContentError(); + } + } + else { + try { + result = JSON.parse(resp.body); + } + catch (e) { + let errorMessage = e.message; + if (["debug", "internal"].includes(objectGraph.client.buildType)) { + errorMessage = `${e.message}, body: ${resp.body}`; + } + throw new JSONParseError(errorMessage); + } + } + const parseEndTime = Date.now(); + if (result) { + result[ResponseMetadata.pageInformation] = serverData.asJSONData(getPageInformationFromResponse(objectGraph, resp)); + if (resp.metrics.length > 0) { + const metrics = { + ...resp.metrics[0], + parseStartTime: parseStartTime, + parseEndTime: parseEndTime, + }; + result[ResponseMetadata.timingValues] = metrics; + } + else { + const fallbackMetrics = { + pageURL: resp.url, + parseStartTime, + parseEndTime, + }; + result[ResponseMetadata.timingValues] = fallbackMetrics; + } + result[ResponseMetadata.contentMaxAge] = getContentTimeToLiveFromResponse(objectGraph, resp); + // If we have an empty data object, throw a 204 (No Content). + if (Array.isArray(result.data) && + serverData.isArrayDefinedNonNullAndEmpty(result.data) && + !serverData.asBooleanOrFalse(options.allowEmptyDataResponse)) { + throw noContentError(); + } + if (Array.isArray(result.data) && request.originalOrdering.length > 1) { + result.data = buildHydratedDataListResponse(objectGraph, request.originalOrdering, (_a = result.data) !== null && _a !== void 0 ? _a : [], request.supplementaryMetadataAssociations); + } + result[ResponseMetadata.requestedUrl] = originalUrl; + } + return result; + }; + if (isSome(fetchTimingMetricsBuilder)) { + return fetchTimingMetricsBuilder.measureParsing(response, parser); + } + else { + return parser(response); + } + } + catch (e) { + if (e instanceof NetworkError) { + throw e; + } + const correlationKey = (_a = response.headers["x-apple-jingle-correlation-key"]) !== null && _a !== void 0 ? _a : (_b = response.metrics[0]) === null || _b === void 0 ? void 0 : _b.clientCorrelationKey; + throw new Error(`Error Fetching - filtered: ${filteredUrlString}, original: ${originalUrl}, correlationKey: ${correlationKey !== null && correlationKey !== void 0 ? correlationKey : "N/A"}, ${e.name}, ${e.message}`); + } +} +export class NetworkError extends Error { +} +class JSONParseError extends Error { +} +export function noContentError() { + const error = new NetworkError(`No content`); + error.statusCode = 204; + return error; +} +export function notFoundError() { + const error = new NetworkError("Not found"); + error.statusCode = 404; + return error; +} +const serverInstanceHeader = "x-apple-application-instance"; +const environmentDataCenterHeader = "x-apple-application-site"; +function getPageInformationFromResponse(objectGraph, response) { + const storeFrontHeader = objectGraph.client.storefrontIdentifier; + let storeFront = null; + if ((storeFrontHeader === null || storeFrontHeader === void 0 ? void 0 : storeFrontHeader.length) > 0) { + const storeFrontHeaderComponents = storeFrontHeader.split("-"); + if (serverData.isDefinedNonNullNonEmpty(storeFrontHeaderComponents)) { + storeFront = storeFrontHeaderComponents[0]; + } + } + return { + serverInstance: response.headers[serverInstanceHeader], + storeFrontHeader: storeFrontHeader, + language: objectGraph.bag.language, + storeFront: storeFront, + environmentDataCenter: response.headers[environmentDataCenterHeader], + }; +} +function getContentTimeToLiveFromResponse(objectGraph, response) { + const cacheControlHeaderKey = Object.keys(response.headers).find((key) => key.toLowerCase() === "cache-control"); + if (serverData.isNull(cacheControlHeaderKey) || cacheControlHeaderKey === "") { + return null; + } + const headerValue = response.headers[cacheControlHeaderKey]; + if (serverData.isNullOrEmpty(headerValue)) { + return null; + } + const matches = headerValue.match(/max-age=(\d+)/); + if (serverData.isNull(matches) || matches.length < 2) { + return null; + } + return serverData.asNumber(matches[1]); +} +/** + * Builds the hydrated list of shelf items, based on the data in the response, and the original unhydated items. + */ +function buildHydratedDataListResponse(objectGraph, unhydratedData, hydratedDataCollection, associations = []) { + // Create a map of all the hydrated data items from the response, using the resource type and ID as the key + const hydratedDataMap = {}; + for (const dataItem of hydratedDataCollection) { + const key = dataMapKey(objectGraph, dataItem.type, dataItem.id); + hydratedDataMap[key] = dataItem; + } + // Next iterate through the unhyrated items in their original order, and swap in the hydrated item. + const hydratedItems = []; + for (const unhydratedItem of unhydratedData) { + const key = dataMapKey(objectGraph, unhydratedItem.type, unhydratedItem.id); + const hydratedItem = hydratedDataMap[key]; + if (isSome(hydratedItem)) { + /// Start with a base of what was there already, this is to ensure we don't lose any existing meta data + /// if some of the requested associations are not present in the response. + if (serverData.isDefinedNonNullNonEmpty(associations)) { + hydratedItem.meta = { + ...unhydratedItem.meta, + }; + for (const association of associations) { + hydrateMetaAssociationIfNecessary(objectGraph, association, hydratedItem, unhydratedItem, hydratedDataMap); + } + } + hydratedItems.push(hydratedItem); + } + } + return hydratedItems; +} +/** + * Creates a key to use in a mapping of data items. + * @param objectGraph Current object graph + * @param data The data item + * @returns A string composed from the data item ID and resource type + */ +function dataMapKey(objectGraph, resourceType, id) { + return `${resourceType}_${id}`; +} +/** + * @param objectGraph The current object graph + * @param association The association we'd like to hydrate in the meta object + * @param unhydratedItem The original MAPI data item to look for editorial card, and fill in with a fetched card if there is one + * @param hydratedDataMap The data map of all the fetched items keyed by id / type + */ +function hydrateMetaAssociationIfNecessary(objectGraph, association, hydratedItem, unhydratedItem, hydratedDataMap) { + var _a; + if (isNothing(hydratedItem.meta)) { + hydratedItem.meta = { + associations: {}, + }; + } + else if (isNothing(hydratedItem.meta.associations)) { + hydratedItem.meta.associations = {}; + } + const unhydratedAssociationsData = extractMetaAssociationFromData(association, unhydratedItem); + if (serverData.isDefinedNonNullNonEmpty(unhydratedAssociationsData)) { + const hydratedAssociationData = []; + for (const unhydratedAssociation of unhydratedAssociationsData) { + const associationDataKey = dataMapKey(objectGraph, unhydratedAssociation.type, unhydratedAssociation.id); + const associationData = hydratedDataMap[associationDataKey]; + if (isSome(associationData)) { + hydratedAssociationData.push(associationData); + } + } + const hydratedAssociations = (_a = serverData.asDictionary(hydratedItem.meta.associations)) !== null && _a !== void 0 ? _a : {}; + hydratedAssociations[association] = { + data: hydratedAssociationData, + }; + } +} +//# sourceMappingURL=network.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/platform-attributes.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/platform-attributes.js new file mode 100644 index 0000000..3849624 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/platform-attributes.js @@ -0,0 +1,143 @@ +import { isNothing } from "@jet/environment/types/optional"; +import * as serverData from "../json-parsing/server-data"; +import * as attributes from "./attributes"; +/** + * There are nested attributes that are platform-specific. If provided with a platform for which the response data has + * platform-specific attributes, this function will return those attributes. + * @param {Data} data The data for which to determine attributes. + * @param {AttributePlatform} platform The platform to fetch the attributes for + * @returns {any} If a platform is provided, returns `true` exactly when platform-specific attributes exist for that + * platform. If no platform is provided, it simply returns the data's top-level attributes. + */ +function attributesForPlatform(data, platform) { + const allPlatformAttributes = attributes.attributeAsDictionary(data, "platformAttributes"); + return serverData.traverse(allPlatformAttributes, platform !== null && platform !== void 0 ? platform : undefined); +} +/** + * Determines if attributes exist for a given platform + * @param {Data} data The data to check + * @param {AttributePlatform} platform The platform to check + * @returns {boolean} True if the platform exists in the data's platform attributes. False if not. + */ +export function hasPlatformAttribute(data, platform) { + const platformAttributes = attributesForPlatform(data, platform); + return serverData.isDefinedNonNullNonEmpty(platformAttributes); +} +/** + * Retrieve the specified attribute from the data as a dictionary + * + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The path of the attribute. + * @returns The value for the requested attribute + */ +export function platformAttributeAsDictionary(data, platform, attributePath) { + const platformAttributes = attributesForPlatform(data, platform); + if (serverData.isNull(platformAttributes)) { + return null; + } + return serverData.asDictionary(platformAttributes, attributePath); +} +/** + * Retrieve the specified attribute from the data as an array. + * + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The path of the attribute. + * @returns {any[]} The attribute value as an array. + */ +export function platformAttributeAsArray(data, platform, attributePath) { + const platformAttributes = attributesForPlatform(data, platform); + if (isNothing(platformAttributes)) { + return null; + } + return serverData.asArray(platformAttributes, attributePath); +} +/** + * Retrieve the specified attribute from the data as an array, coercing to an empty array if the object is not an array. + * + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The path of the attribute. + * @returns {any[]} The attribute value as an array. + */ +export function platformAttributeAsArrayOrEmpty(data, platform, attributePath) { + const platformAttributes = attributesForPlatform(data, platform); + if (serverData.isNull(platformAttributes)) { + return []; + } + return serverData.asArrayOrEmpty(platformAttributes, attributePath); +} +/** + * Retrieve the specified attribute from the data as a string. + * + * If the attribute lives under the platform-specific attributes, then a platform may be provided to properly call in to + * the nested structure. + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The object path for the attribute. + * @param policy The validation policy to use when resolving this value. + * @returns {string} The attribute value as a string. + */ +export function platformAttributeAsString(data, platform, attributePath, policy = "coercible") { + const platformAttributes = attributesForPlatform(data, platform); + if (serverData.isNull(platformAttributes)) { + return null; + } + return serverData.asString(platformAttributes, attributePath, policy); +} +/** + * Retrieve the specified attribute from the data as a boolean. + * + * If the attribute lives under the platform-specific attributes, then a platform may be provided to properly call in to + * the nested structure. + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The path of the attribute. + * @param policy The validation policy to use when resolving this value. + * @returns {boolean} The attribute value as a boolean. + */ +export function platformAttributeAsBoolean(data, platform, attributePath, policy = "coercible") { + const platformAttributes = attributesForPlatform(data, platform); + if (serverData.isNull(platformAttributes)) { + return null; + } + return serverData.asBoolean(platformAttributes, attributePath, policy); +} +/** + * Retrieve the specified attribute from the data as a boolean, which will be `false` if the attribute does not exist. + * + * If the attribute lives under the platform-specific attributes, then a platform may be provided to properly call in to + * the nested structure. + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The path of the attribute. + * @returns {boolean} The attribute value as a boolean, coercing to `false` if the value is not present.. + */ +export function platformAttributeAsBooleanOrFalse(data, platform, attributePath) { + const platformAttributes = attributesForPlatform(data, platform); + if (serverData.isNull(platformAttributes)) { + return false; + } + return serverData.asBooleanOrFalse(platformAttributes, attributePath); +} +/** + * Retrieve the specified attribute from the data as a number. + * + * If the attribute lives under the platform-specific attributes, then a platform may be provided to properly call in to + * the nested structure. + * @param data The data from which to retrieve the attribute. + * @param platform The platform to look up + * @param attributePath The path of the attribute. + * @param policy The validation policy to use when resolving this value. + * @returns {boolean} The attribute value as a number. + */ +export function platformAttributeAsNumber(data, platform, attributePath, policy = "coercible") { + const platformAttributes = attributesForPlatform(data, platform); + if (serverData.isNull(platformAttributes)) { + return null; + } + return serverData.asNumber(platformAttributes, attributePath, policy); +} +// endregion +//# sourceMappingURL=platform-attributes.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/relationships.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/relationships.js new file mode 100644 index 0000000..0f96574 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/relationships.js @@ -0,0 +1,43 @@ +import * as serverData from "../json-parsing/server-data"; +export function hasRelationship(data, relationshipType, checkForContent = true) { + const relationshipDataContainer = relationship(data, relationshipType); + if (!relationshipDataContainer) { + return false; + } + if (!relationshipDataContainer.data || (checkForContent && relationshipDataContainer.data.length === 0)) { + return false; + } + return true; +} +export function relationship(data, relationshipType) { + if (serverData.isDefinedNonNull(data)) { + return serverData.asInterface(data.relationships, relationshipType); + } + return null; +} +export function relationshipViewsContainer(data, relationshipType) { + return serverData.asInterface(data.views, relationshipType); +} +export function relationshipData(objectGraph, data, relationshipType) { + const relationshipDataArray = serverData.asArrayOrEmpty(data.relationships, [ + relationshipType, + "data", + ]); + if (relationshipDataArray.length === 0) { + return null; + } + if (relationshipDataArray.length > 1) { + objectGraph.console.warn(`there was an array of relationships when only the first was asked for in relationship ${relationshipType}`); + } + return relationshipDataArray[0]; +} +export function relationshipCollection(data, relationshipType, allowNulls = false) { + if (!hasRelationship(data, relationshipType, false) && allowNulls) { + return null; + } + return serverData.asArrayOrEmpty(data.relationships, [relationshipType, "data"]); +} +export function relationshipViewsCollection(data, relationshipType) { + return serverData.asArrayOrEmpty(data.views, [relationshipType, "data"]); +} +//# sourceMappingURL=relationships.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/url-builder.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/url-builder.js new file mode 100644 index 0000000..955529c --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/url-builder.js @@ -0,0 +1,381 @@ +/** + * Created by joel on 11/4/2018. + */ +import { isNothing, isSome } from "@jet/environment/types/optional"; +import * as serverData from "../json-parsing/server-data"; +import * as urls from "../network/urls"; +import * as attributes from "./attributes"; +/// this is exposed for compatibility. If you find yourself needing to use this outside of the media api module you +/// probably have code smell. DO NOT USE. +export function buildURLFromRequest(objectGraph, request) { + var _a, _b; + const baseURL = request.href && request.href.length > 0 + ? baseURLForHref(request.href) + : baseURLForResourceType(objectGraph, request.isMixedMediaRequest, request.resourceType, request.countryCodeOverride); + const mediaApiURL = new urls.URL(baseURL); + if (serverData.isDefinedNonNullNonEmpty(request.resourceType)) { + for (const pathComponent of pathComponentsForRequest(request.resourceType, request.targetResourceType)) { + mediaApiURL.append("pathname", pathComponent); + } + } + if (request.isMixedMediaRequest) { + for (const [resourceType, ids] of request.idsByResourceType.entries()) { + mediaApiURL.param(`ids[${resourceType}]`, Array.from(ids).sort().join(",")); + } + } + else if (request.ids.size > 1 || request.useIdsAsQueryParam) { + mediaApiURL.param("ids", Array.from(request.ids).sort().join(",")); + } + else if (request.ids.size === 1) { + const id = request.ids.values().next().value; + mediaApiURL.append("pathname", id); + } + if (request.resourceType !== undefined) { + const trailingPathComponent = trailingPathComponentForResourceType(request.resourceType); + if (serverData.isDefinedNonNullNonEmpty(trailingPathComponent)) { + mediaApiURL.append("pathname", trailingPathComponent); + } + } + mediaApiURL.param("platform", (_a = request.platform) !== null && _a !== void 0 ? _a : undefined); + if (request.additionalPlatforms.size > 0) { + mediaApiURL.param("additionalPlatforms", Array.from(request.additionalPlatforms).sort().join(",")); + } + /** + * Add `extend` attributes. + * Note that when `useCustomAttributes` is true, there is `customArtwork` param even when `attributeIncludes` is initially empty. + * This due MAPI auto-extend for `artwork`, and lack of auto-extend for `customArtwork` + */ + if (request.attributeIncludes.size > 0 || request.useCustomAttributes) { + let extendAttributes = Array.from(request.attributeIncludes); + if (request.useCustomAttributes) { + extendAttributes = convertRequestAttributesToCustomAttributes(objectGraph, extendAttributes); + } + extendAttributes.sort(); + mediaApiURL.param("extend", extendAttributes.join(",")); + } + // Add age restriction if present. + if (serverData.isDefinedNonNull(request.ageRestriction) && objectGraph.bag.enableAgeRatingFilter) { + mediaApiURL.param("restrict[ageRestriction]", request.ageRestriction.toString()); + } + // Automatically extend iOS catalog requests for apps to include appBinaryTraits. + if (request.includeAppBinaryTraitsAttribute) { + request.includingScopedAttributes("apps", ["appBinaryTraits"]); + } + if (serverData.isDefinedNonNull(request.scopedAttributeIncludes)) { + for (const [dataType, scopedIncludes] of request.scopedAttributeIncludes.entries()) { + mediaApiURL.param(`extend[${dataType}]`, Array.from(scopedIncludes).sort().join(",")); + } + } + if (request.relationshipIncludes.size > 0) { + mediaApiURL.param("include", Array.from(request.relationshipIncludes).sort().join(",")); + } + if (serverData.isDefinedNonNull(request.scopedRelationshipIncludes)) { + for (const [dataType, scopedIncludes] of request.scopedRelationshipIncludes.entries()) { + mediaApiURL.param(`include[${dataType}]`, Array.from(scopedIncludes).sort().join(",")); + } + } + if (serverData.isDefinedNonNull(request.metaIncludes)) { + for (const [dataType, scopedMeta] of request.metaIncludes.entries()) { + mediaApiURL.param(`meta[${dataType}]`, Array.from(scopedMeta).sort().join(",")); + } + } + if (serverData.isSetDefinedNonNullNonEmpty(request.viewsIncludes)) { + mediaApiURL.param("views", Array.from(request.viewsIncludes).sort().join(",")); + } + if (serverData.isDefinedNonNull(request.kindIncludes)) { + for (const [dataType, scopedMeta] of request.kindIncludes.entries()) { + mediaApiURL.param(`kinds[${dataType}]`, Array.from(scopedMeta).sort().join(",")); + } + } + if (serverData.isDefinedNonNull(request.associateIncludes)) { + for (const [dataType, scopedAssociate] of request.associateIncludes.entries()) { + mediaApiURL.param(`associate[${dataType}]`, Array.from(scopedAssociate).sort().join(",")); + } + } + if (serverData.isDefinedNonNull(request.scopedAvailableInIncludes)) { + for (const [dataType, scopedAvailableIn] of request.scopedAvailableInIncludes.entries()) { + mediaApiURL.param(`availableIn[${dataType}]`, Array.from(scopedAvailableIn).sort().join(",")); + } + } + if (serverData.isDefinedNonNullNonEmpty(request.fields)) { + let extendedFields = Array.from(request.fields); + if (request.useCustomAttributes) { + extendedFields = convertRequestFieldsToCustomFields(extendedFields); + } + request.fields.sort(); + mediaApiURL.param("fields", extendedFields.join(",")); + } + if (serverData.isDefinedNonNull(request.limit) && request.limit > 0) { + mediaApiURL.param(`limit`, `${request.limit}`); + } + if (serverData.isDefinedNonNull(request.sparseLimit)) { + mediaApiURL.param(`sparseLimit`, `${request.sparseLimit}`); + } + if (serverData.isDefinedNonNull(request.scopedSparseLimit)) { + for (const [dataType, scopedLimit] of request.scopedSparseLimit.entries()) { + mediaApiURL.param(`sparseLimit[${dataType}]`, String(scopedLimit)); + } + } + if (serverData.isDefinedNonNull(request.sparseCount)) { + mediaApiURL.param(`sparseCount`, `${request.sparseCount}`); + } + for (const relationshipID of Object.keys(request.relationshipLimits).sort()) { + const limit = request.relationshipLimits[relationshipID]; + mediaApiURL.param(`limit[${relationshipID}]`, `${limit}`); + } + if (serverData.isDefinedNonNullNonEmpty(request.additionalQuery)) { + mediaApiURL.append("query", request.additionalQuery); + } + if (serverData.isDefinedNonNullNonEmpty(request.searchTerm)) { + // Search hints shouldn't add `search` to the end of the path name as the correct final path + // is `v1/catalog/us/search/suggestions`, which is handled by `trailingPathComponentForResourceType()` + // Search hints also shouldn't have the bubble param + if (isNothing(request.resourceType) || request.resourceType !== "search-hints") { + mediaApiURL.append("pathname", "search"); + mediaApiURL.param("bubble[search]", request.searchTypes.join(",")); + } + mediaApiURL.param("term", request.searchTerm); + } + if (serverData.isDefinedNonNullNonEmpty(request.enabledFeatures)) { + mediaApiURL.param("with", request.enabledFeatures.join(",")); + } + if (serverData.isDefinedNonNullNonEmpty(request.context)) { + mediaApiURL.param("contexts", request.context); + } + if (serverData.isDefinedNonNullNonEmpty(request.filterType) && + serverData.isDefinedNonNullNonEmpty(request.filterValue)) { + mediaApiURL.param(`filter[${request.filterType}]`, request.filterValue); + } + const language = objectGraph.bag.mediaApiLanguage; + // Only attach the language query param if: + // - there is a language available in the bag, and + // - a language has not been manually attached to the request. This is used for special situations to override the language for particular content, + // so it should take precedence over the default. + if (serverData.isDefinedNonNull(language) && serverData.isNull(request.additionalQuery["l"])) { + mediaApiURL.param("l", language); + } + mediaApiURL.host = (_b = hostForUrl(objectGraph, mediaApiURL, request)) !== null && _b !== void 0 ? _b : undefined; + mediaApiURL.protocol = "https"; + return mediaApiURL; +} +/** + * Get the media api base url for all requests. + * @param objectGraph Current object graph + * @param isMixedCatalogRequest Whether the request intends to use mixed catalog + * @param type The request resource type + * @param overrideCountryCode A country code to override the bag value. + * @returns A built base URL string + */ +function baseURLForResourceType(objectGraph, isMixedCatalogRequest, type, overrideCountryCode) { + switch (type) { + case "personalization-data": + case "reviews": + case "app-distribution": + return `/v1/${endpointTypeForResourceType(type)}/`; + default: + const countryCode = isSome(overrideCountryCode) && overrideCountryCode.length > 0 + ? overrideCountryCode + : objectGraph.bag.mediaCountryCode; + const baseURL = `/v1/${endpointTypeForResourceType(type)}/${countryCode}`; + return isMixedCatalogRequest ? baseURL : `${baseURL}/`; + } +} +/** + * Get the media api base url for all requests that already have an href. + * @return {string} + */ +function baseURLForHref(href) { + return href; +} +function endpointTypeForResourceType(type) { + switch (type) { + case "apps": + case "app-events": + case "arcade-apps": + case "app-bundles": + case "charts": + case "contents": + case "developers": + case "eula": + case "in-apps": + case "multiple-system-operators": + case "user-reviews": + case "customers-also-bought-apps-with-download-intent": + return "catalog"; + case "categories": + case "editorial-pages": + case "editorial-items": + case "editorial-item-groups": + case "editorial-elements": + case "groupings": + case "multiplex": + case "multirooms": + case "rooms": + case "today": + case "collections": + return "editorial"; + case "ratings": + return "ratings"; + case "personalization-data": + case "reviews": + return "me"; + case "upsellMarketingItem": + case "landing": + return "engagement"; + case "landing:new-protocol": + return "recommendations"; + case "personal-recommendations": + return "recommendations"; + case "engagement-data": + return "engagement"; + case "app-distribution": + return "listing"; + default: + return "catalog"; + } +} +/** + * The path component to add for the given resource + */ +function pathComponentsForRequest(resourceType, targetResourceType) { + switch (resourceType) { + case "eula": + if (targetResourceType === undefined) { + return [resourceType]; // Might be modelled better as an error. + } + else { + return [resourceType, targetResourceType]; + } + case "landing:new-protocol": + return []; + case "landing": + if (targetResourceType === undefined) { + return ["search", resourceType]; // Might be modelled better as an error. + } + else { + return ["search", resourceType, targetResourceType]; + } + case "user-reviews": + return ["apps"]; + case "reviews": + return ["reviews", "apps"]; + case "multiplex": + return ["multiplex"]; + case "upsellMarketingItem": + return ["upsell", "marketing-items"]; + case "trending-contents": + return ["search", resourceType]; + case "customers-also-bought-apps-with-download-intent": + return ["apps"]; + case "searchLanding:see-all": + return []; + case "search-hints": + return []; + case "app-distribution": + return ["apps"]; + default: + return [resourceType]; + } +} +/** + * Add a component to the end of the path for the given resource + */ +function trailingPathComponentForResourceType(type) { + switch (type) { + case "user-reviews": + return "reviews"; + case "customers-also-bought-apps-with-download-intent": + return "view/customers-also-bought-apps-with-download-intent"; + case "collections": + return "contents"; + case "searchLanding:see-all": + return "view/see-all"; + case "search-hints": + return "search/suggestions"; + default: + return null; + } +} +function hostForUrl(objectGraph, url, request) { + var _a; + const path = (_a = url.pathname) !== null && _a !== void 0 ? _a : ""; + let host = null; + if (request.isStorePreviewRequest) { + host = objectGraph.bag.mediaPreviewHost; + } + else if (request.isMediaRealmRequest) { + host = objectGraph.bag.mediaRealmHost; + } + else if (path.includes("search/landing")) { + // Special case <rdar://problem/50185140> RFW3: Use bag key "apps-media-api-edge-end-points" for "search/landing" end-point + // until we figure out a better way to test the paths + const useEdgeForSearchLanding = objectGraph.bag.edgeEndpoints.indexOf("landing") !== -1; + host = useEdgeForSearchLanding ? objectGraph.bag.mediaEdgeHost(objectGraph) : objectGraph.bag.mediaHost; + } + else if (request.resourceType === "app-distribution" && isSome(objectGraph.bag.appDistributionMediaAPIHost)) { + host = objectGraph.bag.appDistributionMediaAPIHost; + } + else if (request.isMixedMediaRequest && objectGraph.bag.mediaAPICatalogMixedShouldUseEdge) { + // CatalogMixed endpoint should be routed to edge when the bag is enabled. + host = objectGraph.bag.mediaEdgeHost(objectGraph); + } + else if (objectGraph.bag.edgeEndpoints.map((endpoint) => path.includes(endpoint)).reduce(truthReducer, false)) { + if (path.includes("search") && !path.includes("view/see-all")) { + host = objectGraph.bag.mediaEdgeSearchHost; + } + else { + host = objectGraph.bag.mediaEdgeHost(objectGraph); + } + } + else { + host = objectGraph.bag.mediaHost; + } + if (serverData.isNull(host)) { + host = "api.apps.apple.com"; + } + return host; +} +const truthReducer = (accumulator, current) => accumulator || current; +/** + * Performs a conversion for given attribute to fetch the customAttribute variant of it. + * @param objectGraph The object graph + * @param attribute Attribute to convert if needed, e.g. `artwork` + * @returns `string` attribute that is custom equivalent of `attribute`, or `attribute` unmodified. + */ +function convertRequestAttributesToCustomAttributes(objectGraph, requestAttributes) { + const convertedAttributes = requestAttributes.map((attribute) => { + var _a; + return (_a = attributes.attributeKeyAsCustomAttributeKey(attribute)) !== null && _a !== void 0 ? _a : attribute; + }); + /** + * `artwork` is an autoincluded resources, so `attributes` usually doesn't contain this explicitly :( + * Per MAPI contract, we "autoinclude" `customArtwork` explicitly for requests with custon attributes. + */ + convertedAttributes.push("customArtwork"); + /** + * `iconArtwork` is not autoincluded, but we need to ensure it is always requested even alongside its + * custom counterpart, `customIconArtwork`. This is because we might be viewing a macOS only app on iOS, + * where custom attributes are supported, but not available for macOS apps. + */ + if (requestAttributes.includes("iconArtwork")) { + convertedAttributes.push("iconArtwork"); + } + /** + * `customDeepLink` is always desired as an included resource in case an app decides to use a custom tap destination. + * Per MAPI contract, we "autoinclude" `customDeepLink` explicitly for all requests with custom attributes. + */ + convertedAttributes.push("customDeepLink"); + return convertedAttributes; +} +/** + * Performs the conversion for given field value (which may specify `attributes` keys) to customAttribute variant of it. + */ +function convertRequestFieldsToCustomFields(requestFields) { + const convertedFields = requestFields.map((fieldName) => { + var _a; + return (_a = attributes.attributeKeyAsCustomAttributeKey(fieldName)) !== null && _a !== void 0 ? _a : fieldName; + }); + // DON'T include `customArtwork` for request `field` conversion. Only specify if `artwork` was initially in `requestFields`. + return convertedFields; +} +//# sourceMappingURL=url-builder.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/media/util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/media/util.js new file mode 100644 index 0000000..77ed5c2 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/media/util.js @@ -0,0 +1,185 @@ +import { noContentError, notFoundError } from "./network"; +/** + * Validate an untrusted adam ID without contacting the server. + * + * This allows avoiding the work of calling the server with completely bogus + * IDs. It's scoped to web since ID formats can change and web can be updated + * easily (whereas backporting to older native clients is trickier). + * + * @param {AppStoreObjectGraph} objectGraph + * @param {string} id - the Adam ID to validate + * @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise + */ +export function validateAdamId(objectGraph, id) { + if (objectGraph.client.isWeb && !isAdamId(id)) { + throw noContentError(); + } +} +/** + * Check if an ID is a valid Adam ID. + * + * @param {string} id - string to validate + * @return {boolean} true if valid, false otherwise + */ +function isAdamId(id) { + // Media API actually validates if the number <= 2^63-1. But doubles in + // JavaScript cannot precisely represent this (the closest number is >2^63) + // so checking this requires BigInt, which might not be available. At the + // end of the day, checking this is likely not worth the complexity. 2^63-1 + // is 9223372036854775807 so we just restrict the number of digits. + // + // See: https://github.pie.apple.com/its/amp-enums/blob/f75500b44f871f35ba3ce459a5ff4c9f225e71b0/src/main/java/com/apple/jingle/store/IdSpace.java#L131-L140 + // See: https://github.com/google/guava/blob/869a75a1e3ff85d36672a3cd154772dc90c7b3d2/guava/src/com/google/common/primitives/Longs.java#L400-L440 + // See: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/Long.html#MAX_VALUE + return /^\d{1,19}$/.test(id); +} +/** + * Validate an untrusted Featured Content ID without contacting the server. + * + * This allows avoiding the work of calling the server with completely bogus + * IDs. It's scoped to web since ID formats can change and web can be updated + * easily (whereas backporting to older native clients is trickier). + * + * @param {AppStoreObjectGraph} objectGraph + * @param {string} id the FC ID to validate + * @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise + */ +export function validateFcId(objectGraph, id) { + if (objectGraph.client.isWeb && !isFcId(id)) { + throw noContentError(); + } +} +/** + * Check if an ID is a valid Featured Content (FC) ID. + * + * @param {string} id - string to validate + * @return {boolean} true if valid, false otherwise + */ +function isFcId(id) { + // FcIds are actually Adam IDs under the hood. + // See: https://github.pie.apple.com/its/Jingle/blob/d2d051c9ef2891f72d5f02f5bbbf2d7748afa7b9/MZStoreComponents/src/main/java/com/apple/jingle/app/store/editorial/SFEditorialHelper.java#L293 + return isAdamId(id); +} +/** + * Validate an untrusted (Chart) Genre ID without contacting the server. + * + * This allows avoiding the work of calling the server with completely bogus + * IDs. It's scoped to web since ID formats can change and web can be updated + * easily (whereas backporting to older native clients is trickier). + * + * @param {AppStoreObjectGraph} objectGraph + * @param {string} id the genre ID to validate + * @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise + */ +export function validateGenreId(objectGraph, id) { + if (objectGraph.client.isWeb && !isGenreId(id)) { + throw noContentError(); + } +} +/** + * Check if an ID is a valid (Chart) Genre ID. + * + * @param {string} id - string to validate + * @return {boolean} true if valid, false otherwise + */ +function isGenreId(id) { + // Genre IDs are Java int (signed 32-bit). + // + // Technically negative is not excluded, but all charts are positive, so + // filter out negative. + // + // See: https://github.pie.apple.com/its/Jingle/blob/main/shared/reference-data-logic/MZReferenceDataLogic/src/main/java/com/apple/jingle/eo/MZGenreService.java#L555 + return isPositiveJavaInt(id); +} +/** + * Validate an untrusted Grouping ID without contacting the server. + * + * This allows avoiding the work of calling the server with completely bogus + * IDs. It's scoped to web since ID formats can change and web can be updated + * easily (whereas backporting to older native clients is trickier). + * + * @param {AppStoreObjectGraph} objectGraph + * @param {string} id the grouping ID to validate + * @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise + */ +export function validateGroupingId(objectGraph, id) { + if (objectGraph.client.isWeb && !isGroupingId(id)) { + throw noContentError(); + } +} +/** + * Check if an ID is a valid Grouping ID. + * + * @param {string} id - string to validate + * @return {boolean} true if valid, false otherwise + */ +function isGroupingId(id) { + // Grouping IDs are Java int (signed 32-bit) + // + // Technically negative does not fail to parse, but they're all positive in + // practice. + // + // See: https://github.pie.apple.com/its/Jingle/blob/aaccec936f1feed227fd171ae66bb160cf38e497/MZStorePlatform/src/main/java/com/apple/jingle/store/mediaapi/util/SFMediaAPIEditorialUtil.java#L634 + return isPositiveJavaInt(id); +} +/** + * Test if a string contains a Java int. + * + * @param {string} s - the string to test + * @returns {boolean} true if the string is a stringified Java int, false otherwise + */ +function isPositiveJavaInt(s) { + // Java int is 32-bit signed. We can check bounds since signed integers are + // exactly representable as doubles (actually up to 2^53 is representable + // exactly). + // + // See: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/Integer.html#MAX_VALUE + return /^\d+$/.test(s) && parseInt(s, 10) <= 2147483647; +} +/** + * Validate an untrusted Editorial Shelf Collection ID without contacting the server. + * + * This allows avoiding the work of calling the server with completely bogus + * IDs. It's scoped to web since ID formats can change and web can be updated + * easily (whereas backporting to older native clients is trickier). + * + * @param {AppStoreObjectGraph} objectGraph + * @param {string} id the editorial shelf collection ID to validate + * @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise + */ +export function validateEditorialShelfCollectionId(objectGraph, id) { + if (objectGraph.client.isWeb && !isEditorialShelfCollectionId(id)) { + throw noContentError(); + } +} +/** + * Check if an ID is a valid editorial shelf collection ID. + * + * @param {string} id - string to validate + * @return {boolean} true if valid, false otherwise + */ +function isEditorialShelfCollectionId(id) { + // Ids are prefixed with "eds.". Beyond that they do have a UUID-type + // identifier that follows, but that seems more liable to change. + // + // See: https://github.pie.apple.com/its/amp-enums/blob/f75500b44f871f35ba3ce459a5ff4c9f225e71b0/src/main/java/com/apple/jingle/store/IdSpace.java#L43C5-L43C20 + // See: https://github.pie.apple.com/its/amp-enums/blob/f75500b44f871f35ba3ce459a5ff4c9f225e71b0/src/main/java/com/apple/jingle/store/IdSpace.java#L250-L252 + return id.startsWith("eds."); +} +/** + * Validates if a request can be performed for `vision` platform content, based on + * the feature flag is disabled. + * + * @param {AppStoreObjectGraph} objectGraph The application's state graph. + * @throws {NetworkError} Throws a 404 error if access is restricted. + * @returns {void} + */ +export function validateNeedsVisionRestriction(objectGraph) { + var _a; + if (objectGraph.client.isWeb && + ((_a = objectGraph.activeIntent) === null || _a === void 0 ? void 0 : _a.previewPlatform) === "vision" && + !objectGraph.bag.enableVisionPlatform) { + throw notFoundError(); + } +} +//# sourceMappingURL=util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/buy-parameters.js b/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/buy-parameters.js new file mode 100644 index 0000000..a48e886 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/buy-parameters.js @@ -0,0 +1,112 @@ +import { isNothing, isSome } from "@jet/environment/types/optional"; +/** + * An object which allows buy parameters to be operated on like a hash map. + */ +export class BuyParameters { + /** + * Create a buy parameters object with an optional string representation. + * + * @param buyParams A string containing buy parameters. + */ + constructor(buyParams) { + this._values = {}; + if (isSome(buyParams) && buyParams.length > 0) { + const pairs = buyParams.split("&"); + for (const pair of pairs) { + const [encodedKey, encodedValue] = pair.split("="); + const key = decodeURIComponent(encodedKey); + const value = isNothing(encodedValue) ? "" : decodeURIComponent(encodedValue); + this._values[key] = value; + } + } + } + /** + * Determine the search key to use for the given parameters. + * + * @param key The key to determine the search value for. + * @param prefix An optional prefix to prepend to `key`. + * @returns A key which can be used to store and retrieve + * values in this buy parameters object. + */ + _searchKey(key, prefix) { + if (key.length === 0) { + throw new Error("key may not be zero length"); + } + if (isNothing(prefix) || prefix.length === 0) { + return key; + } + else { + return `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`; + } + } + /** + * Returns the value associated with a given key. + * + * @param key The key to retrieve the associated value for. + * @param keyPrefix The prefix to add to the key. Defaults to `mt`. + * @returns The value associated with `key`, or `undefined` is none is found. + */ + get(key, keyPrefix = "mt") { + const searchKey = this._searchKey(key, keyPrefix); + return this._values[searchKey]; + } + /** + * Add a given key-value pair to this buy parameters. + * + * If a value was previously associated with `key`, + * it will be replaced with `value`. + * + * @param key The key to associate `value` with in these buy parameters. + * @param value The value to add to the buy parameters. If value is `undefined` + * or `null`, any values previously associated with `key` will be removed. + * @param keyPrefix The prefix to add to the key. Defaults to `mt`. + * @returns `this` + */ + set(key, value, keyPrefix = "mt") { + const searchKey = this._searchKey(key, keyPrefix); + if (isNothing(value)) { + delete this._values[searchKey]; + } + else { + this._values[searchKey] = value; + } + return this; + } + /** + * Convert this buy parameters to its string representation. + */ + toString() { + let buyParams = ""; + for (const key of Object.keys(this._values)) { + const value = this._values[key]; + if (buyParams.length > 0) { + buyParams += "&"; + } + buyParams += encodeURIComponent(key); + buyParams += "="; + buyParams += encodeURIComponent(value); + } + return buyParams; + } + /** + * Convert the buy params into a map representation, in which + * the keys and values are encoded. + */ + toEncodedMap() { + const map = {}; + for (const key of Object.keys(this._values)) { + const value = this._values[key]; + const encodedKey = encodeURIComponent(key); + const encodedValue = encodeURIComponent(value); + map[encodedKey] = encodedValue; + } + return map; + } + /** + * Buy params in a raw (non url encoded) map representation. + */ + toRawMap() { + return { ...this._values }; + } +} +//# sourceMappingURL=buy-parameters.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/cookies.js b/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/cookies.js new file mode 100644 index 0000000..5005b72 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/cookies.js @@ -0,0 +1,27 @@ +import { isNothing } from "@jet/environment/types/optional"; +/** + * Iterate the cookies contained in a string. + * + * @param cookie A string containing zero or more cookies. + */ +export function* cookiesOf(cookie) { + if (isNothing(cookie)) { + return; + } + const rawEntries = cookie.split(";"); + for (const rawEntry of rawEntries) { + const keyEndIndex = rawEntry.indexOf("="); + if (keyEndIndex === -1) { + // If there's no splitter, treat the whole raw + // entry as the key and provide an empty value. + const key = decodeURIComponent(rawEntry).trim(); + yield { key, value: "" }; + } + else { + const key = decodeURIComponent(rawEntry.substring(0, keyEndIndex)).trim(); + const value = decodeURIComponent(rawEntry.substring(keyEndIndex + 1)).trim(); + yield { key, value }; + } + } +} +//# sourceMappingURL=cookies.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/metrics-identifiers-cache.js b/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/metrics-identifiers-cache.js new file mode 100644 index 0000000..96a487f --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/metrics/metrics-identifiers-cache.js @@ -0,0 +1,245 @@ +import { isNothing, isSome } from "@jet/environment"; +import * as validation from "@jet/environment/json/validation"; +import { makeMetatype } from "@jet/environment/util/metatype"; +/** + * @public + * Type to define which type of metrics identifier key to use to fetch identifiers, fields, etc. + */ +export var MetricsIdentifierType; +(function (MetricsIdentifierType) { + MetricsIdentifierType["client"] = "clientId"; + MetricsIdentifierType["user"] = "userId"; + MetricsIdentifierType["canonical"] = "canonicalAccountIdentifierOverride"; +})(MetricsIdentifierType || (MetricsIdentifierType = {})); +/** + * @public + * The default namespaces to provide to the native `MetricsIdStore` for fetching and caching + */ +export var MetricsIdentifiersDefaultBagNamespaces; +(function (MetricsIdentifiersDefaultBagNamespaces) { + MetricsIdentifiersDefaultBagNamespaces["client"] = "APPSTORE_ENGAGEMENT_CLIENT"; + MetricsIdentifiersDefaultBagNamespaces["user"] = "APPSTORE_ENGAGEMENT"; +})(MetricsIdentifiersDefaultBagNamespaces || (MetricsIdentifiersDefaultBagNamespaces = {})); +export var MetricsIdentifiersPaymentBagNamespaces; +(function (MetricsIdentifiersPaymentBagNamespaces) { + MetricsIdentifiersPaymentBagNamespaces["client"] = "APPSTORE_PAYMENTS_ENGAGEMENT_CLIENT"; + MetricsIdentifiersPaymentBagNamespaces["user"] = "APPSTORE_PAYMENTS_ENGAGEMENT"; +})(MetricsIdentifiersPaymentBagNamespaces || (MetricsIdentifiersPaymentBagNamespaces = {})); +export var MetricsIdentifiersPersonalizationBagNamespaces; +(function (MetricsIdentifiersPersonalizationBagNamespaces) { + MetricsIdentifiersPersonalizationBagNamespaces["user"] = "APPSTORE_PERSONALIZATION"; +})(MetricsIdentifiersPersonalizationBagNamespaces || (MetricsIdentifiersPersonalizationBagNamespaces = {})); +/** + * The payment context mappings for a metrics identifier key. + */ +export const paymentIdentifierContextMapping = { + [MetricsIdentifierType.client]: { + keyType: MetricsIdentifierType.client, + bagNamespace: MetricsIdentifiersPaymentBagNamespaces.client, + crossSyncDevice: false, + }, + [MetricsIdentifierType.user]: { + keyType: MetricsIdentifierType.user, + bagNamespace: MetricsIdentifiersPaymentBagNamespaces.user, + crossSyncDevice: true, + }, +}; +/** + * The default context mappings for a metrics identifier key. + */ +const defaultIdentifierContextMapping = { + [MetricsIdentifierType.client]: { + keyType: MetricsIdentifierType.client, + bagNamespace: MetricsIdentifiersDefaultBagNamespaces.client, + crossSyncDevice: false, + }, + [MetricsIdentifierType.user]: { + keyType: MetricsIdentifierType.user, + bagNamespace: MetricsIdentifiersDefaultBagNamespaces.user, + crossSyncDevice: true, + }, +}; +/** + * The personalization context mappings for a metrics identifier key. + */ +export const personalizationIdentifierContextMapping = { + [MetricsIdentifierType.user]: { + keyType: MetricsIdentifierType.user, + bagNamespace: MetricsIdentifiersPersonalizationBagNamespaces.user, + crossSyncDevice: true, + }, +}; +/** + * Represents a cache for metrics identifiers and fields. + */ +export class MetricsIdentifiersCache { + /** + * Constructs a new instance of the MetricsIdentifiersCache class. + * @param identifierContextMapping - The mapping of identifier types to key contexts. + */ + constructor(identifierContextMapping = defaultIdentifierContextMapping) { + this.cachedMetricsIds = {}; + this.cachedMetricsFields = {}; + /** + * A flag indicating whether a DSID fallback field should be added to the event fields. + */ + this.shouldAddDsIdFallbackField = false; + /** + * The DSID of the currently logged in user on this JS request. + */ + this.userDsId = null; + this.identifierContextMapping = identifierContextMapping; + } + /** + * Loads the values for the specified identifier types. + * @param objectGraph - The object graph. + * @param idTypes - The identifier types. + * @returns A promise that resolves to an array of loaded values. + */ + async loadValues(objectGraph, idTypes) { + if (objectGraph.bag.isMetricsUserIdFallbackEnabled && !objectGraph.user.isUnderThirteen) { + this.shouldAddDsIdFallbackField = true; + this.userDsId = objectGraph.user.dsid; + } + else { + this.shouldAddDsIdFallbackField = false; + this.userDsId = null; + } + const loadedValues = []; + for (const idType of idTypes) { + const idTypeLoadedValues = await this.loadValuesForIdType(objectGraph, idType); + loadedValues.push(idTypeLoadedValues); + } + for (const { idType, id, fields } of loadedValues) { + if (id) { + this.cachedMetricsIds[idType] = id; + } + if (fields) { + this.cachedMetricsFields[idType] = fields; + } + } + } + /** + * @param objectGraph The app store object graph for all our native dependencies + * @param idType The type of id we're loading values for + * @returns The MetricsIdStore values for this id type + */ + async loadValuesForIdType(objectGraph, idType) { + const returnValues = { + idType, + }; + const context = this.identifierContextMapping[idType]; + if (context) { + try { + const fields = await validation.context("MetricsIdentifiersCache:loadValues:metricsFields", async () => { + return await objectGraph.metricsIdentifiers.getMetricsFieldsForContexts([context]); + }); + if (isSome(fields)) { + returnValues["fields"] = fields; + const extractedIdValue = fields[idType]; + if (isSome(extractedIdValue) && + typeof extractedIdValue === "string" && + extractedIdValue.length > 0) { + returnValues["id"] = extractedIdValue; + } + } + } + catch (error) { + objectGraph.console.error(`Unable to fetch metrics fields for idType ${idType}`, error); + } + if (isNothing(returnValues["id"])) { + try { + const id = await validation.context("MetricsIdentifiersCache:loadValues:metricsIdentifier", async () => { + return await objectGraph.metricsIdentifiers.getIdentifierForContext(context); + }); + if (isSome(id)) { + returnValues["id"] = id; + } + } + catch (error) { + objectGraph.console.error(`Unable to fetch metrics identifier for idType ${idType}`, error); + } + } + } + return returnValues; + } + /** + * Gets the metrics identifier for the specified identifier type. + * @param idType - The identifier type. + * @returns The metrics identifier, or undefined if not found. + */ + getMetricsIdForType(idType) { + return this.cachedMetricsIds[idType]; + } + /** + * Gets the metrics fields for the specified identifier types. + * @param idTypes - The identifier types. + * @returns The metrics fields, or undefined if not found. + */ + getMetricsFieldsForTypes(idTypes) { + const fieldsForTypes = idTypes.map((idType) => { var _a; return (_a = this.cachedMetricsFields[idType]) !== null && _a !== void 0 ? _a : {}; }); + const eventFields = Object.assign({}, ...fieldsForTypes); + if (this.shouldAddDsIdFallbackField && idTypes.indexOf(MetricsIdentifierType.user) !== -1) { + this.addDsIdFallbackFieldIfNecessary(eventFields); + } + return eventFields; + } + /** + * Adds a DSID fallback field to the event fields if necessary. + * @param eventFields - The event fields. + */ + addDsIdFallbackFieldIfNecessary(eventFields) { + const existingUserId = eventFields[MetricsIdentifierType.user]; + const isExistingUserIdInvalid = isNothing(existingUserId) || + typeof existingUserId !== "string" || + existingUserId.length === 0 || + existingUserId.length === MetricsIdentifiersCache.clientGeneratedUserIdLength; + if (isExistingUserIdInvalid && isSome(this.userDsId) && this.userDsId.length > 0) { + eventFields["dsId"] = this.userDsId; + } + } + /** + * Allows the setting of an app-level canonical account override for downstream metrics computations + * @param canonicalAccountIdentifier - The identifier to use when operated with canonical account overides + */ + setCanonicalAccountIdentifierOverride(canonicalAccountIdentifier) { + if (canonicalAccountIdentifier.length >= 0) { + this.cachedMetricsFields[MetricsIdentifierType.canonical] = { + [MetricsIdentifierType.canonical]: canonicalAccountIdentifier, + }; + } + } +} +/** + * The metatype of the MetricsIdentifiersCache class. + */ +MetricsIdentifiersCache.defaultMetatype = makeMetatype("app-store:metricsIdentifiersCache"); +MetricsIdentifiersCache.paymentMetatype = makeMetatype("app-store:paymentMetricsIdentifiersCache"); +MetricsIdentifiersCache.personalizationMetatype = makeMetatype("app-store:personalizationMetricsIdentifiersCache"); +/** + * The length of a client generated user ID. + */ +MetricsIdentifiersCache.clientGeneratedUserIdLength = 24; +export class MockMetricsIdentifiersCache extends MetricsIdentifiersCache { + constructor() { + super(); + this.cachedMetricsIds = { + [MetricsIdentifierType.client]: "1a2b3c4d5e", + [MetricsIdentifierType.user]: "1a2b3c4d5e", + }; + this.cachedMetricsFields = { + [MetricsIdentifierType.client]: { + clientId: "1a2b3c4d5e", + }, + [MetricsIdentifierType.user]: { + userNs: "mockuserNs", + userId: "1a2b3c4d5e", + metricsId: 2, + }, + [MetricsIdentifierType.canonical]: { + canonicalAccountIdentifierOverride: "mockCanonicalAccountIdentifier", + }, + }; + } +} +//# sourceMappingURL=metrics-identifiers-cache.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/network/http.js b/node_modules/@jet-app/app-store/tmp/src/foundation/network/http.js new file mode 100644 index 0000000..34c6a9a --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/network/http.js @@ -0,0 +1,42 @@ +/** + * Created by keithpk on 12/3/16. + */ +/** + * The `FormBuilder` class can be used to construct HTTP + * the form parameters for use with an HTTP post operation. + */ +export class FormBuilder { + /** + * The content type to use for form parameters. + */ + static get contentType() { + return "application/x-www-form-urlencoded"; + } + /** + * Construct an empty form builder. + */ + constructor() { + this._params = ""; + } + /** + * Append a parameter to the builder's form parameters. + * @param key The key of the parameter. + * @param value The value of the parameter. + * @return The builder. + */ + param(key, value) { + if (key && value) { + const separator = this._params.length > 0 ? "&" : ""; + this._params += `${separator}${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + } + return this; + } + /** + * Create and return a form parameters string based on the contents of the builder. + * @return The form parameters string. + */ + build() { + return this._params; + } +} +//# sourceMappingURL=http.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/network/network.js b/node_modules/@jet-app/app-store/tmp/src/foundation/network/network.js new file mode 100644 index 0000000..db302e7 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/network/network.js @@ -0,0 +1,123 @@ +/** + * Created by ls on 9/7/2018. + * + * This `network.ts` is the NON-MEDIA API arm of network fetch requests. + * It is built on `Network` object and provides standard functionality, such as: + * 1. Parsing the body into specific format. + * 2. Adding timing metrics onto blob. + * + * This should *only* be used for objects that should have timing metrics, i.e. requests to Non-MediaAPI endpoints + * that will ultimately render some whole page. Otherwise, use `objectGraph.network.fetch` directly. + * + * @see `src/media/network.ts` for fetching from Media API endpoints + */ +import { isSome } from "@jet/environment/types/optional"; +import * as serverData from "../json-parsing/server-data"; +/** @public */ +// eslint-disable-next-line @typescript-eslint/no-namespace +export var ResponseMetadata; +(function (ResponseMetadata) { + ResponseMetadata.requestedUrl = "_jet-internal:metricsHelpers_requestedUrl"; + /** + * Symbol used to place timing metrics values onto fetch responses + * without interfering with the data returned by the server. + */ + ResponseMetadata.timingValues = "_jet-internal:metricsHelpers_timingValues"; + /** + * Key used to access the page information gathered from a response's headers + */ + ResponseMetadata.pageInformation = "_jet-internal:metricsHelpers_pageInformation"; + /** + * Key used to access the content max-age gathered from a response's headers. + */ + ResponseMetadata.contentMaxAge = "_jet-internal:responseMetadata_contentMaxAge"; +})(ResponseMetadata || (ResponseMetadata = {})); +/** + * Module's private fetch implementation built off `net` global. + * + * @param {FetchRequest} request describes fetch request. + * @param {(value: string) => Type} parser Some function parsing response body `string` into specific type. + * @returns {Promise<Type>} Promise resolving to specific object. + * @throws {Error} Throws error if status code of request is not 200. + * + * @note Similar to `fetchWithToken` in `media` module, but excludes media token specific functionality. + * Top level data fetches to endpoints that don't do redirects, and can benefit from metrics should + * call methods that build off of this instead of calling `objectGraph.network.fetch(...)` directly. + */ +async function fetch(objectGraph, request, parser) { + const response = await objectGraph.network.fetch(request); + if (!response.ok) { + throw Error(`Bad Status code ${response.status} for ${request.url}`); + } + const parseStartTime = Date.now(); + const result = parser(response.body); + const parseEndTime = Date.now(); + if (result) { + // Build full network timing metrics. + const completeTimingMetrics = networkTimingMetricsWithParseTime(response.metrics, parseStartTime, parseEndTime); + if (serverData.isDefinedNonNull(completeTimingMetrics)) { + result[ResponseMetadata.timingValues] = completeTimingMetrics; + } + } + result[ResponseMetadata.requestedUrl] = request.url.toString(); + return result; +} +/** + * Fetch from an endpoint with JSON response body. + * + * @param {FetchRequest} request to fetch from endpoint with JSON response.. + * @returns {Promise<Type>} Promise resolving to body of response parsed as `Type`. + * @throws {Error} Throws error if status code of request is not 200. + */ +export async function fetchJSON(objectGraph, request) { + return await fetch(objectGraph, request, (body) => { + if (isSome(body)) { + return JSON.parse(body); + } + else { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return {}; + } + }); +} +/** + * Fetch from an endpoint with XML response body. + * + * @param {FetchRequest} request to fetch from endpoint with XML response. + * @returns {Promise<Type>} Promise resolving to body of response parsed as `Type`. + * @throws {Error} Throws error if status code of request is not 200. + */ +export async function fetchPlist(objectGraph, request) { + return await fetch(objectGraph, request, (body) => { + if (isSome(body)) { + return objectGraph.plist.parse(body); + } + else { + throw new Error(`Could not fetch Plist, response body was not defined for ${request.url}`); + } + }); +} +/** + * With network requests now being created and parsed in JS, different timing metrics are measured in both Native and JS. + * This function populates the missing values from `HTTPTimingMetrics`'s native counterpart, `JSNetworkPerformanceMetrics`. + * + * @param {HTTPTimingMetrics[] | null} responseMetrics Array of response metrics provided by native. + * @param {number} parseStartTime Time at which response body string parse began in JS. + * @param {number} parseEndTime Time at which response body string parse ended in JS. + * @returns {HTTPTimingMetrics | null} Fully populated timing metrics, or `null` if native response provided no metrics events to build off of. + */ +function networkTimingMetricsWithParseTime(responseMetrics, parseStartTime, parseEndTime) { + // No metrics events to build from. + if (serverData.isNull(responseMetrics) || responseMetrics.length === 0) { + return null; + } + // Append parse times to first partial timing metrics from native. + const firstPartialTimingMetrics = { + ...responseMetrics[0], + parseStartTime: parseStartTime, + parseEndTime: parseEndTime, + }; + // Timing metrics with all properties populated. + return firstPartialTimingMetrics; +} +//# sourceMappingURL=network.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/network/url-constants.js b/node_modules/@jet-app/app-store/tmp/src/foundation/network/url-constants.js new file mode 100644 index 0000000..f25d9cb --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/network/url-constants.js @@ -0,0 +1,452 @@ +/** + * Copied from SKUIUrl these action types come in urls like scheme:/?action=[UrlAction] + */ +/* tslint:disable:variable-name */ +export const Protocol = { + /// Protocol used for internal app store routing + internal: "x-as3-internal", + /// Protocol used for routing HTTP URLs + http: "http", + /// Protocol used for routing standard HTTPS URLs + https: "https", + /// The protocol used for iTunes app urls + itms: "itms", + /// The protocol used for secure iTunes app urls + itmss: "itmss", + /// Protocol used to target the app store + itmsAppss: "itms-appss", + /// Protocol used to target the app store + itmsApps: "itms-apps", + /// Protocol used to target the messages app store + itmsMessagess: "itms-messagess", + /// Protocol used to target the messages app store + itmsMessages: "itms-messages", + /// Protocol used to target the watch app store + itmsWatchs: "itms-watchs", + /// Protocol used to target the watch app store + itmsWatch: "itms-watch", + /// Protocol used for internal file routing + file: "file", + /// Protocol used for referencing resources in the host app's bundles. + resource: "resource", + /// Protocol used to target the mac app store + macappstore: "macappstore", + /// Protocol used to target the mac app store + macappstores: "macappstores", + /// Legacy protocol used to target the tv app store, + /// kept to maintain backwards compatibility. + tvappstoreLegacy: "com.apple.tvappstore", + /// Protocol used to target the tv app store + tvappstore: "com.apple.TVAppStore", + /// Protocol used to target Arcade + arcade: "com.apple.Arcade", + /// Protocol used to target StoreKitUI App Store service + storeKitUIServiceAppStore: "appstore-ui", +}; +export const Path = { + // Path for loading a shelf + shelf: "shelf", + // Path for MZStore requests + store: "WebObjects/MZStore.woa/wa", + // Path for MZStoreElements requests + storeElements: "WebObjects/MZStoreElements.woa/wa", + // Path for performing a lookup + lookup: "lookup", + // Path for performing a docTypeLookup + docTypeLookup: "docTypeLookup", + // Path for performing an install sheet + install: "install", + // Path to the grouping page + grouping: "viewGrouping", + // Path to the artist page + viewArtist: "viewArtist", + // Path to the product page + viewSoftware: "viewSoftware", + // Path to the today page + today: "today", + // Path to the arcade main page + arcade: "arcade", + arcadeUpsellPreview: "arcadeUpsellPreview", + // Path to Arcade See All + arcadeSeeAllGames: "arcadeSeeAllGames", + // Pagination path for Arcade See All + arcadeSeeAllGamesLoadMore: "arcadeSeeAllGamesLoadMore", + // Path to the genre page (which is a grouping page) + genre: "genre", + // Path to the genre page (which is a grouping page) + viewGenre: "viewGenre", + recommendationsSeeAll: "recommendationsSeeAll", + // Path for a screenshots lookup. + screenshots: "screenshots", + // Path to a single room + room: "viewRoom", + // Path to a programmed multi-room + multiRoom: "viewMultiRoom", + // Path to the main top charts page + charts: "charts", + // Path to the product page + product: "app", + // Path to the game page + game: "game", + // Path to siri product page deep links on tvOS App Store. + siri: "siri", + // Path to the product page for an app bundle. + productBundle: "app-bundle", + // Path component for the developer page + developer: "developer", + artist: "artist", + // Path component for the ratings shelf + ratings: "ratings", + // Path component for the reviews shelf + reviews: "reviews", + // Path to view reviews for MZStore + viewReviews: "viewContentsUserReviews", + // Path component for the personalized review shelf + personalizedReviews: "personalizedReviews", + // Path to editorial items (articles) + article: "article", + story: "story", + editorialItem: "editorialItem", + viewEditorialItem: "viewEditorialItem", + todayCardPreview: "todayCardPreview", + // Path component for MSO rooms + mso: "mso", + // Path for resetting the storefront + resetAndRedirect: "resetAndRedirect", + // Account + account: "account", + personalizationTransparency: "personalizationTransparency", + // Path component for an href-driven route. + href: "href", + // Path component for the EULA. + eula: "eula", + // Path component for the EULA on tvOS. + tvEula: "tv-eula", + // Path component for the Privacy Policy on tvOS. + privacyPolicy: "privacyPolicy", + // Path component for the Safety and Compliance Page on tvOS. + safetyCompliance: "safety-compliance", + // Path component for recommendations + appsForYou: "apps-for-you", + // Path for the storeFront + storeFront: "storeFront", + // Path for the tab + tab: "tab", + // Path component for the search landing page. + searchLandingPage: "searchLandingPage", + // Path component for search trending content page. + searchTrendingApps: "searchTrendingApps", + // Path for showing product privacy detail page + privacyDetail: "privacyDetail", + // Path for showing product privacy definitions page + privacyDefinitions: "privacyDefinitions", + // Path for the report a problem link + reportAProblem: "reportaproblem.apple.com/store", + // Path for showing a game center player profile + gameCenterProfile: "gameCenterProfile", + // Path for showing the ODP page + onDeviceRecommendations: "onDeviceRecommendations", + // Path for editorial pages + editorialPage: "editorialPage", + // Path to acessibility details + accessibilityDetails: "accessibilityDetails", + // ---------------------- + // Debug + // ---------------------- + test: "test", + shelfTypes: "shelfTypes", + groupingTest: "grouping", + builtIn: "builtIn", + shelfTextTest: "shelfText", + shelfLockupsTest: "shelfLockups", + shelfMiscTest: "shelfMisc", + lockupTest: "lockupTest", + articleTestArtwork: "articleArtworkTest", + articleTestSingleAppIcon: "articleSingleAppIconTest", + articleTestSingleAppIconHeroArt: "articleSingleAppIconHeroArtTest", + articleTestBrandedApp: "articleBrandedAppTest", + articleTestMultiAppTwo: "articleMultiAppTwoTest", + articleTestMultiAppThree: "articleMultiAppThreeTest", + articleTestGrid: "articleGridTest", + articleTestInAppPurchase: "articleInAppPurchaseTest", +}; +export const Host = { + // Legacy Mac Software Update URL + showUpdatesPage: "showUpdatesPage", + // Legacy Deep Link Actions + showAccountPage: "showAccountPage", + showPurchasesPage: "showPurchasesPage", + showSubscriptionsPage: "showSubscriptionsPage", + // Legacy MAS Safari Extension Search + searchExtensions: "searchExtensions", + // Legacy search.itunes.apple.com redirected urls + searchItunes: "search.itunes.apple.com", + // Legacy itunes.apple.com redirected urls + iTunes: "itunes.apple.com", + // Urls allowing programming to preview programmed groupings + storePreview: "storepreview.apple.com", + // Urls allowing programming to preview programmed stories + appsPreview: "preview.apps.apple.com", + // Legacy product url for tvOS App Store + product: "product", + // Host for arcade subscribe page + arcadeSubscribePage: "arcadeSubscribePage", + // Host for an upsell that must necessarily go through our own Arcade subscribe page, + // rather than through an AMS marketing item page. + arcadeSubscribePageCustomContext: "arcadeSubscribePageCustomContext", + // Host for arcade welcome page + arcadeWelcomePage: "arcadeWelcomePage", + // Host for Arcade See All page for standalone case. + arcadeSeeAllPage: "arcadeSeeAllGames", + // Host for the deep link from an Arcade app clip. + appClipSubscribe: "appClipSubscribe", + familyCircle: "familyCircle", + // Host for showing spam blocking extensions + spamBlockingExtensions: "spamBlockingExtensions", + // Host for showing Safari extensions + safariExtensions: "safariExtensions", + // Host for app launch trampoline + launchApp: "launchApp", + // Host for SharePlay more deeplink + sharePlayApps: "sharePlayApps", + // Host for opening a deeplink at the end of Buddy + buddyOnboarding: "buddyOnboarding", + // Host for Arcade download (starter) pack page + arcadeDownloadPackPage: "arcadeDownloadPack", + // Host for showing settings page + showSettingsPage: "showSettingsPage", + // Host for showing settings page + showHiddenPurchasesPage: "showHiddenPurchases", +}; +/// The different preview hosts that require the media api preview endpoint +export const previewHosts = new Set([Host.storePreview, Host.appsPreview]); +export const Parameters = { + // Generic ID + id: "id", + // Generic IDs + ids: "ids", + // Product variant ID for given `id` + productVariantID: "ppid", + // Country Code + countryCode: "cc", + // Language override + language: "l", + // Featured Content ID + featuredContentId: "fcId", + fetchData: "fetchData", + isTodaySection: "isTodaySection", + isTodayFeedPreview: "isTodayFeedPreview", + // Genre + genre: "genre", + // Bundle Identifier + bundleIdentifier: "bundleIdentifier", + // BundleID + bundleId: "bundleId", + // Spotlight In-App Purchase Identifier + offerName: "offerName", + // Top Charts + charts: "charts", + ages: "ages", + chart: "chart", + types: "types", + // v0 urls (used e.g. when redirected from MZContentLink URLs). + v0: "v0", + // Action urls + action: "action", + // Type parameter + type: "type", + context: "context", + isArcade: "isArcade", + isSubscribed: "isSubscribed", + isTrialAvailable: "isTrialAvailable", + isTrialEnrolled: "isTrialEnrolled", + groupingFeaturedContentId: "groupingFeaturedContentId", + editorialPageShelfType: "editorialPageShelfType", + nativeGroupingShelfId: "nativeGroupingShelfId", + isArcadeSeeAllGamesShelf: "isArcadeSeeAllGamesShelf", + isGameCenterActivityFeedShelf: "isGameCenterActivityFeedShelf", + isGameCenterPlayerShelf: "isGameCenterPlayerShelf", + isGameCenterPlayerRibbonItem: "isGameCenterPlayerRibbonItem", + isGameCenterAchievementsShelf: "isGameCenterAchievementsShelf", + isGameCenterContinuePlayingShelf: "isGameCenterContinuePlayingShelf", + isGameCenterPopularWithYourFriendsShelf: "isGameCenterPopularWithYourFriendsShelf", + isGameCenterSuggestedFriendsShelf: "isGameCenterSuggestedFriendsShelf", + isGameCenterReengagementShelf: "isGameCenterReengagementShelf", + isOnDeviceRecommendationsShelf: "isOnDeviceRecommendationsShelf", + isOnDeviceSearchHistoryShelf: "isOnDeviceSearchHistoryShelf", + isSearchFocusHeaderShelf: "isSearchFocusHeaderShelf", + isArcadeDownloadPackShelfPlaceholder: "isArcadeDownloadPackShelfPlaceholder", + onDeviceRecommendationsUseCase: "onDeviceRecommendationsUseCase", + onDevicePersonalizationUseCase: "onDevicePersonalizationUseCase", + // Denotes that this url is coming from the purchases page. + isPurchasesApp: "isPurchasesApp", + // Used for single sign on, the parameter is passed from store kit + isViewOnly: "isViewOnly", + // Determines whether unlisted apps should be returned in the response. + // Note that for direct lookups, MAPI allows the App Store to view unlisted + // apps without this parameter, although they would like to eventually gate + // the behavior behind this parameter. + includeUnlistedApps: "includeUnlistedApps", + enabled: "enabled", + href: "href", + recoMetrics: "recoMetrics", + // Used on article urls when the TodayCard is showing fallback media + showingFallbackMedia: "showingFallbackMedia", + path: "path", + useReleaseId: "useReleaseId", + // The client identifier that should be used to display the item, regardless of host device. + clientIdentifierOverride: "clientIdentifierOverride", + // The message attached to the subscribe page, for ATB. + subscribePageMessage: "message", + // The ID for a specific editorial item, in the case of a subscribe page. + editorialItem: "editorialItem", + // The identifer for the ATB request. + askToBuyId: "askToBuyId", + // Indicates an ID, when the 'app' disambiguation is necessary. + appId: "appId", + // Whether the URL has been launched from a PPT (Purple Performance Test). + isPPT: "isPPT", + // How the shelf is sorted, represented by a value of `ArcadeSeeAllGamesPageSort` + sort: "sort", + // Featured Content ID + grouping: "grouping", + // Code Parameter, e.g. for Redeem code + code: "code", + // Parameter to include attribution on offer action for post subcribge + includePostSubscribeAttribution: "includePostSubscribeAttribution", + // Campaign Token + campaignToken: "ct", + // Provider Token + providerToken: "pt", + // Q Token + qToken: "its_qt", + // Advertisement ID + advertisementId: "adId", + // The token used to pass arbitrary data in a url + token: "token", + // The current parse context for the a page + parseContext: "parseContext", + // An ID for a privacy type + privacyTypeId: "privacyTypeId", + // The token used to pass arbitrary data in a url + requestDescriptor: "requestDescriptor", + // Page Facet + ageRating: "ageRating", + // Page Facet + controllerSupport: "controllerSupport", + // Page Facet + multiplayerSupport: "multiplayerSupport", + // Page Facet + comingSoon: "comingSoon", + // Page Facet + binCompatGames: "binCompatGames", + // Page Facet + gamePreviews: "gamePreviews", + // Upsell (marketing item) marketing hint + offerHints: "offerHints", + // Indicates that a 204 network error should invalidate the App Store's widgets. + invalidateWidgetsOnFailure: "invalidateWidgetsOnFailure", + // Metrics + metrics: "metrics", + // App event ID + appEventId: "eventid", + // Offer Item ID + offerItemId: "offerItemId", + // App event deep link + appEventDeepLink: "deepLink", + // The use case param for collection deeplinking + useCaseShort: "uc", + // The collection id param for collection deeplinking + collectionId: "collection-id", + // The seed id param for collection deeplinking + seedId: "seed-id", + // Whether the shelf is the product page similar items shelf. + isShelfWithAd: "isShelfWithAd", + // The ad placement type for the shelf. Only used if `isShelfWithAd` is true. + shelfWithAdPlacementType: "shelfWithAdPlacementType", + // Provides a refresh type to route a refresh request for a shelf. + // See `refreshUrl` on `Shelf`. + shelfRefreshType: "refreshType", + // Param to denote that this url originated for preloading purposes. + isPreloading: "isPreloading", + // The name parameter for editorial pages with known names + name: "name", + // The editorial page shelf type + shelfType: "shelfType", + // The id of the shelf in a url + shelfId: "shelfId", + // Parameter for the onboarding cards for the today tab + onbaordingCardIds: "onboardingCardIds", + // Parameter for the today card previews to provide a future date and time + preview: "preview", + // The card config for used for the card on a today page + todayCardConfig: "todayCardConfig", + // The parameter that is expanded into the header used for today previews. + experimentId: "experimentId", + shortEditorialNotes: "shortEditorialNotes", + // User subscription status defined by Mercury and passed to Arcade download pack onboarding. + // It is used to select the right copy variant. + arcadeSubscriptionStatus: "subscriptionStatus", + // Used to indicate a web browser is being displayed in the web browser context via App Store Components. + webBrowser: "webBrowser", + // The id of the editorial-page that was used to create a url + editorialPageId: "editorialPageId", + // A filter ID to add to a editorial item request in order to fetch a certain version of the story. + editorialCardId: "filter[canvas:cardId]", + // A filter to add to the hydration call for the Today tab recommended candidates to fetch only recommendable apps. + filterRecommendable: "filter[recommendable]", + // A device family, used for accessibility labels + deviceFamily: "deviceFamily", +}; +// Install Page-specific parameters +export const InAppPurchaseInstallPageParameters = { + // The adamId for the IAP. + inAppPurchaseId: "inAppPurchaseId", +}; +// Product Page-specific parameters +export const ProductPageParameters = { + // Product URL + url: "productUrl", + // Whether an iAP is a subscription. + isSubscription: "isSubscription", + // The minimum version of the app to display + // Typically used to prevent older cached versions being displayed + // when deep-linking from an app clip. + minExternalVersionId: "minExternalVersionId", +}; +// Review Page-specific parameters +export const ReviewsPageParameters = { + // Product adamId + adamId: "adamId", + // Sort index for the reviews request + sort: "sort", +}; +export const MSOPageParameters = { + // Ids of available apps. + availableAdamIds: "availableAdamIds", +}; +export const ShelfParameters = { + // Shelf Title + title: "shelfTitle", + // Whether the shelf should auto-configure a see-all on fetch + shouldInferSeeAllFromFetchedItems: "shelfShouldInferSeeAllFromFetchedItems", + // Shelf content type + contentType: "shelfContentType", + // Shelf offer them + offerTheme: "shelfOfferTheme", + // metrics page information + metricsPageInformation: "metricsPage", +}; +export const ShareURLParameters = { + // Which client should be the link be opened with + clientSpecifier: "app", +}; +export const Hashes = { + // Reviews action anchor (#reviews) + reviews: "reviews", +}; +export const ShelfRefreshType = { + productPageSimilarItems: "productPageSimilarItems", +}; +/* tslint:enable:variable-name */ +//# sourceMappingURL=url-constants.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/network/urls.js b/node_modules/@jet-app/app-store/tmp/src/foundation/network/urls.js new file mode 100644 index 0000000..6abbd37 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/network/urls.js @@ -0,0 +1,382 @@ +/** + * Created by keithpk on 12/2/16. + */ +import { isNothing } from "@jet/environment/types/optional"; +const protocolRegex = /^([a-z][a-z0-9.+-]*:)(\/\/)?([\S\s]*)/i; +const queryParamRegex = /([^=?&]+)=?([^&]*)/g; +const componentOrder = ["hash", "query", "pathname", "host"]; +function splitUrlComponent(input, marker, style) { + const index = input.indexOf(marker); + let result; + let remainder = input; + if (index !== -1) { + const prefix = input.slice(0, index); + const suffix = input.slice(index + marker.length, input.length); + if (style === "prefix") { + result = prefix; + remainder = suffix; + } + else { + result = suffix; + remainder = prefix; + } + } + // log("Token: " + marker + " String: " + input, " Result: " + result + " Remainder: " + remainder) + return { + result: result, + remainder: remainder, + }; +} +export class URL { + constructor(url) { + this.query = {}; + if (!url) { + return; + } + // Split the protocol from the rest of the urls + let remainder = url; + const match = protocolRegex.exec(url); + if (match) { + // Pull out the protocol + let protocol = match[1]; + if (protocol) { + protocol = protocol.split(":")[0]; + } + this.protocol = protocol; + // Save the remainder + remainder = match[3]; + } + // Then match each component in a specific order + let parse = { remainder: remainder, result: undefined }; + for (const component of componentOrder) { + if (!parse.remainder) { + break; + } + switch (component) { + case "hash": { + parse = splitUrlComponent(parse.remainder, "#", "suffix"); + this.hash = parse.result; + break; + } + case "query": { + parse = splitUrlComponent(parse.remainder, "?", "suffix"); + if (parse.result) { + this.query = URL.queryFromString(parse.result); + } + break; + } + case "pathname": { + parse = splitUrlComponent(parse.remainder, "/", "suffix"); + if (parse.result) { + // Replace the initial /, since paths require it + this.pathname = "/" + parse.result; + } + break; + } + case "host": { + if (parse.remainder) { + const authorityParse = splitUrlComponent(parse.remainder, "@", "prefix"); + const userInfo = authorityParse.result; + const hostPort = authorityParse.remainder; + if (userInfo) { + const userInfoSplit = userInfo.split(":"); + this.username = decodeURIComponent(userInfoSplit[0]); + this.password = decodeURIComponent(userInfoSplit[1]); + } + if (hostPort) { + const hostPortSplit = hostPort.split(":"); + this.host = hostPortSplit[0]; + this.port = hostPortSplit[1]; + } + } + break; + } + default: { + throw new Error("Unhandled case!"); + } + } + } + } + set(component, value) { + if (!value) { + return this; + } + if (component === "query") { + if (typeof value === "string") { + value = URL.queryFromString(value); + } + } + switch (component) { + // Exhaustive match to make sure TS property minifiers and other + // transformer plugins do not break this code. + case "protocol": + this.protocol = value; + break; + case "username": + this.username = value; + break; + case "password": + this.password = value; + break; + case "port": + this.port = value; + break; + case "pathname": + this.pathname = value; + break; + case "query": + this.query = value; + break; + case "hash": + this.hash = value; + break; + default: + // The fallback for component which is not a property of URL object. + this[component] = value; + break; + } + return this; + } + get(component) { + switch (component) { + // Exhaustive match to make sure TS property minifiers and other + // transformer plugins do not break this code. + case "protocol": + return this.protocol; + case "username": + return this.username; + case "password": + return this.password; + case "port": + return this.port; + case "pathname": + return this.pathname; + case "query": + return this.query; + case "hash": + return this.hash; + default: + // The fallback for component which is not a property of URL object. + return this[component]; + } + } + append(component, value) { + const existingValue = this.get(component); + let newValue; + if (component === "query") { + if (typeof value === "string") { + value = URL.queryFromString(value); + } + if (typeof existingValue === "string") { + newValue = { existingValue, ...value }; + } + else { + newValue = { ...existingValue, ...value }; + } + } + else { + let existingValueString = existingValue; + if (!existingValueString) { + existingValueString = ""; + } + let newValueString = existingValueString; + if (component === "pathname") { + const pathLength = existingValueString.length; + if (!pathLength || existingValueString[pathLength - 1] !== "/") { + newValueString += "/"; + } + } + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands, @typescript-eslint/no-base-to-string + newValueString += value; + newValue = newValueString; + } + return this.set(component, newValue); + } + param(key, value) { + if (!key) { + return this; + } + if (!this.query) { + this.query = {}; + } + this.query[key] = value; + return this; + } + removeParam(key) { + if (!key || !this.query) { + return this; + } + if (this.query[key] !== undefined) { + delete this.query[key]; + } + return this; + } + /** + * Push a new string value onto the path for this url + * @returns URL this object with the updated path. + */ + path(value) { + return this.append("pathname", value); + } + pathExtension() { + var _a; + // Extract path extension if one exists + if (isNothing(this.pathname)) { + return null; + } + const lastFilenameComponents = (_a = this.pathname + .split("/") + .filter((item) => item.length > 0) // Remove any double or trailing slashes + .pop()) === null || _a === void 0 ? void 0 : _a.split("."); + if (lastFilenameComponents === undefined) { + return null; + } + if (lastFilenameComponents.filter((part) => { + return part !== ""; + }).length < 2 // Remove any empty parts (e.g. .ssh_config -> ["ssh_config"]) + ) { + return null; + } + return lastFilenameComponents.pop(); + } + /** + * Returns the path components of the URL + * @returns An array of non-empty path components from `urls`. + */ + pathComponents() { + if (!this.pathname) { + return []; + } + return this.pathname.split("/").filter((component) => component.length > 0); + } + /** + * Returns the last path component from this url, updating the url to not include this path component + * @returns String the last path component from this url. + */ + popPathComponent() { + if (!this.pathname) { + return null; + } + const lastPathComponent = this.pathname.slice(this.pathname.lastIndexOf("/") + 1); + if (lastPathComponent.length === 0) { + return null; + } + this.pathname = this.pathname.slice(0, this.pathname.lastIndexOf("/")); + return lastPathComponent; + } + /** + * Same as toString + * + * @returns {string} A string representation of the URL + */ + build() { + return this.toString(); + } + /** + * Converts the URL to a string + * + * @returns {string} A string representation of the URL + */ + toString() { + let url = ""; + if (this.protocol) { + url += this.protocol + "://"; + } + if (this.username) { + url += encodeURIComponent(this.username); + if (this.password) { + url += ":" + encodeURIComponent(this.password); + } + url += "@"; + } + if (this.host) { + url += this.host; + if (this.port) { + url += ":" + this.port; + } + } + if (this.pathname) { + url += this.pathname; + /// Trim off trailing path separators when we have a valid path + /// We don't do this unless pathname has elements otherwise we will trim the `://` + if (url.endsWith("/") && this.pathname.length > 0) { + url = url.slice(0, -1); + } + } + if (this.query && Object.keys(this.query).length) { + url += "?" + URL.toQueryString(this.query); + } + if (this.hash) { + url += "#" + this.hash; + } + return url; + } + // ---------------- + // Static API + // ---------------- + /** + * Converts a string into a query dictionary + * @param query The string to parse + * @returns The query dictionary containing the key-value pairs in the query string + */ + static queryFromString(query) { + const result = {}; + let parseResult = queryParamRegex.exec(query); + while (parseResult) { + const key = decodeURIComponent(parseResult[1]); + const value = decodeURIComponent(parseResult[2]); + result[key] = value; + parseResult = queryParamRegex.exec(query); + } + return result; + } + /** + * Converts a query dictionary into a query string + * + * @param query The query dictionary + * @returns {string} The string representation of the query dictionary + */ + static toQueryString(query) { + let queryString = ""; + let first = true; + for (const key of Object.keys(query)) { + if (!first) { + queryString += "&"; + } + first = false; + queryString += encodeURIComponent(key); + const value = query[key]; + if (value && value.length) { + queryString += "=" + encodeURIComponent(value); + } + } + return queryString; + } + /** + * Convenience method to instantiate a URL from a string + * @param url The URL string to parse + * @returns {URL} The new URL object representing the URL + */ + static from(url) { + return new URL(url); + } + /** + * Convenience method to instantiate a URL from numerous (optional) components + * @param protocol The protocol type + * @param host The host name + * @param path The path + * @param query The query + * @param hash The hash + * @returns {URL} The new URL object representing the URL + */ + static fromComponents(protocol, host, path, query, hash) { + const url = new URL(); + url.protocol = protocol; + url.host = host; + url.pathname = path; + url.query = query; + url.hash = hash; + return url; + } +} +//# sourceMappingURL=urls.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/routing/routing-components.js b/node_modules/@jet-app/app-store/tmp/src/foundation/routing/routing-components.js new file mode 100644 index 0000000..28fdc48 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/routing/routing-components.js @@ -0,0 +1,318 @@ +/** + * Created by km on 11/16/16. + */ +import * as urls from "../network/urls"; +import * as urlUtil from "./url-util"; +// endregion +// region private URLRule helpers. +/** + * Checks whether or not a given _pathComponents component contains a parameter. + * @param pathComponent The _pathComponents component to check. + * @returns true if the _pathComponents component is surrounded by curly braces; false otherwise. + */ +function isPathComponentParameter(pathComponent) { + const parameterStartIndex = pathComponent.indexOf("{"); + const parameterEndIndex = pathComponent.indexOf("}"); + return parameterStartIndex >= 0 && parameterEndIndex > parameterStartIndex + 1; +} +/** + * Extracts the parameter contained in a _pathComponents component. + * @param pathComponent A _pathComponents component surrounded by curly braces. + * @returns The parameter contained in the component. + */ +function getPathComponentParameter(pathComponent) { + const parameterStartIndex = pathComponent.indexOf("{"); + const parameterEndIndex = pathComponent.indexOf("}"); + const pathHasRequiredCurlyBraces = parameterStartIndex >= 0 && parameterEndIndex > parameterStartIndex; + return pathHasRequiredCurlyBraces + ? pathComponent.substring(parameterStartIndex + 1, parameterEndIndex) + : pathComponent; +} +/** + * Extracts the value from a path component, for a given internal key example: + * Path Component: "id123456" + * Internal Key: "id{id}" + * Return Value: "123456" + * @param pathComponent A _pathComponents component surrounded by curly braces. + * @returns The parameter contained in the component. + */ +export function getPathComponentParameterValueUsingInternalKey(pathComponent, internalKey) { + const valueStartIndex = internalKey.indexOf("{"); + const valueEndIndex = pathComponent.length - (internalKey.length - (internalKey.indexOf("}") + 1)); + const pathHasRequiredCurlyBraces = valueStartIndex >= 0 && valueEndIndex > valueStartIndex; + return pathHasRequiredCurlyBraces ? pathComponent.substring(valueStartIndex, valueEndIndex) : pathComponent; +} +/** + * Creates a mapping from key to _pathComponents component index + * for efficiently extracting parameters from a _pathComponents. + * @param rulePath The _pathComponents to create a mapping for. + * @returns A map of keys to _pathComponents component indexes. + */ +function makePathParameterMapping(rulePath) { + const mapping = {}; + rulePath.forEach((ruleComponent, index) => { + if (isPathComponentParameter(ruleComponent)) { + mapping[ruleComponent] = index; + } + }); + return mapping; +} +/** + * Normalizes a given protocol string for matching. + * @param protocol The protocol to match against. + * @returns The `protocol` with colon added if needed. + */ +function normalizeProtocol(protocol) { + // An empty string is falsy. + if (protocol === null || protocol === undefined) { + return null; + } + return protocol; +} +/** + * Creates `UrlRouteQuery` objects from substring of url. + * ? = optional + * -caseInsensitive = case insensitive + * @param parameters strings of form `<key>[?][-i]=<value>`. + * @returns Array of `UrlRouteQuery` objects. + */ +function parseQuery(parameters) { + const parsedQuery = []; + if (!parameters) { + return parsedQuery; + } + for (const param of parameters) { + const parts = param.split("="); + let key = parts[0]; + const optional = key.indexOf("?") !== -1; + key = key.replace("?", ""); + const caseInsensitive = key.indexOf("-caseInsensitive") !== -1; + key = key.replace("-caseInsensitive", ""); + let value = null; + if (parts.length > 1) { + value = decodeURIComponent(parts[1]); + } + parsedQuery.push({ + key, + value, + optional, + caseInsensitive, + }); + } + return parsedQuery; +} +// endregion +// region Url Rule +/** + * The `UrlRule` class extracts the pattern format from `UrlRuleDefinition`s, and encapsulates + * the information needed to match against a candidate URL and extract parameters from it. + * + * The terminology here is: + * - rule: A specific url pattern. + * - route: A group of rules that together form a single route, i.e. UrlRule[]. + */ +export class UrlRule { + /** + * Construct the route with all required properties. + * @param rule The rule to match. + */ + constructor(rule) { + this.identifier = rule.identifier; + this._protocol = normalizeProtocol(rule.protocol); + this._hostName = rule.hostName; + if (rule.path) { + this._pathComponents = rule.path.split("/").filter((component) => component.length > 0); + this._pathParameterMap = makePathParameterMapping(this._pathComponents); + } + else { + this._pathComponents = null; + this._pathParameterMap = null; + } + this._pathExtension = rule.pathExtension; + this._query = parseQuery(rule.query); + this._hash = rule.hash; + this._regex = rule.regex; + if (rule.exclusions) { + this._exclusions = rule.exclusions.map(function (ex) { + return new UrlRule(ex); + }); + } + else { + this._exclusions = null; + } + } + /** + * Checks whether or not the route matches a given URL. + * @param url The URL to check against. + * @returns true if the route matches `urls`; false otherwise. + */ + matches(url) { + var _a, _b; + if (this._regex) { + if (!this._regex.length) { + // If the rule specifies regex but does not supply patterns, we need to return false. Otherwise, we will + // risk matching against everything. This is because an empty regex with no other rule parameters will + // cause us to fallthrough to the end and match against all URLs. + return false; + } + let didMatchRegex = false; + for (const regexPattern of this._regex) { + if (regexPattern.test(url.toString())) { + // If we match against any of regex patterns, then we should proceed. + // If no matches are found, then this rule is not matched. + didMatchRegex = true; + break; + } + } + if (!didMatchRegex) { + return false; + } + } + if (this._protocol && url.protocol !== this._protocol) { + return false; + } + if (this._hostName && url.host !== this._hostName) { + return false; + } + if (this._pathComponents) { + const rulePathComponents = this._pathComponents; + const urlPathComponents = url.pathComponents(); + if (rulePathComponents.length !== urlPathComponents.length) { + return false; + } + // We're iterating two arrays here, an old style for-loop is appropriate + const length = rulePathComponents.length; + for (let i = 0; i < length; i++) { + const ruleComponent = rulePathComponents[i]; + if (isPathComponentParameter(ruleComponent)) { + // component parameters always match + continue; + } + const urlComponent = urlPathComponents[i]; + if (ruleComponent !== urlComponent) { + return false; + } + } + } + if (this._pathExtension) { + if (url.pathExtension() !== this._pathExtension) { + return false; + } + } + if (this._query) { + for (const param of this._query) { + let value; + if (param.caseInsensitive) { + for (const [queryKey, queryValue] of Object.entries((_a = url.query) !== null && _a !== void 0 ? _a : {})) { + if (param.key.toLocaleLowerCase() === queryKey.toLocaleLowerCase()) { + value = queryValue; + } + } + } + else { + value = (_b = url.query) === null || _b === void 0 ? void 0 : _b[param.key]; + } + if (!value && !param.optional) { + return false; + } + if (param.value && param.value !== value) { + return false; + } + } + } + if (this._hash && url.hash !== this._hash) { + return false; + } + if (this._exclusions) { + for (const exclusionRule of this._exclusions) { + if (exclusionRule._exclusions) { + throw Error("Matching exclusion rules with further exclusion rules may introduce significant code-complexity and/or reduce the ease with which developers are able to reason about your desired goals. Are there any simpler options?"); + } + if (exclusionRule.matches(url)) { + return false; + } + } + } + return true; + } + /** + * Extract information from a matching url. + * @param matchingUrl The url to extract parameters from. + * @returns `Parameters` extracted from `matchingUrl` + * @note This function is only valid when `this.matches(matchingUrl) === true`. + */ + extractParameters(matchingUrl) { + var _a, _b; + const parameters = {}; + if (this._pathComponents !== null && this._pathParameterMap !== null) { + const urlPathComponents = matchingUrl.pathComponents(); + for (const internalKey of Object.keys(this._pathParameterMap)) { + const externalKey = getPathComponentParameter(internalKey); + const index = this._pathParameterMap[internalKey]; + const parameterValue = getPathComponentParameterValueUsingInternalKey(urlPathComponents[index], internalKey); + parameters[externalKey] = decodeURIComponent(parameterValue); + } + } + if (this._query) { + for (const param of this._query) { + parameters[param.key] = (_b = (_a = matchingUrl.query) === null || _a === void 0 ? void 0 : _a[param.key]) !== null && _b !== void 0 ? _b : undefined; + } + } + return parameters; + } +} +/** + * `UrlRouter` manages a set of url rule templates to allow `urls` to serve as keys for different associated objects (like Builders). + * + * @note This is replaces old `UrlRouter` as a synchronous way match route URLs to handlers. In contrast to the previous implementation, + * it maps entire objects (containing related async handlers and properties) to urls. + */ +export class UrlRouter { + /// Constructs an empty URL router object. + constructor() { + this._routeMappings = []; + } + /** + * Register a new route defined by a set of definitions and object on the router. + * @param routeDefinitions The definitions of rules to register. + * @param object The object for the rule. + */ + associate(routeDefinitions, object) { + const route = []; + for (const definition of routeDefinitions) { + route.push(new UrlRule(definition)); + } + this._routeMappings.push({ route: route, object: object }); + } + /** + * Resolve given url to associated object, if any exist. + * @param urlOrString URL or string representation of url to resolve objects for. + * @returns `UrlRouterResult` containing url, extracted parameters, and associated object, or `null` if no match was found. + */ + routedObjectForUrl(urlOrString) { + let url = typeof urlOrString === "string" ? new urls.URL(urlOrString) : urlOrString; + url = urlUtil.normalizedAppStoreUrl(url); + url = urlUtil.normalizedActionUrl(url); + for (const mapping of this._routeMappings) { + for (const rule of mapping.route) { + if (rule.matches(url)) { + return { + normalizedUrl: url, + parameters: rule.extractParameters(url), + object: mapping.object, + matchedRuleIdentifier: rule.identifier, + }; + } + } + } + // No match. Still return a result with normalized url. + return { + normalizedUrl: url, + parameters: null, + object: null, + matchedRuleIdentifier: null, + }; + } +} +// endregion +//# sourceMappingURL=routing-components.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/action-provider.js b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/action-provider.js new file mode 100644 index 0000000..e0038da --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/action-provider.js @@ -0,0 +1,13 @@ +import { isBaseActionProvider } from "@jet/environment/dispatching"; +/** + * Retrieve an {@linkcode ActionModel} for the given {@linkcode intent} through + * any registered `IntentController`s that implement `ActionProvider` + */ +export function actionFor(intent, objectGraph, options = {}) { + const resolvedController = objectGraph.dispatcher.controller(intent); + if (isBaseActionProvider(resolvedController)) { + return resolvedController.actionFor(intent, objectGraph, options); + } + return null; +} +//# sourceMappingURL=action-provider.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-intent-dispatcher.js b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-intent-dispatcher.js new file mode 100644 index 0000000..0a88ada --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-intent-dispatcher.js @@ -0,0 +1,62 @@ +import { IntentDispatcher } from "@jet/environment/dispatching"; +import { ExperimentCache } from "../experimentation/experiment-cache"; +import { MetricsIdentifiersCache, MetricsIdentifierType, paymentIdentifierContextMapping, personalizationIdentifierContextMapping, } from "../metrics/metrics-identifiers-cache"; +import { AppStoreObjectGraph } from "./app-store-object-graph"; +export class AppStoreIntentDispatcher { + constructor() { + this.dispatcher = new IntentDispatcher(); + } + // MARK: - IBaseIntentDispatcher + register(controller) { + this.dispatcher.register(controller); + } + async dispatch(intent, objectGraph) { + const intentObjectGraph = await this.createIntentObjectGraphWithAsyncValues(objectGraph); + return await this.dispatcher.dispatch(intent, intentObjectGraph); + } + controller(intent) { + return this.dispatcher.controller(intent); + } + get registeredControllers() { + return this.dispatcher.registeredControllers; + } + async createIntentObjectGraphWithAsyncValues(objectGraph) { + const experimentCache = new ExperimentCache(); + const metricsIdentifiersCache = new MetricsIdentifiersCache(); + const personalizationMetricsIdentifierCache = new MetricsIdentifiersCache(personalizationIdentifierContextMapping); + let paymentMetricsIdentifiersCache; + // Doing these in sequence to help minimize the liklihood of hitting the treatment store locking timeout + // rdar://147518835 (AppStore Ad Regression Performance - Metrics Identifier blocking network requests) + if (objectGraph instanceof AppStoreObjectGraph) { + let appStoreObjectGraph = objectGraph; + // reassigning the bag creates a fresh cache + appStoreObjectGraph = appStoreObjectGraph.addingBag(appStoreObjectGraph.bag.underlyingBag); + await experimentCache.loadTreatments(appStoreObjectGraph); + await metricsIdentifiersCache.loadValues(appStoreObjectGraph, [ + MetricsIdentifierType.client, + MetricsIdentifierType.user, + ]); + // Only load the values if the namespace is in the bag + if (objectGraph.bag.personalizationUserIdEnabled) { + await personalizationMetricsIdentifierCache.loadValues(objectGraph, [MetricsIdentifierType.user]); + } + // The payment topic uses a different metrics identifier cache as it has a separate namespace. + if (appStoreObjectGraph.props.enabled("paymentTopicFromBag") && + appStoreObjectGraph.bag.metricsPaymentNamespaceEnabled) { + paymentMetricsIdentifiersCache = new MetricsIdentifiersCache(paymentIdentifierContextMapping); + // Only add this promise if paymentMetricsIdentifiersCache is actually created + await paymentMetricsIdentifiersCache.loadValues(appStoreObjectGraph, [ + MetricsIdentifierType.client, + MetricsIdentifierType.user, + ]); + } + objectGraph = appStoreObjectGraph; + } + return objectGraph + .adding(ExperimentCache.metatype, experimentCache) + .adding(MetricsIdentifiersCache.defaultMetatype, metricsIdentifiersCache) + .adding(MetricsIdentifiersCache.paymentMetatype, paymentMetricsIdentifiersCache || metricsIdentifiersCache) + .adding(MetricsIdentifiersCache.personalizationMetatype, personalizationMetricsIdentifierCache); + } +} +//# sourceMappingURL=app-store-intent-dispatcher.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-object-graph.js b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-object-graph.js new file mode 100644 index 0000000..a5102b2 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-object-graph.js @@ -0,0 +1,380 @@ +import { ObjectGraph } from "@jet/environment/dependencies"; +import { fetchTimingMetricsBuilderType, } from "@jet/environment/metrics/fetch-timing-metrics-builder"; +import * as jetTypes from "@jet/environment/types/globals/types"; +import * as askTypes from "../../api/typings/constants"; +import { isDefinedNonNull } from "../json-parsing/server-data"; +import { AppleSiliconWrapper } from "../wrappers/apple-silicon"; +import { BagWrapper } from "../wrappers/bag"; +import { ClientWrapper } from "../wrappers/client"; +import { ClientOrderingWrapper } from "../wrappers/client-ordering"; +import { ConsoleWrapper } from "../wrappers/console"; +import { HostWrapper } from "../wrappers/host"; +import { LocalizationWrapper } from "../wrappers/localization"; +import { PropertiesWrapper } from "../wrappers/properties"; +import { StorageWrapper } from "../wrappers/storage"; +import { ExperimentCache } from "../experimentation/experiment-cache"; +import { MetricsIdentifiersCache } from "../metrics/metrics-identifiers-cache"; +import { LocaleMetaType } from "../dependencies/locale/locale"; +import { SEOMetaType } from "../dependencies/seo"; +import { ActiveIntentMetaType, ActiveIntent } from "../dependencies/active-intent"; +import { LocaleFromBag } from "../dependencies/locale/locale-from-bag"; +import { ObjectGraphType } from "../../gameservicesui/src/foundation/object-graph-types"; +export class AppStoreObjectGraph extends ObjectGraph { + configureDefaults( + // Jet Types + aBag, aCryptography, aHost, aNetwork, aPlatform, aPlist, aRandom, aServices, aCookieProvider, aConsole, + // Ask Types + aStoreMetrics, aAMSEngagement, aLocalization, aAdsLocalizer, aDevice, aClient, aProperties, aUser, aPlayer, aMetricsIdentifiers, aClientOrdering, aArcade, aGameCenter, aResilientDeepLinks, aAppleSilicon, aStorage, aAds, aOnDeviceRecommendationsManager, aOnDeviceSearchHistoryManager, aFeatureFlags, aMediaToken, aAppDistribution, timeoutManager, treatmentStore, userDefaults) { + let objectGraph = this + // Jet Types + .addingCryptography(aCryptography) + .addingHost(aHost) + .addingNetwork(aNetwork) + .addingPlatform(aPlatform) + .addingPlist(aPlist) + .addingRandom(aRandom) + .addingServices(aServices) + .addingCookieProvider(aCookieProvider) + .addingBag(aBag) // Bag depends on having Host + .addingConsole(aConsole) + // ASK types + .addingStoreMetrics(aStoreMetrics) + .addingAMSEngagement(aAMSEngagement) + .addingLoc(aLocalization) + .addingAdsLoc(aAdsLocalizer) + .addingDevice(aDevice) + .addingClient(aClient) + .addingProperties(aProperties) + .addingUser(aUser) + .addingPlayer(aPlayer) + .addingMetricsIdentifiers(aMetricsIdentifiers) + .addingClientOrdering(aClientOrdering) + .addingArcade(aArcade) + .addingGameCenter(aGameCenter) + .addingDeepLinks(aResilientDeepLinks) + .addingAppleSilicon(aAppleSilicon) + .addingStorage(aStorage) + .addingAds(aAds) + .addingOnDeviceRecommendationsManager(aOnDeviceRecommendationsManager) + .addingOnDeviceSearchHistoryManager(aOnDeviceSearchHistoryManager) + .addingFeatureFlags(aFeatureFlags) + .addingMediaToken(aMediaToken) + .addingAppDistribution(aAppDistribution) + .addingTimeoutManager(timeoutManager) + .addingAdsLoc(aAdsLocalizer) + .addingTreatmentStore(treatmentStore) + .addingUserDefaults(userDefaults); + objectGraph.loc.load(objectGraph); + // `LocaleFromBag` requires that the `bag` is already present on the Object Graph + objectGraph = objectGraph.addingLocale(new LocaleFromBag(objectGraph)); + return objectGraph; + } + // Jet Types + get bag() { + return this.required(BagWrapper.type); + } + addingBag(bag) { + return this.addingBagWrapper(new BagWrapper(bag, this.host)).adding(jetTypes.bag, bag); + } + addingBagWrapper(bag) { + return this.adding(BagWrapper.type, bag); + } + get console() { + return this.required(ConsoleWrapper.type); + } + addingConsole(console) { + return this.addingConsoleWrapper(new ConsoleWrapper(console)); + } + addingConsoleWrapper(console) { + return this.adding(ConsoleWrapper.type, console); + } + get cryptography() { + return this.required(jetTypes.cryptography); + } + addingCryptography(cryptography) { + return this.adding(jetTypes.cryptography, cryptography); + } + get host() { + return this.required(HostWrapper.type); + } + addingHost(host) { + return this.addingHostWrapper(new HostWrapper(host)); + } + addingHostWrapper(host) { + return this.adding(HostWrapper.type, host); + } + get locale() { + return this.required(LocaleMetaType); + } + addingLocale(locale) { + return this.adding(LocaleMetaType, locale); + } + get network() { + return this.required(jetTypes.net); + } + addingNetwork(network) { + return this.adding(jetTypes.net, network); + } + get platform() { + return this.required(jetTypes.platform); + } + addingPlatform(platform) { + return this.adding(jetTypes.platform, platform); + } + get plist() { + return this.required(jetTypes.plist); + } + addingPlist(plist) { + return this.adding(jetTypes.plist, plist); + } + get random() { + return this.required(jetTypes.random); + } + addingRandom(random) { + return this.adding(jetTypes.random, random); + } + get services() { + return this.required(jetTypes.services); + } + addingServices(services) { + return this.adding(jetTypes.services, services); + } + get cookieProvider() { + return this.required(jetTypes.cookieProvider); + } + addingCookieProvider(cookieProvider) { + return this.adding(jetTypes.cookieProvider, cookieProvider); + } + get fetchTimingMetricsBuilder() { + return this.optional(fetchTimingMetricsBuilderType); + } + addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder) { + return this.adding(fetchTimingMetricsBuilderType, fetchTimingMetricsBuilder); + } + // Ask Types + get storeMetrics() { + return this.required(askTypes.storeMetrics); + } + addingStoreMetrics(storeMetrics) { + return this.adding(askTypes.storeMetrics, storeMetrics); + } + get amsEngagement() { + return this.optional(askTypes.amsEngagement); + } + addingAMSEngagement(amsEngagement) { + return this.adding(askTypes.amsEngagement, amsEngagement); + } + get loc() { + return this.required(LocalizationWrapper.type); + } + addingLoc(loc) { + return this.addingLocWrapper(new LocalizationWrapper(loc, this)); + } + addingLocWrapper(loc) { + return this.adding(LocalizationWrapper.type, loc); + } + get adsLoc() { + return this.required(askTypes.adsLocalizer); + } + addingAdsLoc(loc) { + return this.adding(askTypes.adsLocalizer, loc); + } + get device() { + return this.required(askTypes.device); + } + addingDevice(device) { + return this.adding(askTypes.device, device); + } + get client() { + return this.required(ClientWrapper.type); + } + addingClient(client) { + return this.addingClientWrapper(new ClientWrapper(client)); + } + addingClientWrapper(client) { + return this.adding(ClientWrapper.type, client); + } + get props() { + return this.required(PropertiesWrapper.type); + } + addingProperties(properties) { + return this.addingPropertiesWrapper(new PropertiesWrapper(properties)); + } + addingPropertiesWrapper(properties) { + return this.adding(PropertiesWrapper.type, properties); + } + get user() { + return this.required(askTypes.user); + } + addingUser(user) { + return this.adding(askTypes.user, user); + } + /// Returns the current GameCenter player, only available for GAMES_TARGET + get player() { + if (preprocessor.GAMES_TARGET && this.props.enabled("157263806-add-playerBridge-askGlobal")) { + return this.required(askTypes.player); + } + else { + return undefined; + } + } + addingPlayer(player) { + return this.adding(askTypes.player, player); + } + get metricsIdentifiers() { + return this.required(askTypes.metricsIdentifiers); + } + addingMetricsIdentifiers(metricsIdentifiers) { + return this.adding(askTypes.metricsIdentifiers, metricsIdentifiers); + } + get clientOrdering() { + return this.required(ClientOrderingWrapper.type); + } + addingClientOrdering(clientOrdering) { + return this.addingClientOrderingWrapper(new ClientOrderingWrapper(clientOrdering)); + } + addingClientOrderingWrapper(clientOrdering) { + return this.adding(ClientOrderingWrapper.type, clientOrdering); + } + get arcade() { + return this.required(askTypes.arcade); + } + addingArcade(arcade) { + return this.adding(askTypes.arcade, arcade); + } + get gameCenter() { + return this.required(askTypes.gameCenter); + } + addingGameCenter(gameCenter) { + return this.adding(askTypes.gameCenter, gameCenter); + } + get deepLinks() { + return this.required(askTypes.resilientDeepLinks); + } + addingDeepLinks(resilientDeepLinks) { + return this.adding(askTypes.resilientDeepLinks, resilientDeepLinks); + } + get appleSilicon() { + return this.required(AppleSiliconWrapper.type); + } + addingAppleSilicon(appleSilicon) { + return this.addingAppleSiliconWrapper(new AppleSiliconWrapper(appleSilicon)); + } + addingAppleSiliconWrapper(appleSilicon) { + return this.adding(AppleSiliconWrapper.type, appleSilicon); + } + get storage() { + return this.required(StorageWrapper.type); + } + addingStorage(storage) { + return this.addingStorageWrapper(new StorageWrapper(storage)); + } + addingStorageWrapper(storage) { + return this.adding(StorageWrapper.type, storage); + } + get ads() { + return this.required(askTypes.ads); + } + addingAds(ads) { + return this.adding(askTypes.ads, ads); + } + get onDeviceRecommendationsManager() { + return this.required(askTypes.onDeviceRecommendationsManager); + } + addingOnDeviceRecommendationsManager(onDeviceRecommendationsManager) { + return this.adding(askTypes.onDeviceRecommendationsManager, onDeviceRecommendationsManager); + } + get onDeviceSearchHistoryManager() { + return this.required(askTypes.onDeviceSearchHistoryManager); + } + addingOnDeviceSearchHistoryManager(onDeviceSearchHistoryManager) { + return this.adding(askTypes.onDeviceSearchHistoryManager, onDeviceSearchHistoryManager); + } + get featureFlags() { + return this.required(askTypes.featureFlags); + } + addingFeatureFlags(featureFlags) { + return this.adding(askTypes.featureFlags, featureFlags); + } + get mediaToken() { + return this.required(askTypes.mediaToken); + } + addingMediaToken(mediaToken) { + return this.adding(askTypes.mediaToken, mediaToken); + } + get appDistribution() { + return this.required(askTypes.appDistribution); + } + addingAppDistribution(appDistribution) { + return this.adding(askTypes.appDistribution, appDistribution); + } + get timeoutManager() { + return this.required(askTypes.timeoutManager); + } + addingTimeoutManager(timeoutManager) { + return this.adding(askTypes.timeoutManager, timeoutManager); + } + get treatmentStore() { + return this.required(askTypes.treatmentStore); + } + addingTreatmentStore(treatmentStore) { + return this.adding(askTypes.treatmentStore, treatmentStore); + } + get experimentCache() { + return this.optional(ExperimentCache.metatype); + } + get metricsIdentifiersCache() { + return this.optional(MetricsIdentifiersCache.defaultMetatype); + } + get paymentMetricsIdentifiersCache() { + return this.optional(MetricsIdentifiersCache.paymentMetatype); + } + get personalizationMetricsIdentifiersCache() { + return this.optional(MetricsIdentifiersCache.personalizationMetatype); + } + get userDefaults() { + if (!this.client.isiOS) { + return undefined; + } + return this.required(askTypes.userDefaults); + } + addingUserDefaults(userDefaults) { + return this.adding(askTypes.userDefaults, userDefaults); + } + isAvailable(type) { + return isDefinedNonNull(this.optional(type)); + } + // "Web" client dependencies + get activeIntent() { + return this.optional(ActiveIntentMetaType); + } + addingActiveIntent(implementation) { + return this.adding(ActiveIntentMetaType, new ActiveIntent(implementation)); + } + get seo() { + return this.optional(SEOMetaType); + } + addingSEO(implementation) { + return this.adding(SEOMetaType, implementation); + } + // GameStoreKit dependencies + get dispatcher() { + return this.required(ObjectGraphType.dispatcher); + } + get nativeIntentDispatcher() { + return this.required(ObjectGraphType.nativeIntentDispatcher); + } + get debugSettings() { + return this.required(ObjectGraphType.debugSettings); + } + get router() { + return this.required(ObjectGraphType.router); + } + get localizer() { + return this.required(ObjectGraphType.localizer); + } + get personNameComponentsFormatter() { + return this.required(ObjectGraphType.personNameComponentsFormatter); + } +} +//# sourceMappingURL=app-store-object-graph.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/runtime.js b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/runtime.js new file mode 100644 index 0000000..d3b5904 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/runtime/runtime.js @@ -0,0 +1,52 @@ +import * as validation from "@jet/environment/json/validation"; +import { LegacyRuntime } from "@jet/environment/runtime"; +export class AppStoreRuntime extends LegacyRuntime { + constructor(dispatcher, objectGraph) { + super(dispatcher, objectGraph, {}); + } + exportingService(name, service) { + this.wrapServiceInValidation(service); + const existingService = this.serviceWithName(name) || {}; + const newService = { + ...existingService, + ...service, + }; + return super.exportingService(name, newService); + } + // eslint-disable-next-line @typescript-eslint/ban-types + exportingServiceName(name, functionName, implementation) { + const service = {}; + service[functionName] = implementation; + this.exportingService(name, service); + } + wrapServiceInValidation(service) { + for (const memberName of Object.keys(service)) { + const serviceFunction = service[memberName]; + // For every service route function that we find, we're going + // to wrap it in a thunk so that we can attach validation + // incidents to it before it's returned back to native code. + if (serviceFunction instanceof Function) { + // Using a regular function here because we want to forward + // `this` as well as our arguments to the wrapped function. + service[memberName] = function validationThunk(...args) { + // Execute the function + const returnValue = serviceFunction.apply(this, args); + // Ensure we record validation incidents after the fact + if (returnValue instanceof Promise) { + return returnValue.then((value) => { + validation.recordValidationIncidents(value); + return value; + }); + } + else { + // This would be a violation of the calling convention, + // but I guess we might as well consume the incidents. + validation.recordValidationIncidents(returnValue); + return returnValue; + } + }; + } + } + } +} +//# sourceMappingURL=runtime.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/array-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/array-util.js new file mode 100644 index 0000000..57c8e84 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/array-util.js @@ -0,0 +1,32 @@ +/** + * Creates an array containing multiple copies of the elements of another array. + * + * For example, if A = [Q, W, E, R, T, Y], then calling repeating(A, 3) would return + * [Q, W, E, R, T, Y, Q, W, E, R, T, Y, Q, W, E, R, T, Y] + * + * @param elements The elements that will be repeated + * @param times The number of times all of the elements should appear in the new array. + */ +export function arrayByRepeating(elements, times) { + if (times <= 0) { + return []; + } + let newArray = []; + for (let i = 0; i < times; i++) { + newArray = newArray.concat(elements); + } + return newArray; +} +export function partition(array, predicate) { + const part1 = []; + const part2 = []; + array.forEach((item) => (predicate(item) ? part1.push(item) : part2.push(item))); + return [part1, part2]; +} +export function isEmpty(elements) { + return elements.length === 0; +} +export function isNotEmpty(elements) { + return !isEmpty(elements); +} +//# sourceMappingURL=array-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/color-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/color-util.js new file mode 100644 index 0000000..84e3815 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/color-util.js @@ -0,0 +1,231 @@ +import { isNothing, isSome } from "@jet/environment/types/optional"; +const HEX_PARSER = new RegExp("#?([0-9,a-f,A-F][0-9,a-f,A-F])([0-9,a-f,A-F][0-9,a-f,A-F])([0-9,a-f,A-F][0-9,a-f,A-F])([0-9,a-f,A-F][0-9,a-f,A-F])?"); +export function fromHex(hexString) { + if (isNothing(hexString) || hexString === "") { + return null; + } + const results = HEX_PARSER.exec(hexString); + if (results === null || !(results.length === 4 || results.length === 5)) { + return null; + } + const red = parseInt(results[1], 16) / 255; + const green = parseInt(results[2], 16) / 255; + const blue = parseInt(results[3], 16) / 255; + const alpha = isSome(results[4]) ? parseInt(results[4], 16) / 255 : undefined; + const newColor = { + red: red, + green: green, + blue: blue, + alpha: alpha, + type: "rgb", + }; + return newColor; +} +export const black = rgbWith(0, 0, 0); +export const white = rgbWith(1, 1, 1); +export function rgbWith(red, green, blue, alpha) { + const newColor = { + red: red, + green: green, + blue: blue, + alpha: alpha, + type: "rgb", + }; + return newColor; +} +export function luminanceFrom(rgbColor) { + // Note: This is lifted from UIColor_Private + // Using RGB color components, calculates and returns (0.2126 * r) + (0.7152 * g) + (0.0722 * b). + return rgbColor.red * 0.2126 + rgbColor.green * 0.7152 + rgbColor.blue * 0.0722; +} +/** + * Determines whether the given color is considered dark. + * @param color The color to test. + * @param darkColorCutoff The cutoff value, which ought to be between 0 and 100. + */ +export function isDarkColor(color, darkColorCutoff = 84) { + if (isNothing(color)) { + return false; + } + switch (color.type) { + case "rgb": + // Multiplying 100 because the rest of the codebase now works off darkColorCutoff 0-100. + // Before this change it used to be darkColorCutoff 0.0-1.0 + return luminanceFrom(color) * 100 < darkColorCutoff; + case "named": + return isNamedColorDarkColor(color); + default: + return false; + } +} +function isNamedColorDarkColor(namedColor) { + switch (namedColor.name) { + case "black": + return true; + default: + return false; + } +} +export function named(name) { + const newColor = { + name: name, + type: "named", + }; + return newColor; +} +export function dynamicWith(lightColor, darkColor) { + const newColor = { + lightColor: lightColor, + darkColor: darkColor, + type: "dynamic", + }; + return newColor; +} +/** + * A convenience for comparing two colors to see if they are equal. + * @param firstColor The first color to compare + * @param secondColor The second color to compare + * @returns True if the colors are considered equal + */ +export function isColorEqualToColor(firstColor, secondColor) { + if (isNothing(firstColor) || isNothing(secondColor)) { + return false; + } + if (firstColor.type === "rgb" && secondColor.type === "rgb") { + return (firstColor.red === secondColor.red && + firstColor.green === secondColor.green && + firstColor.blue === secondColor.blue && + firstColor.alpha === secondColor.alpha); + } + else if (firstColor.type === "named" && secondColor.type === "named") { + return firstColor.name === secondColor.name; + } + else if (firstColor.type === "dynamic" && secondColor.type === "dynamic") { + return (isColorEqualToColor(firstColor.lightColor, secondColor.lightColor) && + isColorEqualToColor(firstColor.darkColor, secondColor.darkColor)); + } + return false; +} +/** + * Type guard to check if a value is a ColorBucketSaturationBrightnessCriteria. + * @param value The value to check. + * @returns True if the value is a ColorBucketSaturationBrightnessCriteria, false otherwise. + */ +function isSaturationBrightnessCriteria(value) { + if (typeof value !== "object" || value === null) { + return false; + } + const obj = value; // Cast for easier property access + // Check if all required properties exist and have the correct types + return ("colorHex" in obj && + typeof obj.colorHex === "string" && + "maxSaturation" in obj && + typeof obj.maxSaturation === "number" && + "maxBrightness" in obj && + typeof obj.maxBrightness === "number"); +} +/** + * Finds the closest matching color from a list of hex colors using LAB color space + * for more perceptually accurate matching + */ +export function findColorBucketForColor(targetRGB, saturationBrightnessBasedBuckets, hueBasedBuckets) { + if (isNothing(targetRGB) || (saturationBrightnessBasedBuckets.length === 0 && hueBasedBuckets.length === 0)) { + return null; + } + const targetColor = targetRGB; + if (isNothing(targetColor)) { + return null; + } + const targetHsb = rgbToHsb(targetColor); + for (const saturationBrightnessBasedBucket of saturationBrightnessBasedBuckets) { + if (doesColorMeetCriteria(targetHsb, saturationBrightnessBasedBucket)) { + return fromHex(saturationBrightnessBasedBucket.colorHex); + } + } + for (const hueBasedBucket of hueBasedBuckets) { + if (doesColorMeetCriteria(targetHsb, hueBasedBucket)) { + return fromHex(hueBasedBucket.colorHex); + } + } + return null; +} +/** + * Determines whether a color meets the specified criteria. + * + * For saturation/brightness criteria, the color passes if either its saturation or brightness + * is less than or equal to the maximum allowed values. + * + * For hue criteria, the color passes if its hue falls within the specified range (inclusive). + * + * @param color The RGB color to evaluate + * @param criteria The criteria to check against (either hue-based or saturation/brightness-based) + * @returns True if the color passes the criteria, false otherwise or if color is null/undefined + */ +export function doesColorMeetCriteria(color, criteria) { + if (isNothing(color)) { + return false; + } + const hsbColor = isHSBColor(color) ? color : rgbToHsb(color); + // Check saturation / brightness buckets first, using <= for inclusive range checks + if (isSaturationBrightnessCriteria(criteria)) { + return hsbColor.saturation <= criteria.maxSaturation || hsbColor.brightness <= criteria.maxBrightness; + } + else { + // Check if the hue falls within the min/max bounds (inclusive) + // Special handling for the Pink range potentially including 360 if needed, + // but mapping 360->0 covers the Coral case correctly. + return hsbColor.hue >= criteria.minHue && hsbColor.hue <= criteria.maxHue; + } +} +function isHSBColor(value) { + if (typeof value !== "object" || value === null) { + return false; + } + const obj = value; // Cast for easier property access + // Check if the type property exists and has the correct value + return "type" in obj && obj.type === "hsb"; +} +/** + * Converts RGB color to HSB color space. + * HSL is particularly good at representing relationships between colors + * as humans perceive them, especially for determining similar hues + * across different brightness levels. + */ +function rgbToHsb(rgb) { + const r = rgb.red; + const g = rgb.green; + const b = rgb.blue; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const delta = max - min; + // Calculate brightness (value in HSV) + const brightness = max * 100; + // Calculate saturation + const saturation = max === 0 ? 0 : (delta / max) * 100; + // Calculate hue + let hue = 0; + if (delta !== 0) { + switch (max) { + case r: + hue = ((g - b) / delta + (g < b ? 6 : 0)) * 60; + break; + case g: + hue = ((b - r) / delta + 2) * 60; + break; + case b: + hue = ((r - g) / delta + 4) * 60; + break; + default: + break; + } + } + // Ensure hue is in [0, 360) + hue = (hue + 360) % 360; + return { + type: "hsb", + hue: hue, + saturation: saturation, + brightness: brightness, + }; +} +//# sourceMappingURL=color-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/constants.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/constants.js new file mode 100644 index 0000000..86522d5 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/constants.js @@ -0,0 +1,26 @@ +/** + * Created by keithpk on 2/9/17. + */ +/** + * These are the developer IDs for apps that Apple works on internally + * The IDs in the list below are CPIDs (content provider IDs), which are not exposed + * to the client. So, we use the developer.id field that we have available instead. + * Note that Dark Sky both the same developer ID as Apple Inc. + * 2003 Apple Inc + * 120076162 Apple Health Research + * 244848 The Dark Sky Company LLC + * 115216 Claris International Inc. + * 14275 Shazam Entertainment Limited + * 118988565 AC Wellness Network LLC + */ +export const appleOwnedDeveloperIds = [ + "284417353", + "1464590764", + "314638464", + "284993479", + "1351056256", // AC Wellness Network LLC +]; +export const iAdRequestDataHeaderName = "X-Apple-iAd-Request-Data"; +export const appStoreClientRequestIdName = "X-Apple-App-Store-Client-Request-Id"; +export const iAdRoutingInfoHeaderName = "X-Apple-iAd-Env-Name"; +//# sourceMappingURL=constants.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/date-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/date-util.js new file mode 100644 index 0000000..182dd63 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/date-util.js @@ -0,0 +1,127 @@ +// +// date.ts +// AppStoreKit +// +// Created by Sam Vafaee on 10/25/17. +// Copyright (c) 2017 Apple Inc. All rights reserved. +// +import * as serverData from "../json-parsing/server-data"; +/** + * Returns the date representation for a string ignoring the time. For preorders, it + * is vital that any date string be passed through this before being used. + * @param dateString The date string to parse + * @returns A date in local time zone. + */ +export function parseDateOmittingTimeFromString(dateString) { + // Sanity check + if (serverData.isNull(dateString) || dateString === "") { + return null; + } + // Only consider date + if (dateString.indexOf("T") !== -1) { + dateString = dateString.split("T")[0]; + } + // This string replacement is needed to ensure midnight in the local time zone + // is used, rather than UTC-midnight. + return new Date(dateString.replace(/-/g, "/")); +} +/** + * Strips time from the given date and returns it as midnight UTC of the same day. + * @param date Local date. + * @returns UTC date + */ +export function convertLocalDateToUTCMidnight(date) { + // Sanity check + if (serverData.isNull(date)) { + return null; + } + return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); +} +/** + * Returns the number of milliseconds from epoch to UTC midnight of the given date. + * @param date Local date. + * @returns Milliseconds from epoch. + */ +export function millisecondsToUTCMidnightFromLocalDate(date) { + // Sanity check + if (serverData.isNull(date)) { + return null; + } + const utcDate = convertLocalDateToUTCMidnight(date); + if (serverData.isNull(utcDate)) { + return null; + } + return utcDate.getTime(); +} +/** + * Removes the time portion from the given date and returns it as midnight of the same day. + * @param date Local date + * @returns Local date + */ +export function convertLocalDateToLocalMidnight(date) { + // Sanity check + if (serverData.isNull(date)) { + return null; + } + const midnight = new Date(date); + midnight.setHours(0, 0, 0, 0); + return midnight; +} +/** + * Sets the minutes, seconds and milliseconds of the given date to 0. + * @param date Local date + * @returns Local date + */ +export function convertLocalDateByFlooringToHour(date) { + // Sanity check + if (serverData.isNull(date)) { + return null; + } + return dateFlooredToHour(date); +} +/** + * Creates a new date with the minutes, seconds and milliseconds of the given date set to 0. + * @param date The date to remove minutes, seconds, and milliseconds from. + * @returns The new date with minutes, seconds, and milliseconds removed using local device time zone. + */ +export function dateFlooredToHour(date) { + const dateCopy = new Date(date.getTime()); + dateCopy.setMinutes(0); + dateCopy.setSeconds(0); + dateCopy.setMilliseconds(0); + return dateCopy; +} +/** + * Returns the number of hours between two dates, or null if either date is null. + * @param fromDate The date to calculate from. + * @param toDate The date to calculate to. + */ +export function numberOfHoursBetween(fromDate, toDate) { + // Sanity check + if (serverData.isNull(fromDate) || serverData.isNull(toDate)) { + return null; + } + return Math.ceil((toDate.getTime() - dateFlooredToHour(fromDate).getTime()) / (60 * 60 * 1000)); +} +/** + * Returns the number of days between two dates, or null if either date is null. + * @param fromDate The date to calculate from. + * @param toDate The date to calculate to. + */ +export function numberOfDaysBetween(fromDate, toDate) { + // Sanity check + if (serverData.isNull(fromDate) || serverData.isNull(toDate)) { + return null; + } + return Math.floor((toDate.getTime() - fromDate.getTime()) / (60 * 60 * 1000 * 24)); +} +/** + * Whether or not two dates occur on the same day. + * @param date A date that will be compared to the second input. + * @param otherDate A date that will be compared to the first input. + */ +export function areLocalDatesSameDay(date, otherDate) { + var _a, _b; + return ((_a = convertLocalDateToLocalMidnight(date)) === null || _a === void 0 ? void 0 : _a.getTime()) === ((_b = convertLocalDateToLocalMidnight(otherDate)) === null || _b === void 0 ? void 0 : _b.getTime()); +} +//# sourceMappingURL=date-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/errors.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/errors.js new file mode 100644 index 0000000..d1a3482 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/errors.js @@ -0,0 +1,13 @@ +/** + * Created by Ben Williams on 3/13/19. + */ +/** + * Enables compile time detection for code that should be unreachable + * For example, calling this function in the default case of a switch statement + * will provide compile time checking that the switch is exhaustive + * @return never + */ +export function unreachable(value) { + throw new Error(`This method should never be called with value: ${value}`); +} +//# sourceMappingURL=errors.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/math-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/math-util.js new file mode 100644 index 0000000..92b4992 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/math-util.js @@ -0,0 +1,32 @@ +/** + * Given an integer m > 1, called a modulus, two integers are said to be congruent modulo m, + * if m is a divisor of their difference (i.e., if there is an integer k such that a \u2212 b = km). + * + * Unlike JavaScript's remainder operator (%), this mod function never returns negative values. + * + * @param n An integer + * @param m The modulous + */ +export function mod(n, m) { + return ((n % m) + m) % m; +} +/** + * Reduce sig fig of `value` to `significantDigits`. + * @param value Value to reduce precision of. + * @param significantDigits Significant digits to keep. + */ +export function reduceSignificantDigits(value, significantDigits) { + const roundFactor = Math.pow(10.0, significantDigits); + const roundingFunction = value > 0.0 ? Math.floor : Math.ceil; + return roundingFunction(value / roundFactor) * roundFactor; +} +/** + * Clamps a value between an upper and lower bound. + * @param value The preferred value. + * @param min The minimum allowed value. + * @param max The maximum allowed value. + */ +export function clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); +} +//# sourceMappingURL=math-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/objects.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/objects.js new file mode 100644 index 0000000..6740572 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/objects.js @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * Created by km on 3/6/17. + */ +/** + * Creates and returns a shallow copy of a given object. + * @param object The object to create a shallow copy of. + * @returns The new copy of object. + */ +export function shallowCopyOf(object) { + if (object === null || object === undefined) { + return object; + } + const copy = Object.create(Object.getPrototypeOf(object)); + Object.assign(copy, object); + return copy; +} +/** + * Returns whether or not a given object is an instance of + * a specified type, modifying the type of the object in + * TypeScript's type system. + * + * Prefer this function to using `instanceof` directly as it + * will retype the passed in object if the check succeeds. + * + * @param object The object whose nominal type will be checked. + * @param type The metatype that `object` is expected to be an instance of. + * @returns Whether object is an instance of type, updating the type of object accordingly. + */ +export function isTypeOf(object, type) { + return object instanceof type; +} +//# sourceMappingURL=objects.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/promise-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/promise-util.js new file mode 100644 index 0000000..d661559 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/promise-util.js @@ -0,0 +1,215 @@ +/** + * This module provides enhanced promise handling capabilities beyond native JavaScript Promises. + * It focuses on distinguishing between required and optional promises, standardizing results, + * and simplifying error handling for complex async operations. + */ +/** + * Symbol used to uniquely identify PromiseResult objects. + * This allows for reliable type checking. + */ +export const PROMISE_RESULT_SYMBOL = Symbol("PromiseResult"); +/** + * Type guard to check if a value is a PromiseResult. + * + * @param value - The value to check + * @returns True if the value is a PromiseResult, false otherwise + */ +function isPromiseResult(value) { + return value !== null && typeof value === "object" && PROMISE_RESULT_SYMBOL in value; +} +/** + * Custom error class that aggregates multiple promise failures. + * Provides access to all underlying errors while presenting a combined error message. + */ +export class MultiPromiseError extends Error { + constructor(reasons) { + super(errorMessageForReasons(reasons)); + this.reasons = reasons; + this.reasons = reasons; + } +} +/** + * Helper function to create a standardized fulfilled result object. + * Ensures the result has the PROMISE_RESULT_SYMBOL for type checking. + * + * @param value - The value to wrap in a fulfilled result + * @returns A standardized fulfilled result object + */ +export function makeFulfilledResult(value) { + return { + success: true, + value, + error: null, + [PROMISE_RESULT_SYMBOL]: true, + }; +} +/** + * Helper function to create a standardized rejected result object. + * Ensures the result has the PROMISE_RESULT_SYMBOL for type checking. + * + * @param error - The error to wrap in a rejected result + * @returns A standardized rejected result object + */ +export function makeRejectedResult(error) { + const normalizedError = error instanceof Error ? error : new Error(String(error)); + return { + success: false, + value: null, + error: normalizedError, + [PROMISE_RESULT_SYMBOL]: true, + }; +} +/** + * Custom error class that indicates that a required promise failed. + */ +export class RequiredPromiseError extends Error { + constructor(reason) { + super(reason.message); + this.reason = reason; + this.reason = reason; + } +} +/** + * Transforms a promise into one that never throws, instead returning a standardized result. + * Use this for optional operations that shouldn't cause the overall process to fail. + * This is a convenience alias for tryAwait with a more semantic name. + * + * @param promise - The promise to make optional + * @returns A promise that resolves to a PromiseResult instead of throwing + */ +export async function optional(promise) { + return await tryAwait(promise); +} +/** + * Marks a promise as required (will throw on rejection). + * This is mainly for code clarity as regular promises are already required by default. + * + * @param promise - The promise to mark as required + * @returns The original promise (identity function) + */ +export async function required(promise) { + return await promise; +} +/** + * Executes a mix of required and optional promises, returning standardized results. + * Regular promises are treated as required and will cause this function to throw if they fail. + * Promises wrapped with optional() will never cause this function to throw. + * + * This implementation handles both regular promises and promises that resolve to PromiseResult. + * + * @param promises - Array of promises (both required and optional) + * @returns Promise resolving to an array of standardized results + * @throws Original error if a single required promise fails + * @throws MultiPromiseError if multiple required promises fail + * @note For homogeneous promise arrays, prefer using more specific functions: + * - Use allRequired() when all promises should be treated as required + * - Use allOptional() when all promises should be treated as optional + * - Only use allMixed() when you need to combine both required and optional promises + */ +export async function allMixed(promises) { + // Process each promise + const results = await Promise.all(promises.map(async (promise) => { + try { + const value = await promise; + // If it's a PromiseResult (from optional()/tryAwait), use it directly + if (isPromiseResult(value)) { + // Handle the case where T is already a PromiseResult + // This fixes the double-wrapping issue when all promises are optional + return value; + } + else { + // It's a regular promise that succeeded + return makeFulfilledResult(value); + } + } + catch (error) { + // This was a required promise that failed + return makeRejectedResult(error instanceof Error ? error : new Error(String(error))); + } + })); + const requiredFailures = []; + results.forEach((result) => { + if (!result.success) { + requiredFailures.push(result.error); + } + }); + // If any required promises failed, throw an error + if (requiredFailures.length > 0) { + if (requiredFailures.length === 1) { + throw requiredFailures[0]; + } + else { + throw new MultiPromiseError(requiredFailures); + } + } + return results; +} +/** + * Executes all promises and returns their values. + * All promises are treated as required and will cause this function to throw if any fail. + * + * @param promises - Array of promises + * @returns Promise resolving to an array of values + * @throws Original error if a single promise fails + * @throws MultiPromiseError if multiple promises fail + */ +export async function allRequired(promises) { + return await Promise.all(promises); +} +/** + * Makes all promises optional and then executes them, returning standardized results. + * This function is specifically designed for cases where you want all promises to be treated as optional. + * It avoids the double-wrapping issue that can occur when using allMixed with arrays of optional promises. + * + * @param promises - Array of promises to make optional + * @returns Promise resolving to an array of standardized results + * @example + * ``` + * const promises = urls.map(fetchData); + * const results = await allOptional(promises); + * // Each result is a PromiseResult<T> that won't cause the overall operation to fail + * ``` + */ +export async function allOptional(promises) { + // Use Promise.all with optional() for each promise + return await Promise.all(promises.map(optional)); +} +/** + * Creates an error message from multiple rejection reasons. + * Formats multiple error reasons into a single error message string, + * handling both Error objects and other rejection values. + * + * @param reasons - Array of error reasons that caused promise rejections + * @returns Concatenated error message string + */ +function errorMessageForReasons(reasons) { + return reasons + .map((reason) => { + if (reason instanceof Error) { + return reason.message; + } + else { + return JSON.stringify(reason); + } + }) + .join(""); +} +/** + * Safely awaits a promise and returns a standardized result object. + * Eliminates the need for repetitive try/catch blocks throughout the codebase. + * + * @param promiseOrFn - Either a promise or a function that returns a promise + * @returns A standardized PromiseResult object indicating success or failure + */ +export async function tryAwait(promiseOrFn) { + try { + // Handle both promise and function that returns a promise + const promise = typeof promiseOrFn === "function" ? promiseOrFn() : promiseOrFn; + const value = await promise; + return makeFulfilledResult(value); + } + catch (error) { + return makeRejectedResult(error); + } +} +//# sourceMappingURL=promise-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/string-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/string-util.js new file mode 100644 index 0000000..affeba3 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/string-util.js @@ -0,0 +1,127 @@ +/** + * Created by dabolfathi on 4/26/17. + */ +/** + * Returns a single string, which is the result of joining together all + * the strings in the list with the provided separator. + * @param strings The list of strings to be joined. + * @param separator The separator to use when joining them together. + * @returns {string} The joined string. + */ +export function join(strings, separator) { + if (strings == null || separator == null) { + return null; + } + if (strings.length === 0) { + return ""; + } + let stringCount = strings.length; + let joinedString = ""; + strings.forEach((element, index) => { + if (element === null) { + stringCount -= 1; + } + else { + joinedString += element; + if (index < stringCount - 1) { + joinedString += separator; + } + } + }); + return joinedString; +} +/** + * Generate a normalized string for robust multilingual search that properly handles + * CJK (Chinese, Japanese, Korean), Arabic, Cyrillic, and Latin-based scripts. + * Preserves Unicode characters while normalizing diacritics and case appropriately. + * + * @param input The input string to normalize + * @returns A normalized string suitable for search matching + */ +export function normalizeForSearch(input) { + if (!input) { + return ""; + } + try { + // Remove special characters + // \u2122 -> (Trade Mark Sign) + // \u2120 -> (Service Mark) + // \u03a9 -> (Greek Capital Letter Omega) + // \u00a9 -> (Copyright Sign) + // \u00ae -> (Registered Sign) + // \u30fc -> (Katakana-Hiragana Prolonged Sound Mark) + // \u03c9 -> (Greek Small Letter Omega) + const removedSpecialUnicodesRegex = /[\u2122\u2120\u03a9\u00a9\u00ae\u30fc\u03c9]/g; + return (input + .toLowerCase() // Case insensitivity + .replace(removedSpecialUnicodesRegex, "") + // Apply normalization only to Latin characters to preserve CJK integrity + // Handle Latin characters safely with basic ranges A-Za-z + // \u00C0-\u00FF - Latin-1 Supplement (Upper Half) + // \u0100-\u017F - Latin Extended-A (includes dotless i \u0131) + // \u0180-\u024F - Latin Extended-B + // \u1E00-\u1EFF - Latin Extended Additional + .replace(/[A-Za-z\u00A0-\u00FF\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF\p{Diacritic}]+/gu, (latinText) => { + return (latinText + .normalize("NFKD") + .replace(/\p{Diacritic}/gu, "") + // Convert specific characters that don't match basic Latin + .replace(/\u0131/g, "i") // Convert dotless i to regular i + // Keep only actual Latin characters and numbers after normalization + .replace(/[^A-Za-z0-9]/g, "")); + }) + // Remove punctuation, symbols, and control characters but preserve: + // - Letters from all writing systems (\p{L}) + // - Numbers (\p{N}) + // - Whitespace (\s) + // - Underscores (_) + .replace(/[^\p{L}\p{N}\s_]/gu, "") + // Normalize multiple whitespace to single spaces + .replace(/\s+/g, " ") + // Trim leading/trailing whitespace + .trim()); + } + catch (error) { + // Fallback: use basic character classes + return (input + .toLowerCase() + // Remove punctuation, symbols, and control characters but preserve: + // - Letters from all writing systems (\p{L}) + // - Numbers (\p{N}) + // - Whitespace (\s) + // - Underscores (_) + .replace(/[^\p{L}\p{N}\s_]/gu, "") + // Normalize multiple whitespace to single spaces + .replace(/\s+/g, " ") + // Trim leading/trailing whitespace + .trim()); + } +} +/** + * Whether or not the input string is contains search term using normalized string which comparing using case insensitive, locale insensitive and ignore all special characters. + * + * @param input + * @param normalizedTerm + * @returns + */ +export function containsSearchTerm(input, normalizedTerm) { + const normalizedInput = normalizeForSearch(input); + return normalizedInput.includes(normalizedTerm); +} +/** + * Wraps a string with bidirectional isolate characters to ensure proper text direction handling. + * This is particularly useful for user-generated content like display names that may contain + * mixed left-to-right and right-to-left text. + * + * @param text The string to wrap with bidi isolates + * @returns The string wrapped with Left-to-Right Isolate (U+2066) and Pop Directional Isolate (U+2069) + */ +export function withBidiIsolates(text) { + if (!text) { + return text; + } + // U+2068: First Strong Isolate + // U+2069: Pop Directional Isolate + return "\u2068" + text + "\u2069"; +} +//# sourceMappingURL=string-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/util/validation-util.js b/node_modules/@jet-app/app-store/tmp/src/foundation/util/validation-util.js new file mode 100644 index 0000000..fe9bd93 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/util/validation-util.js @@ -0,0 +1,30 @@ +import { isSome } from "@jet/environment"; +import * as validation from "@jet/environment/json/validation"; +import { tryAwait } from "./promise-util"; +/** + * Creates a validation context for async operations and ensures proper cleanup. + * + * Wraps an async operation in a validation context, automatically handling + * context cleanup even when errors occur. Optionally processes errors through + * a provided error handler. + * + * @param name - The name for the validation context + * @param producer - Async function that produces the result + * @param errorHandler - Optional function to handle errors from the producer + * @returns The result of the async operation + */ +export async function withAsyncValidationContext(name, producer, errorHandler) { + validation.beginContext(name); + let result = await tryAwait(producer()); + if (!result.success && isSome(errorHandler)) { + result = await tryAwait(errorHandler(result.error)); + } + validation.endContext(); + if (!result.success) { + throw result.error; + } + else { + return result.value; + } +} +//# sourceMappingURL=validation-util.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/apple-silicon.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/apple-silicon.js new file mode 100644 index 0000000..4371e77 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/apple-silicon.js @@ -0,0 +1,13 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +import * as serverData from "../../foundation/json-parsing/server-data"; +import { Wrapper } from "./wrapper"; +export class AppleSiliconWrapper extends Wrapper { + get isSupportEnabled() { + return serverData.isDefinedNonNull(this.implementation) && this.implementation.isSupportEnabled; + } + get isRosettaAvailable() { + return serverData.isDefinedNonNull(this.implementation) && this.implementation.isRosettaAvailable; + } +} +AppleSiliconWrapper.type = makeMetatype("app-store:as-wrapper"); +//# sourceMappingURL=apple-silicon.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/bag.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/bag.js new file mode 100644 index 0000000..ad76b76 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/bag.js @@ -0,0 +1,1126 @@ +/** + * Created by km on 1/9/17. + */ +import { isSome } from "@jet/environment"; +import { makeMetatype } from "@jet/environment/util/metatype"; +import * as serverData from "../json-parsing/server-data"; +import { Wrapper } from "./wrapper"; +import { CachedBag } from "./cached-bag"; +export class BagWrapper extends Wrapper { + constructor(bag, host) { + super(new CachedBag(bag)); + this.underlyingBag = bag; + } + /// The boolean for whether today ad medium lockup screenshots are enabled + get todayAdMediumLockupScreenshotEnabled() { + var _a; + return (_a = this.implementation.boolean("today-ad-medium-lockup-screenshots-enabled")) !== null && _a !== void 0 ? _a : false; + } + get todayAdMediumLockupScreenshotAnimationEnabled() { + var _a; + return (_a = this.implementation.boolean("today-ad-medium-lockup-screenshots-animation-enabled")) !== null && _a !== void 0 ? _a : true; + } + /// The URL for the trending searches endpoint. + get trendingSearchesURL() { + return this.implementation.url("trending-searches"); + } + /// The URL for the search hints endpoint. + get searchHintsURL() { + return this.implementation.url("searchHints"); + } + /// The URL for fetching a personalized review + get personalizedUserReviewURL() { + return this.implementation.url("personalizedUserReviewUrl"); + } + /// The boolean for whether personalized reviews are enabled + get personalizedUserReviewEnabled() { + return this.implementation.boolean("personalizedUserReviewEnabled"); + } + /// The URL for posting tap-to-rate requests. + get userRateURL() { + return this.implementation.url("p2-application-user-rate-content"); + } + /// The URL for posting write review requests. + get writeReviewURL() { + return this.implementation.url("p2-application-user-write-review"); + } + /// The base URL for accessory rooms + get accessoryRoomURL() { + return this.implementation.url("p2-accessory-room"); + } + /// The URL for passbook main room + get passbookMainURL() { + return this.implementation.url("passbook"); + } + /// The URL for library-link room + get libraryLinkURL() { + return this.implementation.url("library-link"); + } + /// The metrics configuration for mt-metrics kit integration + get metricsConfiguration() { + return serverData.asJSONData(this.implementation.dictionary("metrics")); + } + // When the metrics payment topic is enabled the clientId and userId are on a separate namespace. + get metricsPaymentNamespaceEnabled() { + if (serverData.isNullOrEmpty(this.metricsPaymentTopic)) { + return false; + } + const identifiers = serverData.asJSONData(this.implementation.dictionary("metrics-identifiers")); + const metricsNameSpace = serverData.asDictionary(identifiers, "APPSTORE_PAYMENTS_ENGAGEMENT"); + const metricsClientIdSpace = serverData.asDictionary(identifiers, "APPSTORE_PAYMENTS_ENGAGEMENT_CLIENT"); + return (isSome(metricsNameSpace) && + metricsNameSpace.length !== 0 && + isSome(metricsClientIdSpace) && + metricsClientIdSpace.length !== 0); + } + // The Payment topic if one is provided by the bag. Otherwise the default one will be used + get metricsPaymentTopic() { + var _a, _b; + if (preprocessor.GAMES_TARGET) { + return (_a = serverData.asString(this.metricsConfiguration, "topics.GAMES_PAYMENTS_ENGAGEMENT_TOPIC")) !== null && _a !== void 0 ? _a : null; + } + else { + return (_b = serverData.asString(this.metricsConfiguration, "topics.APPSTORE_PAYMENTS_ENGAGEMENT_TOPIC")) !== null && _b !== void 0 ? _b : null; + } + } + // Whether the personalization user id has been inlcuded in the metrics identifiers bag entry + get personalizationUserIdEnabled() { + const identifiers = serverData.asJSONData(this.implementation.dictionary("metrics-identifiers")); + const personalizationIdNameSpace = serverData.asDictionary(identifiers, "APPSTORE_PERSONALIZATION"); + return isSome(personalizationIdNameSpace) && Object.keys(personalizationIdNameSpace).length !== 0; + } + /// The language for the users storefront + get language() { + return this.implementation.string("language"); + } + /// The language for media API + get mediaApiLanguage() { + const languageTag = this.implementation.string("language-tag"); + if (languageTag) { + return languageTag; + } + return this.implementation.string("language"); + } + /// The URL for terms and conditions page + get termsAndConditionsURL() { + return this.implementation.url("p2-service-terms-url"); + } + /// whether we should send iAd data as a post request + get usePostForAppStoreSearch() { + return this.implementation.boolean("usePostForAppStoreSearch"); + } + /// Whether or not monetary gifting is enabled. + get isMonetaryGiftingEnabled() { + return this.implementation.boolean("isBuyingScheduledGiftCertificateEnabled"); + } + /// The URL for Account Top-Up in the finance sheet. + get accountTopUpURL() { + return this.implementation.url("AddFundsUrl"); + } + /// The title for Account Top-Up in the finance sheet. + get accountTopUpTitle() { + return this.implementation.string("account-top-up-title"); + } + /// Whether or not content gifting is enabled. + get isContentGiftingEnabled() { + return this.implementation.boolean("isScheduledGiftingEnabled"); + } + /// The URL for buy button metadata. + get buyButtonMetadataURL() { + return this.implementation.url("personalized-buy-buttons/software"); + } + /// Whether or not the current storefront supports the TV App. + get isTVAppEnabled() { + return this.implementation.boolean("uvSearch/nowplaying-enabled"); + } + /// the url to send people to to email support + get emailSupportLinkURL() { + return this.implementation.url("supportLinkUrl"); + } + /// The URL to report a review item helpful or not. + get voteUrl() { + return this.implementation.url("voteUrl"); + } + /// Enables the Review Summary module + get enableReviewSummarization() { + return this.implementation.boolean("enable-review-summarization"); + } + /// Provides configuration for Review Summary Report A Concern + get reviewSummaryReportConcernData() { + return serverData.asJSONData(this.implementation.dictionary("review-summarization-report-concern")); + } + /// The URL to report a concern about a review item. + get reportConcernUrl() { + return this.implementation.url("reportConcernUrl"); + } + /// The explanation for the report a concern screen for a review. + get reportConcernExplanation() { + return this.implementation.string("reportConcernExplanation"); + } + /// An array of reasons for reporting a concern for a review. + get reportConcernReasons() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("reportConcernReasons"))); + } + /// The boolean that enables report a problem. + get reportProblemEnabled() { + var _a; + return (_a = this.implementation.boolean("product-page-report-problem-enabled")) !== null && _a !== void 0 ? _a : false; + } + /// The bag key that stores the report a problem URL. + get productPageReportProblemURL() { + return this.implementation.string("product-page-report-problem-url"); + } + /// The string array of adamIDs of SAD apps with subscriptions. + get productPageReportProblemSADSubscriptionArray() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("product-page-report-problem-sad-subscriptions"))); + } + /// The string array of second party apps to not show the report a problem link for. + /// We have added the array of IDs while we wait for bag key support. Removal is tracked by rdar://82606581 (Remove array of second party app ids from JS bag.) + get productPageReportProblemSecondPartyAppArray() { + const secondPartyAppArray = serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("product-page-report-problem-second-party-apps"))); + const defaultSecondPartyAppArray = [ + "1473505534", + "1416238567", + "640199958", + "1529498570", + "915061776", + "1130498044", + "1070072560", + ]; + if (serverData.isNullOrEmpty(secondPartyAppArray)) { + return defaultSecondPartyAppArray; + } + return secondPartyAppArray; + } + /// The URL to create a new account. + get createAccountUrl() { + var _a; + return ((_a = this.implementation.url("createAccountUrl")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/signupWizard"); + } + get mediaCountryCode() { + return this.implementation.string("countryCode"); + } + get mediaHost() { + return this.implementation.url("apps-media-api-host"); + } + mediaEdgeHost(objectGraph) { + // The key has different type for AppStoreComponents + // see <rdar://71290056>. + if (objectGraph.host.clientIdentifier === "com.apple.appstorecomponentsd") { + return this.implementation.url("apps-media-api-edge-host"); + } + else { + return this.implementation.string("apps-media-api-edge-host"); + } + } + get mediaAPICatalogMixedShouldUseEdge() { + var _a; + return (_a = this.implementation.boolean("apps-media-api-catalog-mixed-should-use-edge")) !== null && _a !== void 0 ? _a : false; + } + get mediaEdgeSearchHost() { + return this.implementation.string("apps-media-api-search-edge-host"); + } + get mediaPreviewHost() { + return this.implementation.string("apps-media-api-preview-host"); + } + get mediaRealmHost() { + return this.implementation.string("notification-settings-media-api-host"); + } + get edgeEndpoints() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("apps-media-api-edge-end-points"))); + } + get mediaAdvertRequestLimit() { + var _a; + return (_a = this.implementation.double("apps-media-api-search-ads-limit")) !== null && _a !== void 0 ? _a : 4; + } + get searchSortOptions() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("searchSortOptions"))); + } + get ageBands() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("ageBands"))); + } + get redirectUrlWhitelistedQueryParams() { + let params = serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("processRedirectUrl/whitelistedQueryParams"))); + if (serverData.isNullOrEmpty(params)) { + params = [ + "affC", + "adId", + "advp", + "at", + "ct", + "itsct", + "itscg", + "itscc", + "itcCt", + "its_qt", + "ls", + "partnerId", + "pt", + "qtkid", + "uo", + ]; + } + return params; + } + get redirectUrlEndpoint() { + var _a; + return ((_a = this.implementation.string("processRedirectUrl/endpoint")) !== null && _a !== void 0 ? _a : "https://itunes.apple.com/WebObjects/MZStoreServices.woa/wa/processRedirectUrl"); + } + get aristotleParentAppAdamId() { + var _a; + return (_a = this.implementation.string("aristotle-app-id")) !== null && _a !== void 0 ? _a : "383941000"; + } + /// The AdamId of the App Store app which owns the IAP for Arcade subscriptions + get arcadeAppAdamId() { + return this.implementation.string("app-store-app-id"); + } + /// The product family of the Arcade subscription IAP. This is for multiple IAPs for the same service not family sharing. + get arcadeProductFamilyId() { + var _a; + return (_a = this.implementation.string("arcade-iap-family-id")) !== null && _a !== void 0 ? _a : this.implementation.string("ocelot-iap-family-id"); + } + /// The Identifier (developer supplied reverse dns style name) of the IAP for Arcade subscriptions + get arcadeProductId() { + var _a; + return ((_a = this.implementation.string("arcade-iap-offer-name")) !== null && _a !== void 0 ? _a : this.implementation.string("ocelot-iap-offer-name")); + } + /// The percentage of users that will see the Arcade category bar See All Games uplift. + get arcadeCategoryBarSAGUpliftDisplayRate() { + var _a; + return (_a = this.implementation.double("arcade-category-bar-see-all-games-display-rate")) !== null && _a !== void 0 ? _a : 0.0; + } + get isArcadeEnabled() { + var _a; + return (_a = this.implementation.boolean("arcade-enabled")) !== null && _a !== void 0 ? _a : false; + } + get isAppsGroupingTagsEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-groupings-tags-enabled")) !== null && _a !== void 0 ? _a : false; + } + get isAppsProductPageTagsEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-product-page-tags-enabled")) !== null && _a !== void 0 ? _a : false; + } + get isAppsSlpTagsEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-slp-tags-enabled")) !== null && _a !== void 0 ? _a : false; + } + get searchResultsLearnMoreEditorialId() { + return this.implementation.string("transparencyLawEditorialItemId"); + } + /// An array containing a mapping of system app bundle IDs and adam IDs. + get systemApps() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("hideableSystemApps"))); + } + /// An array containing a mapping of system app bundle IDs and adam IDs + /// Should only be used for watchOS as a temporary fix for rdar://100234873 + get nonDeletableSystemApps() { + const apps = serverData.asJSONValue(this.implementation.array("nonDeletableSystemApps")); + if (serverData.isDefinedNonNullNonEmpty(apps)) { + return serverData.asArrayOrEmpty(apps); + } + else { + return [ + { "id": 1635387927, "bundle-id": "com.apple.Depth" }, + { "id": 1635862301, "bundle-id": "com.apple.Mandrake" }, + { "id": 1584216343, "bundle-id": "com.apple.findmy.finddevices" }, + { "id": 1584215960, "bundle-id": "com.apple.NanoWorldClock" }, + { "id": 1584215812, "bundle-id": "com.apple.HeartRate" }, + { "id": 1584215851, "bundle-id": "com.apple.SessionTrackerApp" }, + { "id": 1146562108, "bundle-id": "com.apple.NanoPhone" }, + { "id": 1146560473, "bundle-id": "com.apple.MobileSMS" }, + { "id": 1584215428, "bundle-id": "com.apple.NanoPhotos" }, + { "id": 1459455352, "bundle-id": "com.apple.DeepBreathing" }, + { "id": 1067456176, "bundle-id": "com.apple.NanoCompass.watchkitapp" }, // Compass + ]; + } + } + /// The standard tabs for the current storefront. + get tabsStandard() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("tabs/standard"))); + } + // URL to sub-in for deep links specifying the watch category. Note that this can be a multiplexing url. + get watchAppsGroupingURL() { + var _a; + return ((_a = this.implementation.url("watchAppsGrouping")) !== null && _a !== void 0 ? _a : "https://apps.apple.com/WebObjects/MZStore.woa/wa/viewFeature?id=1472048385"); + } + get requireAgeVerification() { + return this.implementation.boolean("requireAgeVerification"); + } + get ageRatingLearnMoreEditorialItemId() { + return this.implementation.string("ageRatingLearnMoreEditorialItemId"); + } + get appleSiliconMacUnverifiedBadgeEditorialItemId() { + return this.implementation.string("appleSiliconMacUnverifiedBadgeEditorialItemId"); + } + get safariExtensionsGroupingURL() { + return this.implementation.url("safariExtensionsGrouping"); + } + get familySubscriptionsLearnMoreEditorialItemId() { + return this.implementation.string("familySubscriptionsLearnMoreEditorialItemId"); + } + get dynamicUIRegexStrings() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("commerce-ui-urls/dynamic-url-patterns"))); + } + get financeUIRegexStrings() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("commerce-ui-urls/url-patterns"))); + } + get webViewRegexStrings() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("commerce-ui-urls/v2-url-patterns"))); + } + get arcadePreOrderUpsellLimitSeconds() { + var _a; + return (_a = this.implementation.double("arcadePreOrderUpsellLimitSeconds")) !== null && _a !== void 0 ? _a : 86400; + } + get recentlyPlayedGamesWindowInSeconds() { + var _a; + return (_a = this.implementation.double("recentlyPlayedGamesWindowInSeconds")) !== null && _a !== void 0 ? _a : 7776000; // 90 days + } + get gamesFriendsPlayedWindowInSeconds() { + var _a; + return (_a = this.implementation.integer("games-friends-played-window-in-seconds")) !== null && _a !== void 0 ? _a : 15778800; // Half a year + } + get enableComingSoonToggle() { + return this.implementation.boolean("enableComingSoonToggle"); + } + /// Whether or not to show the app acessibility labels + get enableAppAccessibilityLabels() { + var _a; + return (_a = this.implementation.boolean("enable-app-accessibility-labels")) !== null && _a !== void 0 ? _a : false; + } + /// Whether or not to show the app privacy labels + get enablePrivacyNutritionLabels() { + var _a; + return (_a = this.implementation.boolean("enable-privacy-nutrition-labels")) !== null && _a !== void 0 ? _a : false; + } + /// Whether or not to show the seller info + get enableSellerInfo() { + var _a; + return (_a = this.implementation.boolean("enable-seller-info")) !== null && _a !== void 0 ? _a : false; + } + // Whether or not to show the seller ICP annotation + get enableSellerICPAnnotation() { + var _a; + return (_a = this.implementation.boolean("enable-seller-icp")) !== null && _a !== void 0 ? _a : false; + } + /// Whether to display enhanced category features (bar, breakout, and swoosh) on apps and games tabs + get enableFeaturedCategoriesOnGroupings() { + var _a; + return (_a = this.implementation.boolean("enable-featured-categories-on-groupings")) !== null && _a !== void 0 ? _a : false; + } + /// Whether to display enhanced category bricks on apps and games tabs + get enableCategoryBricksOnGroupings() { + var _a; + return (_a = this.implementation.boolean("enable-category-bricks-on-groupings")) !== null && _a !== void 0 ? _a : false; + } + get arcadeOfferEditorialItemId() { + return this.implementation.string("arcadeOfferEditorialItemId"); + } + get sponsoredSearchODMLTimeout() { + var _a; + return (_a = this.implementation.double("sponsored-search-odml-timeout")) !== null && _a !== void 0 ? _a : 3; + } + get isSearchLandingAdsEnabled() { + var _a; + return (_a = this.implementation.boolean("isSearchLandingAdsEnabled")) !== null && _a !== void 0 ? _a : false; + } + get isLLMSearchTagsEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-search-tags-enabled")) !== null && _a !== void 0 ? _a : false; + } + get searchLandingAdFetchTimeout() { + var _a; + return (_a = this.implementation.double("search-landing-ad-fetch-timeout")) !== null && _a !== void 0 ? _a : 0.175; + } + // Time that SLP needs to be offscreen for it to be refreshed in seconds. + get searchLandingPageOffscreenRefreshInterval() { + var _a; + return (_a = this.implementation.double("search-landing-offscreen-refresh-interval-in-seconds")) !== null && _a !== void 0 ? _a : 60; // default is minute + } + // Time to delay the data fetch for SLP when refreshing + get searchLandingPageRefreshUpdateDelayInterval() { + var _a; + return (_a = this.implementation.double("search-landing-page-update-delay-interval-in-seconds")) !== null && _a !== void 0 ? _a : 0.3; // default is 0.3s + } + get appPrivacyLearnMoreEditorialItemId() { + return this.implementation.string("appPrivacyLearnMoreEditorialItemId"); + } + // The editorial id for the article page showing the transparency info for ratings and reviews + get ratingsAndReviewsLearnMoreEditorialId() { + return this.implementation.string("ratings-and-reviews-learn-more-editorial-item-id"); + } + // The editorial id for the article page showing info about the review summary + get reviewSummarizationLearnMoreEditorialItemId() { + return this.implementation.string("review-summarization-learn-more-editorial-item-id"); + } + /// An array containing a list of app bundle IDs and adam IDs for apps that should not display + /// any app privacy shelves + get suppressedPrivacyAppIds() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("suppressedPrivacyLabels"))); + } + /// An array containing a list of app bundle IDs and adam IDs for apps that should not display + /// any app accessibility shelves + get suppressedAccessibilityAppIds() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("suppressed-accessibility-labels"))); + } + get appPrivacyDefinitionsEditorialItemId() { + return this.implementation.string("appPrivacyDefinitionsEditorialItemId"); + } + // The EI ID for the web's category tabs shown in the sidebar + get webNavigationCategoryTabsEditorialItemId() { + return this.implementation.string("web-navigation-category-tabs-editorial-item-id"); + } + /// The percentage of users that will see a live preview of the widget in the Widget Gallery + get todayWidgetLivePreviewRolloutRate() { + var _a; + return (_a = this.implementation.double("todayWidgetLivePreviewRolloutRate")) !== null && _a !== void 0 ? _a : 1.0; + } + /// The percentage of users that will see the new carousel item styles under Hero 3.0 + get hero3RolloutRate() { + var _a; + return (_a = this.implementation.double("arcade-hero-shelf-tagline-style-rollout-rate")) !== null && _a !== void 0 ? _a : 1.0; + } + /// The percentage of users that will see the trial enrolled (subscriber) tab + get arcadeTrialEnrolledStateRate() { + var _a; + return (_a = this.implementation.double("arcade-trial-enrolled-state-rate")) !== null && _a !== void 0 ? _a : 0.0; + } + get marketingItemSelectionTimeout() { + var _a; + return (_a = this.implementation.double("marketing-item-selection-timeout")) !== null && _a !== void 0 ? _a : 1.0; + } + /// Whether or not to enable app events + get enableAppEvents() { + var _a; + return (_a = this.implementation.boolean("enableAppEvents")) !== null && _a !== void 0 ? _a : false; + } + /// Whether or not product page variants is enabled (feature-gate) + get enableProductPageVariants() { + var _a; + return (_a = this.implementation.boolean("enableProductPageVariants")) !== null && _a !== void 0 ? _a : false; + } + get enableArcadeTrialEligibleBadging() { + return this.implementation.boolean("enable-arcade-trial-eligible-badging"); + } + /// The duration we should use for the hero carousel auto scrolling + get heroCarouselAutoScrollDuration() { + var _a; + return (_a = this.implementation.double("heroCarouselAutoScrollDuration")) !== null && _a !== void 0 ? _a : 7.0; + } + /// Enable additional JS logging for Product Page Variants + get enableAdditionalLoggingForPPV() { + var _a; + return (_a = this.implementation.boolean("enableAdditionalLoggingForPPV")) !== null && _a !== void 0 ? _a : false; + } + /// Whether or not to enable on device personalization + get enableOnDevicePersonalization() { + const enableOnDevicePersonalization = this.implementation.boolean("enable-on-device-personalization"); + if (serverData.isNull(enableOnDevicePersonalization)) { + return true; + } + return enableOnDevicePersonalization; + } + /// Whether or not automatic page refreshing is enabled + get enableAutomaticPageRefresh() { + var _a; + return (_a = this.implementation.boolean("enable-automatic-page-refresh")) !== null && _a !== void 0 ? _a : true; + } + /// Rollout rate for smart stack suggestions for the today widget. + get widgetSuggestionsFromTodayTabRolloutRate() { + var _a; + return (_a = this.implementation.double("today-widget-suggestions-from-today-tab-rollout-rate")) !== null && _a !== void 0 ? _a : 1.0; + } + /// The upper bound for how many extra minutes we delay the smart stack from suggesting the Today Widget (to protect MAPI). + get todayWidgetSmartStackJitterMinutes() { + var _a; + return (_a = this.implementation.double("today-widget-smart-stack-jitter-minutes")) !== null && _a !== void 0 ? _a : 45; + } + get enableSystemAppReviews() { + var _a; + return (_a = this.implementation.boolean("enable-system-app-reviews")) !== null && _a !== void 0 ? _a : false; + } + /// Whether or not CPPs are enabled for Search Ads. + get enableCPPInSearchAds() { + return this.implementation.boolean("enableCPPsInSearchAds") || false; + } + get cancelPreorderItemSrv() { + return (this.implementation.url("cancelPreorderItemSrv") || "https://buy.itunes.apple.com/commerce/preorders/cancel"); + } + get getCancellablePreorderItemsSrv() { + return (this.implementation.url("getCancellablePreorderItemsSrv") || + "https://buy.itunes.apple.com/commerce/preorders/cancellable"); + } + /// An array of the enabled ad placements. + get enabledAdPlacements() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("enabled-ad-placements"))); + } + /// A dictionary of timeout values for the ad placements. + get adPlacementTimeouts() { + return serverData.asDictionary(serverData.asJSONData(this.implementation.dictionary("ad-placement-timeouts"))); + } + /// The editorial story ID for In App Purchases + get inAppPurchasesLearnMoreEditorialItemId() { + return this.implementation.string("in-app-purchases-learn-more-editorial-item-id"); + } + /// Determines whether external purchases are enabled. + get enableExternalPurchases() { + var _a; + return (_a = this.implementation.boolean("enable-external-purchase")) !== null && _a !== void 0 ? _a : false; + } + /// An array of the enabled external purchases placements + get enabledExternalPurchasesPlacements() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("enabled-external-purchase-placements"))); + } + /// The editorial item ID for the external purchases story + get externalPurchasesLearnMoreEditorialItemId() { + return this.implementation.string("external-purchase-learn-more-editorial-item-id"); + } + /// The editorial item ID for the external browser story + get externalBrowserLearnMoreEditorialItemId() { + return this.implementation.string("external-browser-learn-more-editorial-item-id"); + } + /// The SharePlay groupings url + get sharePlayAppsEditorialItemId() { + return this.implementation.string("share-play-apps-editorial-item-id"); + } + /// Determines whether to show the icon on the external purchases product page banner + get externalPurchasesIncludeProductPageBannerIcon() { + var _a; + return (_a = this.implementation.boolean("external-purchase-product-page-banner-include-icon")) !== null && _a !== void 0 ? _a : false; + } + /// Determines whether to use a variant of the external purchases product page annotation copy + get externalPurchasesProductPageAnnotationVariant() { + return this.implementation.string("external-purchase-product-page-annotation-variant"); + } + /// Tells us whether we want new events for ODJ to be enabled or not. + get newEventsForODJAreEnabled() { + var _a; + return (_a = this.implementation.boolean("new-events-for-odj-are-enabled")) !== null && _a !== void 0 ? _a : false; + } + get defaultChart() { + return serverData.asJSONData(this.implementation.dictionary("default-chart")); + } + // The url to display the user account + get accountUrl() { + var _a; + return ((_a = this.implementation.url("accountUrl")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/accountSummary"); + } + // The url to display the user account + get redeemUrl() { + var _a; + return ((_a = this.implementation.url("redeemUrl")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/redeemLandingPage"); + } + // The url to display the user account + get charityUrl() { + var _a; + return ((_a = this.implementation.url("charityUrl")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/buyCharityGiftWizard"); + } + // The url to display the manage subs page + get manageSubscriptionsUrl() { + var _a; + return ((_a = this.implementation.url("manageSubscriptionsUrl")) !== null && _a !== void 0 ? _a : "https://finance-app.itunes.apple.com/subscriptions/manage?context=deeplink"); + } + // The url to display the manage subs page + get manageSubscriptionsV2Url() { + var _a; + return ((_a = this.implementation.url("manageSubscriptionsV2Url")) !== null && _a !== void 0 ? _a : "https://apps.mzstatic.com/content/54a1317a0ad442d3965d64ef6bfaae1c/"); + } + /// The language value to use for ad language content filtering. + /// This may not necessarily match the user's language setting - in some regions ads are required + /// to be localized to the country's primary language, regardless of the user's own preferences. + get adsOverrideLanguage() { + return this.implementation.string("ads-override-language"); + } + /// The percentage of users that will see the controller recommended or required treatment + /// (as opposed to supported) on supported platforms. + get gameControllerRecommendedRolloutRate() { + var _a; + return (_a = this.implementation.double("game-controller-recommended-rollout-rate")) !== null && _a !== void 0 ? _a : 0; + } + /// The ID of the editorial item that provides information about game controllers. + get gameControllerLearnMoreEditorialItemId() { + return this.implementation.string("game-controller-learn-more-editorial-item-id"); + } + /// The ID of the editorial item that provides information about spatial controllers. + get spatialControlsLearnMoreEditorialItemId() { + return this.implementation.string("spatial-controllers-learn-more-editorial-item-id"); + } + // Whether the user can use MAPI based search focus + get mediaAPISearchFocusEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-search-focus-suggestions-enabled")) !== null && _a !== void 0 ? _a : false; + } + /// Indicates whether the Search Landing Page supports the V2 protocol + get supportsSearchLandingPageV2() { + var _a; + return (_a = this.implementation.boolean("supports-apps-slp-v2")) !== null && _a !== void 0 ? _a : false; + } + get enableSearchLandingPageV2ByTreatment() { + var _a; + return (_a = this.implementation.boolean("enable-apps-slp-v2-by-treatment")) !== null && _a !== void 0 ? _a : false; + } + /// The percentage of users that will see the new Search Landing Page V2 protocol + get searchLandingPageV2RolloutRate() { + var _a; + return (_a = this.implementation.double("apps-slp-v2-rollout-rate")) !== null && _a !== void 0 ? _a : 0; + } + /// The percentage of users that will see the Today tab Arcade personalization + get todayTabArcadePersonalizationRate() { + var _a; + return (_a = this.implementation.double("today-tab-arcade-personalization-rate")) !== null && _a !== void 0 ? _a : 0; + } + /// Whether to enable extending supported Game Center features. + get gameCenterExtendSupportedFeatures() { + var _a; + return (_a = this.implementation.boolean("game-center-extend-supported-features")) !== null && _a !== void 0 ? _a : false; + } + get adPlacementEligibleSlotPositions() { + const eligibleSlotPositions = serverData.asJSONData(this.implementation.dictionary("ad-placement-eligible-slot-positions")); + if (serverData.isDefinedNonNullNonEmpty(eligibleSlotPositions)) { + return eligibleSlotPositions; + } + return { + "today": [ + { + shelfIdentifier: "0", + slot: 0, + }, + { + shelfIdentifier: "0", + slot: 1, + }, + ], + "product-page-ymal": [ + { + shelfIdentifier: "customers-also-bought-apps", + slot: 0, + }, + ], + }; + } + // The url to display the manage preorders page + get managePreordersUrl() { + var _a; + return (_a = this.implementation.url("preordersUrl")) !== null && _a !== void 0 ? _a : "https://finance-app.itunes.apple.com/preorders"; + } + // A url to modify the user's account. + get modifyAccount() { + var _a; + return ((_a = this.implementation.url("modifyAccount")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/accountSummary"); + } + // The url to display the purchase history page + get purchaseHistoryUrl() { + return this.implementation.url("purchaseHistoryUrl"); + } + // The url to display the ratings and reviews page + get ratingsReviewsUrl() { + var _a; + return ((_a = this.implementation.url("ratingsReviewsUrl")) !== null && _a !== void 0 ? _a : "https://apps.mzstatic.com/content/54a1317a0ad442d3965d64ef6bfaae1c/ratings-reviews"); + } + // A url to sign up for a new account. + get signup() { + var _a; + return ((_a = this.implementation.url("signup")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/signupWizard"); + } + // The landing page to redeem a code. + get redeemCodeLanding() { + var _a; + return ((_a = this.implementation.url("redeemCodeLanding")) !== null && _a !== void 0 ? _a : "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/redeemLandingPage"); + } + /// The bag key that stores the generic report a problem URL. + get reportProblemUrl() { + return this.implementation.string("reportProblemUrl"); + } + // Whether the app store should allow unrestricted arcade tab badging by server side marketing journeys + get unrestrictedServerSideTabBadging() { + var _a; + return (_a = this.implementation.boolean("unrestricted-server-side-tab-badging")) !== null && _a !== void 0 ? _a : false; + } + // Whether all users should receive the "condensed" today ad. + get todayAdCondensedEnabled() { + var _a; + return (_a = this.implementation.boolean("today-ad-condensed-enabled")) !== null && _a !== void 0 ? _a : false; + } + // Whether to enable bin-compat checks for the visionOS App Store + get enableVisionAppStoreBinCompatChecks() { + var _a; + return (_a = this.implementation.boolean("enable-vision-app-store-bincompat-checks")) !== null && _a !== void 0 ? _a : false; + } + // The ID for an editorial page containing Safari Extensions + get safariExtensionsEditorialPageId() { + return this.implementation.url("safari-extensions-editorial-page-id"); + } + /// The ID of the editorial item to show when deeplinking to buddy onboarding + get buddyOnboardingEditorialItemId() { + return this.implementation.string("buddy-onboarding-editorial-item-id"); + } + // Whether click events should be sent for the friends playing shelf on the product page + get productPageFriendsPlayingClickEventsEnabled() { + var _a; + return (_a = this.implementation.boolean("product-page-friends-playing-click-events-enabled")) !== null && _a !== void 0 ? _a : false; + } + // ID of the editorial item to show for more information about the "High Motion" annotation. + get highMotionLearnMoreEditorialItemId() { + return this.implementation.string("high-motion-learn-more-editorial-item-id"); + } + // The speed of the river in the medium lockup with screenshots ad. + // This is temporary, and only for testing purposes. + get todayAdMediumLockupScreenshotsRiverSpeed() { + return this.implementation.double("today-ad-medium-lockup-screenshots-river-speed"); + } + // Collection ID of the game categories for Arcade download/starter pack onboarding. + get arcadeDownloadPackCategoriesCollectionId() { + return this.implementation.string("arcade-download-packs-onboarding-collection-id"); + } + // Time interval in seconds where Arcade download pack shelf is visible after user has gone through the onboarding flow. + get arcadeDownloadPackShelfTTLInSeconds() { + var _a; + return (_a = this.implementation.integer("arcade-starter-pack-ttl-in-seconds")) !== null && _a !== void 0 ? _a : 0; + } + // The feature flag as whether or not we should display IAP offers (eg winback offers). + get enableOfferItems() { + var _a; + return (_a = this.implementation.boolean("enable-winback-offers")) !== null && _a !== void 0 ? _a : false; + } + // The host for App Distribution Media API requests. + get appDistributionMediaAPIHost() { + return this.implementation.string("app-distribution-media-api-host"); + } + // The language tag to use for App Distribution Media API requests. + get appDistributionLanguageTag() { + return this.implementation.string("app-distribution-language-tag"); + } + // Whether App Distribution is supported. + get supportsAppDistribution() { + var _a; + return (_a = this.implementation.boolean("supports-app-distribution")) !== null && _a !== void 0 ? _a : false; + } + // Whether Arcade download/starter pack onboarding should be triggered after Arcade purchase from the page offer button. + // When Mercury supports this flow the flag should be disabled. + get arcadeDownloadPackPostSubscribeTrigger() { + var _a; + return (_a = this.implementation.boolean("arcade-download-packs-post-subscribe-trigger")) !== null && _a !== void 0 ? _a : true; + } + // The feature flag as whether or not we should display contingent offers. + get enableContingentOffers() { + var _a; + return (_a = this.implementation.boolean("enable-contingent-offers")) !== null && _a !== void 0 ? _a : false; + } + /// The percentage of users that will see Arcade packs onboarding experience. + get arcadeDownloadPackRolloutRate() { + var _a; + return (_a = this.implementation.double("arcade-download-packs-rollout-rate")) !== null && _a !== void 0 ? _a : 0; + } + // Whether to enable including the Vision platform in fetch requests + get enableVisionPlatform() { + var _a; + return (_a = this.implementation.boolean("enable-vision-platform")) !== null && _a !== void 0 ? _a : false; + } + // Dictionary containing the use cases that are supported for mixed media requests. + get supportedMixedMediaRequestUsecases() { + var _a; + return ((_a = serverData.asDictionary(serverData.asJSONData(this.implementation.dictionary("supported-mixed-media-request-usecases")))) !== null && _a !== void 0 ? _a : {}); + } + // Whether the page and click events are enabled for Arcade download pack. + get arcadeDownloadPacksMetricsEventsEnabled() { + var _a; + return (_a = this.implementation.boolean("arcade-download-packs-metrics-events-enabled")) !== null && _a !== void 0 ? _a : true; + } + // Whether the impression events are enabled for Arcade download pack. + get arcadeDownloadPacksImpressionEventsEnabled() { + var _a; + return (_a = this.implementation.boolean("arcade-download-packs-impression-events-enabled")) !== null && _a !== void 0 ? _a : true; + } + /// The learn more EI ID for when purchasing a visionOS only app on iOS + get visionOnlyAppLearnMoreEditorialItemId() { + return this.implementation.string("vision-only-app-learn-more-editorial-item-id"); + } + // Whether Arcade download pack onboarding will be shown after successful CIP carrier link. + get arcadeDownloadPacksCIPDeeplinkIntegrationEnabled() { + var _a; + return (_a = this.implementation.boolean("arcade-download-packs-cip-deeplink-trigger")) !== null && _a !== void 0 ? _a : false; + } + // Whether Arcade download pack onboarding will be shown after Hardware upsell successful purchase. + // Only for tab badge upsell. + get arcadeDownloadPacksHardwareTabBadgeUpsellIntegrationEnabled() { + var _a; + return (_a = this.implementation.boolean("arcade-download-packs-hw-tabbadge-trigger")) !== null && _a !== void 0 ? _a : false; + } + // A URL that links to the About the App Store website + get aboutAppStoreUrl() { + return this.implementation.string("about-app-store-url"); + } + // The EI ID for the About In App Purchases article + get aboutInAppPurchasesEditorialItemId() { + return this.implementation.string("about-in-app-purchases-editorial-item-id"); + } + // A URL that links to the Request A Refund website + get requestARefundUrl() { + return this.implementation.string("request-a-refund-url"); + } + // Whether the personalized recommendations toggle should be displayed in the personalized + // recommendations screen. + get personalizedRecommendationsToggleEnabled() { + var _a; + return (_a = this.implementation.boolean("enable-personalized-recommendations-toggle")) !== null && _a !== void 0 ? _a : false; + } + /// Whether we should be using metricsId exclusively and remove DSID in events + get metricsIdMigrationEnabled() { + var _a; + return (_a = this.implementation.boolean("metrics-id-migration-enabled")) !== null && _a !== void 0 ? _a : true; + } + // Whether natural language search is enabled for all features, e.g. What's New, Bubble Tip, Hints, and Results. + get isNaturalLanguageSearchEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-natural-language-search-enabled")) !== null && _a !== void 0 ? _a : false; + } + // Whether natural language search is partially enabled, e.g. Bubble Tip and Results only, excludes What's New and Hints. + get isNaturalLanguageSearchResultsEnabled() { + var _a; + return (_a = this.implementation.boolean("apps-natural-language-search-results-enabled")) !== null && _a !== void 0 ? _a : false; + } + /// Whether we should be using metricsId exclusively and remove DSID in events + get metricsIdentifiersShouldCache() { + var _a; + return (_a = this.implementation.boolean("metrics-identifiers-should-cache")) !== null && _a !== void 0 ? _a : true; + } + // A URL that links to the Change Your Payment Method support article + get changePaymentMethodUrl() { + return this.implementation.string("change-payment-method-url"); + } + // The EI ID for the Information about the French App Store article + get aboutFrenchAppStoreEditorialItemId() { + return this.implementation.string("about-app-store-editorial-item-id"); + } + // A bag override which can disable product page on demand shelf fetching if necessary, without + // a JS release + get isOnDemandShelfFetchingEnabled() { + var _a; + return (_a = this.implementation.boolean("on-demand-product-shelf-fetching-enabled")) !== null && _a !== void 0 ? _a : true; + } + // A bag override which can enable the addition of the dsId field to metrics events in the case of + // missing userId + get isMetricsUserIdFallbackEnabled() { + var _a; + return (_a = this.implementation.boolean("metrics-user-id-fallback-enabled")) !== null && _a !== void 0 ? _a : false; + } + // A bag override which can enable the addition of the alt_ab2_data field to metrics events in the case of + // missing ab2_data + get isMetricsAb2DataFallbackEnabled() { + var _a; + return (_a = this.implementation.boolean("metrics-ab2data-fallback-enabled")) !== null && _a !== void 0 ? _a : !preprocessor.GAMES_TARGET; + } + /// Whether or not to enable on device reco reordering. Default to true while in dev. + get enableRecoOnDeviceReordering() { + var _a; + return (_a = this.implementation.boolean("enable-on-device-reco-reordering")) !== null && _a !== void 0 ? _a : false; + } + // The EI IDs for any Vision Pro ribbon bar items + get ribbonBarVisionEditorialItemIds() { + return serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("ribbon-bar-vision-editorial-item-ids"))); + } + // The IDs for any EIs that should be filtered from search results + get searchFilterEditorialItemIds() { + const array = serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("search-filter-editorial-item-ids"))); + return new Set(array); + } + // The ID for the accessibility learn more EI on the product page + get accessibilityLearnMoreEditorialItemId() { + return this.implementation.string("accessibility-learn-more-editorial-item-id"); + } + /// Whether discovery content based on ownership of a Vision Pro device is enabled + get enableDeviceDrivenDiscoveryContent() { + var _a; + return (_a = this.implementation.boolean("enable-device-driven-discovery-content")) !== null && _a !== void 0 ? _a : false; + } + /// Whether to show install size for apps on the product page + get enableProductPageInstallSize() { + var _a; + return (_a = this.implementation.boolean("enable-product-page-install-size")) !== null && _a !== void 0 ? _a : false; + } + // Whether we show the prerendered icon artwork + get enableIconArtwork() { + var _a; + return (_a = this.implementation.boolean("enable-icon-artwork")) !== null && _a !== void 0 ? _a : false; + } + // The rollout rate associated with `enable-icon-artwork` + get iconArtworkRolloutRate() { + var _a; + return (_a = this.implementation.double("icon-artwork-rollout-rate")) !== null && _a !== void 0 ? _a : 0.0; + } + // Whether the new age rating system should be used + get enableUpdatedAgeRatings() { + var _a; + return (_a = this.implementation.boolean("enable-app-store-age-ratings")) !== null && _a !== void 0 ? _a : false; + } + // Whether age rating should be sent to MAPI to filter returned content + get enableAgeRatingFilter() { + var _a; + return (_a = this.implementation.boolean("enable-age-rating-filter")) !== null && _a !== void 0 ? _a : false; + } + // Whether two-phase buy is enabled. This is expected to always be false, and only exists + // as an escape hatch. + get enableTwoPhaseOfferConfirmation() { + var _a; + return (_a = this.implementation.boolean("enable-two-phase-offer-confirmation")) !== null && _a !== void 0 ? _a : false; + } + /// Determines whether to use a variant of the external purchases product page banner text copy + get externalPurchasesProductPageBannerTextVariant() { + return this.implementation.string("external-purchase-product-page-banner-text-variant"); + } + /// Describes the variant of the external purchases product page banner icon to use + get externalPurchasesProductPageBannerIconVariant() { + return this.implementation.string("external-purchase-product-page-banner-icon-variant"); + } + /** ******* GameStoreKit *********/ + // A default maximum number of games used for query events & updates. + get maxGamesForFetchingEvents() { + var _a; + return (_a = this.implementation.integer("max-games-for-fetching-events")) !== null && _a !== void 0 ? _a : 30; + } + // The mock home feed url for testing + get mockHomeFeedURL() { + return this.implementation.string("mock-home-feed-url"); + } + // Disable play-together endpoint + get disablePlayTogetherEndpoint() { + var _a; + return (_a = this.implementation.boolean("disable-play-together-endpoint")) !== null && _a !== void 0 ? _a : false; + } + // Metrics topic for the current bundle. + get metricsTopic() { + var _a, _b; + if (preprocessor.GAMES_TARGET) { + const defaultTopic = "xp_amp_gc_cs"; + const metricsTopic = (_a = serverData.asString(this.metricsConfiguration, "topics.GAMES_CLICKSTREAM_TOPIC")) !== null && _a !== void 0 ? _a : null; + return metricsTopic !== null && metricsTopic !== void 0 ? metricsTopic : defaultTopic; + } + else { + const defaultTopic = "xp_ase_appstore_ue"; + return (_b = this.implementation.string("metrics_topic")) !== null && _b !== void 0 ? _b : defaultTopic; + } + } + // A default collectionID of the game recommendations for arcade subscriber. + get playTogetherGameRecommendationsArcade() { + var _a; + return (_a = this.implementation.string("play-together-arcade-game-recommendations")) !== null && _a !== void 0 ? _a : "1803255513"; + } + // A default collectionID of the game recommendations for users with no arcade subscription. + get playTogetherGameRecommendationsNonArcade() { + var _a; + return (_a = this.implementation.string("play-together-non-arcade-game-recommendations")) !== null && _a !== void 0 ? _a : "1804480915"; + } + /// A collectionID of arcade game recommendations for multiplayer activity + get multiplayerActivityGameRecommendationsArcade() { + var _a; + return (_a = this.implementation.string("multiplayer-activity-arcade-game-recommendations")) !== null && _a !== void 0 ? _a : "1821553042"; + } + /// A collectionID of non-arcade game recommendations for multiplayer activity + get multiplayerActivityGameRecommendationsNonArcade() { + var _a; + return (_a = this.implementation.string("multiplayer-activity-non-arcade-game-recommendations")) !== null && _a !== void 0 ? _a : "1821553152"; + } + // Whether to show the Subscriber Access Arcade Badge + get showArcadeSubscriberAccessBadge() { + var _a; + return (_a = this.implementation.boolean("show-arcade-subscriber-access-badge")) !== null && _a !== void 0 ? _a : false; + } + // Whether to show the Non-Subscriber Arcade Badge + get showArcadeNonSubscriberBadge() { + var _a; + return (_a = this.implementation.boolean("show-arcade-non-subscriber-badge")) !== null && _a !== void 0 ? _a : false; + } + /// The URL for the learn more link on the Cross Use Consent screen. + get gamesCrossUseConsentLearnMoreURL() { + return this.implementation.string("games-crossuse-consent-learn-more-url"); + } + /// The duration for the auto advance interval for the Play Now feed hero carousel + get gamesPlayNowHeroCarouselAutoAdvanceInterval() { + var _a; + return (_a = this.implementation.double("games-play-now-hero-carousel-auto-advance-interval")) !== null && _a !== void 0 ? _a : 10.0; + } + /// The max duration for the auto advance interval for the Play Now feed hero carousel + get gamesPlayNowHeroCarouselAutoAdvanceMaxInterval() { + var _a; + return (_a = this.implementation.double("games-play-now-hero-carousel-auto-advance-max-interval")) !== null && _a !== void 0 ? _a : 30.0; + } + /// The duration for the auto advance interval for the Editorial hero carousel + get gamesEditorialHeroCarouselAutoAdvanceInterval() { + var _a; + return (_a = this.implementation.double("games-editorial-hero-carousel-auto-advance-interval")) !== null && _a !== void 0 ? _a : 10.0; + } + /// The max duration for the auto advance interval for the Editorial hero carousel + get gamesEditorialHeroCarouselAutoAdvanceMaxInterval() { + var _a; + return (_a = this.implementation.double("games-editorial-hero-carousel-auto-advance-max-interval")) !== null && _a !== void 0 ? _a : 30.0; + } + get enablePreviewPlatformForWeb() { + var _a; + return (_a = this.implementation.boolean("enable-preview-platform-for-web")) !== null && _a !== void 0 ? _a : false; + } + // How long to look back for completed challenges to show in active challenges shelf + get completedChallengesInActiveShelfTimeThreshold() { + var _a; + return (_a = this.implementation.integer("completed-challenges-in-active-shelf-time-threshold")) !== null && _a !== void 0 ? _a : 86400; // default to 1 day + } + // Whether we should request for a review when we have a chance. + get requestReviewEnabled() { + var _a; + return (_a = this.implementation.boolean("request-review-enabled")) !== null && _a !== void 0 ? _a : true; + } + // The minimum number of app launches before we consider requesting for a review. + get requestReviewMinAppLaunchCount() { + var _a; + return (_a = this.implementation.integer("request-review-min-app-launch-count")) !== null && _a !== void 0 ? _a : 15; + } + // The minimum number of game launches from the app before we consider requesting a review. + get requestReviewMinGameLaunchCount() { + var _a; + return (_a = this.implementation.integer("request-review-min-game-launch-count")) !== null && _a !== void 0 ? _a : 5; + } + // Whether we should request licenses in product information + get enableLicenses() { + var _a; + return (_a = this.implementation.boolean("enable-licenses")) !== null && _a !== void 0 ? _a : false; + } + // The number of apps to display in the chart detail screen. + get chartDetailPageItemCount() { + var _a; + return (_a = this.implementation.integer("chart-detail-page-item-count")) !== null && _a !== void 0 ? _a : 25; + } + /// https://quip-apple.com/G5EGABrSh1YO#temp:C:UdDa344becf5aac473db8085f35a + /// URL query parameters that are permitted to be included in the pageUrl field of page metrics events. + get metricsAllowedListURLParams() { + let params = serverData.asArrayOrEmpty(serverData.asJSONValue(this.implementation.array("metrics-allowed-list-url-params"))); + if (serverData.isNullOrEmpty(params)) { + params = [ + "itsct", + "itscg", + "itcCt", + "ct", + "pt", + "advp", + "mttn3pid", + "mttnagencyid", + "mttncc", + "mttnpid", + "mttnsiteid", + "mttnsub1", + "mttnsub2", + "mttnsubad", + "mttnsubkw", + "mttnsubplmnt", + "id", + "term", + "salableAdamId", + "ign-itscg", + "ign-itsct", + "utm_campaign", + "clusterId", + ]; + } + return params; + } +} +BagWrapper.type = makeMetatype("app-store:bag-wrapper"); +//# sourceMappingURL=bag.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/cached-bag.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/cached-bag.js new file mode 100644 index 0000000..e7ac2db --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/cached-bag.js @@ -0,0 +1,82 @@ +import { isSome } from "@jet/environment"; +import { Wrapper } from "./wrapper"; +/** + * A bag wrapper that implements caching. All accessed values are cached until this object is reinitialized via rebootstrap or recreated. + * + */ +export class CachedBag extends Wrapper { + constructor() { + super(...arguments); + /** + * A cache of entries + * + * Note that `Opt<T>` values which are nil are stored, representing that the bag has no value for a key, which + * is coalesced in the JS. This prevents infinite cache misses when the bag has no value for a key, resulting in + * unnecessary calls to the native client + */ + this.cache = {}; + } + registerBagKeys(keys) { + this.implementation.registerBagKeys(keys); + } + string(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.string(key); + }); + } + double(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.double(key); + }); + } + integer(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.integer(key); + }); + } + boolean(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.boolean(key); + }); + } + array(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.array(key); + }); + } + dictionary(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.dictionary(key); + }); + } + url(key, forceDisableCache = false) { + return this.fromCache(key, forceDisableCache, () => { + return this.implementation.url(key); + }); + } + /** + * Returns a cached value if possible to do so, otherwise uses the closure to fetch a new value which is cached and returned + * + * @param key The key on which to cache + * @param forceDisableCache Disables the cache to ensure a fresh value from the bag + * @param fetchValue A closure to fetch new values with + * @returns A previously or newly cached value + */ + fromCache(key, forceDisableCache = false, fetchValue) { + if (forceDisableCache) { + return fetchValue(); + } + else { + const cacheEntry = this.cache[key]; + if (isSome(cacheEntry)) { + return cacheEntry.value; + } + else { + const newValue = fetchValue(); + this.cache[key] = { value: newValue }; + return newValue; + } + } + } +} +//# sourceMappingURL=cached-bag.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client-ordering.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client-ordering.js new file mode 100644 index 0000000..683d81c --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client-ordering.js @@ -0,0 +1,43 @@ +/** + * Created by dersu on 6/23/17. + */ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { Wrapper } from "./wrapper"; +export class ClientOrderingWrapper extends Wrapper { + async orderedVisibleIAPs(appBundleId, defaultOrdering, defaultVisibleIdentifiers, spotlightIdentifier) { + return await new Promise((resolve, reject) => { + // Collections that are parameters over the bridge cannot have `nil` constituents on the other side, so we + // should make sure we don't give such inputs or else we'll crash the app. + const cleanedDefaultOrdering = defaultOrdering.filter((item) => { + return item !== null && item !== undefined; + }); + const cleanedDefaultVisibleIdentifiers = defaultVisibleIdentifiers.filter((item) => { + return item !== null && item !== undefined; + }); + this.implementation.orderedVisibleIAPs(appBundleId, cleanedDefaultOrdering, cleanedDefaultVisibleIdentifiers, spotlightIdentifier, (ordering, error) => { + if (error) { + reject(error); + } + else { + resolve(ordering); + } + }); + }); + } + async visibilityForIAPs(productMap) { + return await new Promise((resolve, reject) => { + this.implementation.visibilityForIAPs(productMap, (visibilities, error) => { + if (error) { + // Do not reject; we should gracefully fail, since this ordering + // data isn't strictly necessary to render search results. + resolve({}); + } + else { + resolve(visibilities); + } + }); + }); + } +} +ClientOrderingWrapper.type = makeMetatype("app-store:client-ordering-wrapper"); +//# sourceMappingURL=client-ordering.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client.js new file mode 100644 index 0000000..5a53dac --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client.js @@ -0,0 +1,204 @@ +/** + * Created by Pete Hare on 2/7/17. + */ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { Size } from "../../api/models/base"; +import * as serverData from "../json-parsing/server-data"; +import { Wrapper } from "./wrapper"; +// region API +/** + * Client identifier for the App Store. + */ +export const appStoreIdentifier = "com.apple.AppStore" /* ClientIdentifier.AppStore */; +/** + * Client identifier for the Bridge (Watch) store. + */ +export const watchIdentifier = "com.apple.AppStore.BridgeStoreExtension" /* ClientIdentifier.AppStore_BridgeStoreExtension */; +/** + * Client identifier for the iMessages store. + */ +export const messagesIdentifier = "com.apple.MobileSMS" /* ClientIdentifier.MobileSMS */; +/** + * Client identifier for the arcade app. + */ +export const arcadeIdentifier = "com.apple.Arcade" /* ClientIdentifier.Arcade */; +/** + * Client identifier indicating ATV. + */ +export const tvIdentifier = "com.apple.TVAppStore" /* ClientIdentifier.TVAppStore */; +/** + * Client identifier for the Arcade launch repair SubscribePageExtension (iOS). + */ +export const productPageExtensionIdentifier = "com.apple.AppStore.ProductPageExtension" /* ClientIdentifier.AppStore_ProductPageExtension */; +/** + * Client identifier for the Arcade launch repair SubscribePageExtension (iOS). + */ +export const subscribePageExtensionIdentifier = "com.apple.AppStore.SubscribePageExtension" /* ClientIdentifier.AppStore_SubscribePageExtension */; +export class ClientWrapper extends Wrapper { + get buildType() { + return this.implementation.buildType; + } + // This is only supported in Luckier client builds from AppStoreJet 11.1.7 & GamesUI 2.1.5 onwards. Any previous + // clients will return undefined. + get buildVersion() { + return this.implementation.buildVersion; + } + get deviceType() { + return this.implementation.deviceType; + } + get guid() { + return this.implementation.guid; + } + get isActivityAvailable() { + return this.implementation.isActivityAvailable; + } + get isElectrocardiogramInstallationAllowed() { + return this.implementation.isElectrocardiogramInstallationAllowed; + } + get isScandiumInstallationAllowed() { + return this.implementation.isScandiumInstallationAllowed; + } + get isSidepackingEnabled() { + return this.implementation.isSidepackingEnabled; + } + get isTinkerWatch() { + return this.implementation.isTinkerWatch; + } + get screenCornerRadius() { + return this.implementation.screenCornerRadius; + } + get screenSize() { + return Size.fromNativeSize(this.implementation.screenSize); + } + get storefrontIdentifier() { + return this.implementation.storefrontIdentifier; + } + get supportsHEIF() { + return this.implementation.supportsHEIF; + } + get thinnedApplicationVariantIdentifier() { + return this.implementation.thinnedApplicationVariantIdentifier; + } + get isMandrakeSupported() { + return this.implementation.isMandrakeSupported; + } + get isCharonSupported() { + return this.implementation.isCharonSupported; + } + get isIconArtworkCapable() { + return this.implementation.isIconArtworkCapable; + } + get maxAppContentRating() { + return this.implementation.maxAppContentRating; + } + get hostBundleId() { + return this.implementation.hostBundleId; + } + isPairedSystemVersionAtLeast(version) { + var _a, _b, _c; + return (_c = (_b = (_a = this.implementation).isPairedSystemVersionAtLeast) === null || _b === void 0 ? void 0 : _b.call(_a, version)) !== null && _c !== void 0 ? _c : false; + } + deletableSystemAppCanBeInstalledOnWatchWithBundleID(bundleId) { + return this.implementation.deletableSystemAppCanBeInstalledOnWatchWithBundleID(bundleId); + } + deviceHasCapabilities(capabilities) { + return this.implementation.deviceHasCapabilities(capabilities); + } + deviceHasCapabilitiesIncludingCompatibilityCheckIsVisionOSCompatibleIOSApp(capabilities, supportsVisionOSCompatibleIOSBinary) { + if (this.isPad && capabilities.includes("healthkit")) { + // Workaround for: rdar://116905381 (J517/21C16: Unable to download AC Wellness even though App Store page says its compatible with my iPad) + return false; + } + return this.implementation.deviceHasCapabilitiesIncludingCompatibilityCheckIsVisionOSCompatibleIOSApp(capabilities, supportsVisionOSCompatibleIOSBinary); + } + isActivePairedWatchSystemVersionAtLeastMajorVersionMinorVersionPatchVersion(majorVersion, minorVersion, patchVersion) { + return this.implementation.isActivePairedWatchSystemVersionAtLeastMajorVersionMinorVersionPatchVersion(majorVersion, minorVersion, patchVersion); + } + canDevicePerformAppActionWithAppCapabilities(appAction, appCapabilities) { + return this.implementation.canDevicePerformAppActionWithAppCapabilities(appAction, appCapabilities); + } + isAutomaticDownloadingEnabled() { + return this.implementation.isAutomaticDownloadingEnabled(); + } + isAuthorizedForUserNotifications() { + return this.implementation.isAuthorizedForUserNotifications(); + } + /** + * Check whether the active paired device's OS is below a given version. + */ + isActivePairedWatchSystemVersionBelow(version) { + // We want to use isActivePairedWatchSystemVersionAtLeastMajorVersionMinorVersionPatchVersion + // rather than isPairedSystemVersionAtLeast here, as the latter is only available from YukonB onwards + const versionComponents = version.split("."); + const majorVersion = serverData.asNumber(versionComponents[0]) || 0; + const minorVersion = serverData.asNumber(versionComponents[1]) || 0; + const patchVersion = serverData.asNumber(versionComponents[2]) || 0; + return !this.implementation.isActivePairedWatchSystemVersionAtLeastMajorVersionMinorVersionPatchVersion(majorVersion, minorVersion, patchVersion); + } + /** Returns `true` for phone-factor iOS devices. */ + get isPhone() { + return this.deviceType === "phone"; + } + /** Returns `true` for pad-factor iOS devices. */ + get isPad() { + return this.deviceType === "pad"; + } + /** + * Returns `true` for iOS devices. + * + * Note: this property might be replaced with a build-time macro in "production" + */ + get isiOS() { + return this.isPhone || this.isPad; + } + /** + * Returns `true` for Mac devices + + * Note: this property might be replaced with a build-time macro in "production" + */ + get isMac() { + return this.deviceType === "mac"; + } + /** + * Returns `true` for TV devices. + + * Note: this property might be replaced with a build-time macro in "production" + */ + get isTV() { + return this.deviceType === "tv"; + } + /** + * Returns `true` for Watch devices. + + * Note: this property might be replaced with a build-time macro in "production" + */ + get isWatch() { + return this.deviceType === "watch"; + } + /** + * Returns `true` for Vision devices. + * + * Note: this property might be replaced with a build-time macro in "production" + */ + get isVision() { + return this.deviceType === "vision"; + } + /** + * Returns `true` for the Web + * + * Note: this property might be replaced with a build-time macro in "production" + */ + get isWeb() { + return this.deviceType === "web"; + } + get isCompanionVisionApp() { + return this.hostBundleId === "com.apple.visionproapp"; + } + /** Returns the list of remote identifiers to download any purchases to. */ + get remoteDownloadIdentifiers() { + return this.implementation.remoteDownloadIdentifiers; + } +} +ClientWrapper.type = makeMetatype("app-store:client-wrapper"); +// endregion +//# sourceMappingURL=client.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/console.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/console.js new file mode 100644 index 0000000..1bbfac0 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/console.js @@ -0,0 +1,18 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { Wrapper } from "./wrapper"; +export class ConsoleWrapper extends Wrapper { + info(...args) { + return this.implementation.info(...args); + } + error(...args) { + return this.implementation.error(...args); + } + log(...args) { + return this.implementation.log(...args); + } + warn(...args) { + return this.implementation.warn(...args); + } +} +ConsoleWrapper.type = makeMetatype("app-store:console-wrapper"); +//# sourceMappingURL=console.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/host.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/host.js new file mode 100644 index 0000000..001a1e3 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/host.js @@ -0,0 +1,64 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { Wrapper } from "./wrapper"; +export class HostWrapper extends Wrapper { + get clientIdentifier() { + return this.implementation.clientIdentifier; + } + get clientVersion() { + return this.implementation.clientVersion; + } + get deviceLocalizedModel() { + return this.implementation.deviceLocalizedModel; + } + get deviceModel() { + return this.implementation.deviceModel; + } + get deviceModelFamily() { + return this.implementation.deviceModelFamily; + } + get devicePhysicalModel() { + return this.implementation.devicePhysicalModel; + } + get deviceMarketingFamilyName() { + return this.implementation.deviceMarketingFamilyName; + } + get osBuild() { + return this.implementation.osBuild; + } + get platform() { + return this.implementation.platform; + } + isOSAtLeast(majorVersion, minorVersion, patchVersion) { + return this.implementation.isOSAtLeast(majorVersion, minorVersion, patchVersion); + } + /** Returns `true` for iOS host. */ + get isiOS() { + return this.platform === "iOS"; + } + /** Returns `true` for macOS host. */ + get isMac() { + return this.platform === "macOS"; + } + /** Returns `true` for tvOS host. */ + get isTV() { + return this.platform === "tvOS"; + } + /** Returns `true` for watchOS host. */ + get isWatch() { + return this.platform === "watchOS"; + } + /** Returns `true` for web host. */ + get isWeb() { + return this.platform === "web"; + } + /** Returns `true` for Windows host. */ + get isWindows() { + return this.platform === "Windows"; + } + /** Returns `true` for visionOS host. */ + get isVision() { + return this.platform === "xrOS"; + } +} +HostWrapper.type = makeMetatype("app-store:host-wrapper"); +//# sourceMappingURL=host.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js new file mode 100644 index 0000000..d7e0ee8 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js @@ -0,0 +1,437 @@ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { AmpLocalization } from "../amp-localization/amp-localization"; +import { isNullOrEmpty } from "../json-parsing/server-data"; +import { Wrapper } from "./wrapper"; +import { isSome } from "@jet/environment/types/optional"; +import * as validation from "@jet/environment/json/validation"; +/** + * Wrapper around an object or `Localization` type. + * The wrapped object is implemented as part of a native app. + */ +export class LocalizationWrapper extends Wrapper { + constructor(loc, objectGraph) { + super(loc); + /** + * Path to localization file. + * The path is set when the localization file is loaded + * for the first time. + */ + this.locFile = null; + /** + * Instance of `AMPLocalization` providing server-side + * values for localized stirngs. + */ + this.ampLoc = new AmpLocalization(); + /** + * Localization strings cache. + * Cache and reuse values returned by + * wrapped `Localization` implementation. + */ + this.LOC_STRING_CACHE = {}; + this.objectGraph = objectGraph; + } + /** + * Returns the wrapped localization's identifier. + */ + get identifier() { + return this.implementation.identifier; + } + /** + * Returns the wrapped localization's safe identifier. + */ + get safeIdentifier() { + return this.implementation.identifier.split("_")[0]; + } + // endregion + // region Localization + /** + * Localizes a string replacing placehoders in key with values in the parameters dictionary + * @param key The loc key to look up + * @param params Parameters to replace in the loc string + * @return The localized string + */ + string(key, defaultValue) { + return this.implementation.string(key); + } + /** + * Localizes a string, and logs & throws an error if the key is a screamer. + * @param key The loc key to look up + * @return The localized string + */ + tryString(key) { + const value = this.implementation.string(key); + if (value === key || value === `**${key}**`) { + validation.context("tryString", () => { + validation.unexpectedType("coercedValue", "Localization key", key, null); + }); + throw new Error(`No value exists for localization key '${key}'`); + } + return value; + } + /** + * Localizes a string, and logs an error if the key is a screamer. + * @param key The loc key to look up + * @param fallback The fallback value for unlocalized keys, e.g. English value + * @return The localized string + */ + stringWithFallback(key, fallback) { + const value = this.implementation.string(key); + return value === `**AppStore.${key}**` ? fallback : value; + } + /** + * Localizes a string using a preferred locale. + * + * The implementation of this function is similar to `string(key:)` above, but with the option + * to prefer a particular locale. If one is provided, we augment the lookup key in the cache + * with the `locale` value. If one is not provided, we fallback to `string(key:)`. This makes it + * much easier to use this function directly, in place of `string(key:)` where necessary. + + * @param objectGraph The object graph, used to forward this call on if the required native function is unavailable. + * Can be removed for 2023. + * @param key The loc key to look up. + * @param locale The preferred locale to use for look up. Falls back to default, if unavailable. + * @param defaultValue A default value to use if nothing is found. + */ + stringForPreferredLocale(objectGraph, key, locale, defaultValue) { + if (isNullOrEmpty(locale)) { + return this.string(key, defaultValue); + } + const cacheKey = `${key}_${locale}`; + let value = this.LOC_STRING_CACHE[cacheKey]; + if (!value) { + value = this.implementation.stringForPreferredLocale(key, locale); + if (value && value !== key) { + this.LOC_STRING_CACHE[cacheKey] = value; + } + else { + const serverValue = this.ampLoc.localize(key); + if (serverValue !== key) { + value = serverValue; + } + else if (defaultValue) { + value = defaultValue; + } + else { + value = key; + } + } + } + return value; + } + /** + * Localize with appropriate plural form based on the count. + * + * Some languages have more plural forms than others. + * The full set of categories is "zero", "one", "two", "few", "many", and "other". + * + * The base loc key is used for "other" (the default). + * Otherwise the category is appended to the base loc key with a "." separator. + * + * http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html + * http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories + * http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-numbers.html#Language_Plural_Rules + * + * English loc keys + * key = "@@count@@ dogs"; // default, aka "plural" + * key.zero = "no dogs"; // used when count is 0 + * key.one = "@@count@@ dog"; // used when count is 1, aka "singular" + * + * @param key base loc key + * @param count number used to determine the plural form + * @param params (optional) substitution keys and values, "count" will be added if it's not already present + * @return localized string + */ + stringWithCount(key, count, params) { + let value = this.implementation.stringWithCount(key, count); + if (!value || value === key) { + const serverValue = this.ampLoc.localizeWithCount(this.objectGraph, key, count, params); + if (serverValue) { + value = serverValue; + } + } + return value; + } + /** + * A variation of `stringWithCount` that supports multiple plural forms. + * @param key base loc key + * @param counts numbers used to determine the plural forms + * @param params (optional) substitution keys and values, "count" will be added if it's not already present + * @return localized string + */ + stringWithCounts(key, counts, params) { + return this.implementation.stringWithCounts(key, counts); + } + /** + * Converts a string into its uppercased form. + * @param value The string to apply the uppercase to. + * @returns {string} The loacle-specific, uppercased form of the string. If for whatever reason the upper-casing cannot + * be applied, this function simply returns the input value. + */ + uppercased(value) { + if (!value) { + return null; + } + return value.toLocaleUpperCase(this.safeIdentifier); + } + /** + * Converts a number into a localized string + * + * @param n The number to convert + * @param decimalPlaces The number of decimal places to include. + * @return {string} The localized version of the number + */ + decimal(n, decimalPlaces) { + let value = this.implementation.decimal(n, decimalPlaces); + if (!value) { + if (typeof n === "number") { + value = `* ${n.toString()} *`; + } + else { + value = this.nullString(); + } + } + return value; + } + /** + * Converts a number of bytes into a localized file size string + * + * @param bytes The number of bytes to convert + * @return The localized file size string + */ + fileSize(bytes) { + let value = this.implementation.fileSize(bytes); + if (!value) { + value = this.nullString(); + } + return value; + } + /** + * Converts a number of bytes into a localized file size string + * + * @param count The number of bytes to convert + * @return The localized file size string + */ + formattedCount(count) { + let value = this.implementation.formattedCount(count); + if (!value) { + value = this.nullString(); + } + return value; + } + /** + * Converts a number into a formatted string representation using the preferred locale identifier. + * + * @param count The number to format. + * @param locale The locale identifier to prefer for lookup. + * @return The localized string. + */ + formattedCountForPreferredLocale(objectGraph, count, locale) { + if (isNullOrEmpty(locale)) { + return this.formattedCount(count); + } + let value = this.implementation.formattedCountForPreferredLocale(count, locale); + if (!value) { + value = this.nullString(); + } + return value; + } + /** + * Converts a date into a time ago label, showing how long ago + * the date occurred + * + * @param date The date object to convert + * @param context The context in which the date should be formatted + * @return The localized string representing the amount of time that has passed + */ + timeAgoWithContext(date, context) { + let value = this.implementation.timeAgoWithContext(date, context); + if (!value) { + value = this.nullString(); + } + return value; + } + /** + * Converts a date into a localized date string using the provided format + * + * @param format The format string describing how the date should be formatted + * @param date The date object to convert + * @return The localized string representing the date + */ + formatDate(format, date) { + let value = this.implementation.formatDate(format, date); + if (!value) { + value = this.nullString(); + } + return value; + } + formatDateWithContext(format, date, context) { + let value = this.implementation.formatDateWithContext(format, date, context); + if (!value) { + value = this.nullString(); + } + return value; + } + formatDateInSentence(sentence, format, date) { + let value = this.implementation.formatDateInSentence(sentence, format, date); + if (!value) { + value = this.nullString(); + } + return value; + } + /** + * Converts a date into a relative date, showing how long ago + * the date occurred + * + * @param date The date object to convert + * @return The localized string representing the amount of time that has passed + */ + relativeDate(date) { + let value = this.implementation.relativeDate(date); + if (!value) { + value = this.nullString(); + } + return value; + } + formatDuration(value, unit) { + let result = this.implementation.formatDuration(value, unit); + if (!result) { + result = this.nullString(); + } + return result; + } + // endregion + // region Private Methods + /** + * Applies a new localization file and localization dictionary as the new set of localizations + * + * @param file The file name of the file that is loaded + * @param localizations The localizations dictionary to use + * @param locale Current normalized locale which includes language code + */ + applyLocalizations(file, localizations, locale) { + if (this.isLocFileLoaded(file)) { + return; + } + this.locFile = file; + // The first 2 characters of normalized locale are the language code. + this.ampLoc.updateLocalizationData(localizations, locale.slice(0, 2)); + } + /** + * Checks if the named loc file is already loaded + * @param file The file name to check + * @return {boolean} True or false depending on whether the file name is loaded + */ + isLocFileLoaded(file) { + return this.locFile === file; + } + /** + * Normalizes the given bag language into a locale. + * @param language The language to normalize. + * @param storefrontIdentifier The storefront identifier to use. + * @return A locale to use. + */ + normalizedLocale(objectGraph, language, storefrontIdentifier) { + language = language.toLowerCase(); + switch (language) { + case "yue-hant": { + // Country code from the bag is not available in JS, so we use the storefront. + const macauStoreFrontIdentifier = objectGraph.props.asString("macauStorefrontIdentifier"); + if (typeof storefrontIdentifier === "string" && + typeof macauStoreFrontIdentifier === "string" && + storefrontIdentifier.indexOf(macauStoreFrontIdentifier) !== -1) { + return "zh-ma"; + } + else { + return "zh-hk"; + } + } + default: + return language; + } + } + nullString() { + return "* null *"; + } + // endregion + // region API + /** + * Loads the localizations from the JetPack. + */ + load(objectGraph) { + // Sanity check + if (objectGraph.bag.language === undefined || objectGraph.bag.language === null) { + throw new Error("Bag language is not available. Unable to load localizations."); + } + const locale = this.normalizedLocale(objectGraph, objectGraph.bag.language, objectGraph.client.storefrontIdentifier); + const locName = `local/${locale}`; + // Load localizations if needed + if (!this.isLocFileLoaded(locName)) { + const localizations = objectGraph.props.asDictionary(`localizations.${locale}`); + if (localizations !== undefined && localizations !== null) { + this.applyLocalizations(locName, localizations, locale); + } + else { + // Fallback to english + const fallbackLocalizations = objectGraph.props.asDictionary(`localizations.en-us`); + if (fallbackLocalizations !== undefined && fallbackLocalizations !== null) { + this.applyLocalizations(locName, fallbackLocalizations, locale); + } + } + } + } + /** + * Returns the localized name for the provided device type. + * The company policy seems to be to always use the branded, + * non-localized text, but this is just-in-case. + * @returns {string} The display name for the device. + */ + deviceDisplayName(objectGraph) { + if (objectGraph.client.isVision && isSome(objectGraph.host.deviceMarketingFamilyName)) { + return objectGraph.host.deviceMarketingFamilyName; + } + if (objectGraph.host.deviceLocalizedModel) { + return objectGraph.host.deviceLocalizedModel; + } + // TODO: <rdar://problem/33575833> Jet: Remove brand loc fallbacks + switch (objectGraph.client.deviceType) { + case "phone": + const localizedPhoneName = this.string("IPHONE_BRAND_NAME"); + if (localizedPhoneName === "IPHONE_BRAND_NAME") { + return "iPhone"; + } + return localizedPhoneName; + case "pad": + const localizedPadName = this.string("IPAD_BRAND_NAME"); + if (localizedPadName === "IPAD_BRAND_NAME") { + return "iPad"; + } + return localizedPadName; + case "tv": + const localizedTvName = this.string("APPLE_TV_BRAND_NAME"); + if (localizedTvName === "APPLE_TV_BRAND_NAME") { + return "Apple\u00a0TV"; + } + return localizedTvName; + case "watch": + const localizedWatchName = this.string("APPLE_WATCH_BRAND_NAME"); + if (localizedWatchName === "APPLE_WATCH_BRAND_NAME") { + return "Apple\u00a0Watch"; + } + return localizedWatchName; + case "mac": + const localizedMacName = this.string("MAC_BRAND_NAME"); + if (localizedMacName === "MAC_BRAND_NAME") { + return "Mac"; + } + return localizedMacName; + default: + return null; + } + } +} +// region Properties +/** + * Localization wrapper metatype to use with object graph. + */ +LocalizationWrapper.type = makeMetatype("app-store:loc-wrapper"); +//# sourceMappingURL=localization.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/properties.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/properties.js new file mode 100644 index 0000000..fa8b4e1 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/properties.js @@ -0,0 +1,74 @@ +/** + * Created by keithpk on 4/2/17. + */ +import { makeMetatype } from "@jet/environment/util/metatype"; +import * as serverData from "../json-parsing/server-data"; +import { Wrapper } from "./wrapper"; +export class PropertiesWrapper extends Wrapper { + /** + * Returns the raw underlying value for the key path + * @param path The path of the property to lookup + * @return {JSONValue | null} The resulting value or null + */ + value(path) { + return serverData.traverse(this.implementation, path); + } + /** + * Returns whether a property is enabled or not. This also + * looks in the clientFeatures key as well for client-enabled + * features + * + * @param key The key to look up + * @return {boolean} The boolean result or false if no value was found for the key + */ + enabled(key) { + const propertyValue = this.value(key); + if (typeof propertyValue !== "undefined") { + return Boolean(propertyValue); + } + return Boolean(this.implementation.clientFeatures[key]); + } + /** + * Syntactic sugar for !enabled(key). + * + * @param key The key to look up + * @return {boolean} The boolean result or true if no value was found for the key + */ + isNotEnabled(key) { + return !this.enabled(key); + } + /** + * Returns the value of the path coerced as a dictionary + * @param path The path to look up + * @return {JSONData|null} The dictionary or null if not found + */ + asDictionary(path) { + return serverData.asDictionary(this.implementation, path); + } + /** + * Returns the value of the path coerced as a string + * @param path The path to look up + * @return {string|null} The string or null if not found + */ + asString(path) { + return serverData.asString(this.implementation, path); + } + /** + * Returns the value of the path coerced as a number + * @param path The path to look up + * @return {string|null} The number or null if not found + */ + asNumber(path) { + return serverData.asNumber(this.implementation, path); + } + /** + * Returns the value of the path coerced as an array + * @param path The path to look up + * @return {string|null} The array or empty if not found + */ + asArray(path) { + return serverData.asArrayOrEmpty(this.implementation, path); + } +} +PropertiesWrapper.type = makeMetatype("app-store:props-wrapper"); +//# sourceMappingURL=properties.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/storage.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/storage.js new file mode 100644 index 0000000..2643e0d --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/storage.js @@ -0,0 +1,22 @@ +/** + * Creates and returns a mock Storage object. + * @return A new mock Storage object. + */ +import { makeMetatype } from "@jet/environment/util/metatype"; +import { Wrapper } from "./wrapper"; +export class StorageWrapper extends Wrapper { + retrieveString(key) { + const value = this.implementation.retrieveString(key); + if ((value === null || value === void 0 ? void 0 : value.length) > 0 && value !== "<null>") { + return value; + } + else { + return null; + } + } + storeString(key, value) { + this.implementation.storeString(value, key); // flip is deliberate. + } +} +StorageWrapper.type = makeMetatype("app-store:storage-wrapper"); +//# sourceMappingURL=storage.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/wrapper.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/wrapper.js new file mode 100644 index 0000000..f03e835 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/wrapper.js @@ -0,0 +1,6 @@ +export class Wrapper { + constructor(implementation) { + this.implementation = implementation; + } +} +//# sourceMappingURL=wrapper.js.map
\ No newline at end of file |
