summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/foundation
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/foundation')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/amp-localization/amp-localization.js371
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/active-intent.js121
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale-from-bag.js41
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/locale/locale.js3
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/dependencies/seo.js3
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/app-store-experiments.js7
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-area-id.js10
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/experiment-cache.js63
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/product-page-experiments.js21
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/search-results-experiments.js10
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/experimentation/today-ad-experiments.js15
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/derived-data.js37
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/json-parsing/server-data.js464
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/associations.js17
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/attributes.js149
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/data-fetching.js631
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/data-structure.js84
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/network.js304
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/platform-attributes.js143
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/relationships.js43
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/url-builder.js381
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/media/util.js185
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/metrics/buy-parameters.js112
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/metrics/cookies.js27
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/metrics/metrics-identifiers-cache.js245
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/network/http.js42
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/network/network.js123
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/network/url-constants.js452
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/network/urls.js382
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/routing/routing-components.js318
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/runtime/action-provider.js13
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-intent-dispatcher.js62
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/runtime/app-store-object-graph.js380
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/runtime/runtime.js52
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/array-util.js32
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/color-util.js231
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/constants.js26
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/date-util.js127
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/errors.js13
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/math-util.js32
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/objects.js33
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/promise-util.js215
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/string-util.js127
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/util/validation-util.js30
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/apple-silicon.js13
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/bag.js1126
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/cached-bag.js82
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client-ordering.js43
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/client.js204
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/console.js18
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/host.js64
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js437
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/properties.js74
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/storage.js22
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/wrapper.js6
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: "&nbsp;", gt: "&gt;", lt: "&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