init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

1
api/csp-report.html Normal file
View File

@@ -0,0 +1 @@
No Content: https://apps.apple.com/api/csp-report

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,157 @@
!(function (e, t) {
'object' == typeof exports && 'undefined' != typeof module
? t()
: 'function' == typeof define && define.amd
? define(t)
: t();
})(0, function () {
'use strict';
function e(e) {
var t = !0,
n = !1,
o = null,
d = {
text: !0,
search: !0,
url: !0,
tel: !0,
email: !0,
password: !0,
number: !0,
date: !0,
month: !0,
week: !0,
time: !0,
datetime: !0,
'datetime-local': !0,
};
function i(e) {
return !!(
e &&
e !== document &&
'HTML' !== e.nodeName &&
'BODY' !== e.nodeName &&
'classList' in e &&
'contains' in e.classList
);
}
function s(e) {
e.classList.contains('focus-visible') ||
(e.classList.add('focus-visible'),
e.setAttribute('data-focus-visible-added', ''));
}
function u(e) {
t = !1;
}
function a() {
document.addEventListener('mousemove', c),
document.addEventListener('mousedown', c),
document.addEventListener('mouseup', c),
document.addEventListener('pointermove', c),
document.addEventListener('pointerdown', c),
document.addEventListener('pointerup', c),
document.addEventListener('touchmove', c),
document.addEventListener('touchstart', c),
document.addEventListener('touchend', c);
}
function c(e) {
(e.target.nodeName && 'html' === e.target.nodeName.toLowerCase()) ||
((t = !1),
document.removeEventListener('mousemove', c),
document.removeEventListener('mousedown', c),
document.removeEventListener('mouseup', c),
document.removeEventListener('pointermove', c),
document.removeEventListener('pointerdown', c),
document.removeEventListener('pointerup', c),
document.removeEventListener('touchmove', c),
document.removeEventListener('touchstart', c),
document.removeEventListener('touchend', c));
}
document.addEventListener(
'keydown',
function (n) {
n.metaKey ||
n.altKey ||
n.ctrlKey ||
(i(e.activeElement) && s(e.activeElement), (t = !0));
},
!0,
),
document.addEventListener('mousedown', u, !0),
document.addEventListener('pointerdown', u, !0),
document.addEventListener('touchstart', u, !0),
document.addEventListener(
'visibilitychange',
function (e) {
'hidden' === document.visibilityState &&
(n && (t = !0), a());
},
!0,
),
a(),
e.addEventListener(
'focus',
function (e) {
var n, o, u;
i(e.target) &&
(t ||
((n = e.target),
(o = n.type),
('INPUT' === (u = n.tagName) &&
d[o] &&
!n.readOnly) ||
('TEXTAREA' === u && !n.readOnly) ||
n.isContentEditable)) &&
s(e.target);
},
!0,
),
e.addEventListener(
'blur',
function (e) {
var t;
i(e.target) &&
(e.target.classList.contains('focus-visible') ||
e.target.hasAttribute(
'data-focus-visible-added',
)) &&
((n = !0),
window.clearTimeout(o),
(o = window.setTimeout(function () {
n = !1;
}, 100)),
(t = e.target).hasAttribute(
'data-focus-visible-added',
) &&
(t.classList.remove('focus-visible'),
t.removeAttribute('data-focus-visible-added')));
},
!0,
),
e.nodeType === Node.DOCUMENT_FRAGMENT_NODE && e.host
? e.host.setAttribute('data-js-focus-visible', '')
: e.nodeType === Node.DOCUMENT_NODE &&
(document.documentElement.classList.add('js-focus-visible'),
document.documentElement.setAttribute(
'data-js-focus-visible',
'',
));
}
if ('undefined' != typeof window && 'undefined' != typeof document) {
var t;
window.applyFocusVisiblePolyfill = e;
try {
t = new CustomEvent('focus-visible-polyfill-ready');
} catch (e) {
(t = document.createEvent('CustomEvent')).initCustomEvent(
'focus-visible-polyfill-ready',
!1,
!1,
{},
);
}
window.dispatchEvent(t);
}
'undefined' != typeof document && e(document);
});
//# sourceMappingURL=focus-visible.min.js.map

View File

@@ -0,0 +1 @@
No Content: https://apps.apple.com/assets/fonts/locale-switcher/ArabicUIText-Regular-subset.woff2

View File

@@ -0,0 +1 @@
<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m7 0c3.8659932 0 7 3.13400675 7 7 0 3.8659932-3.1340068 7-7 7-3.86599325 0-7-3.1340068-7-7 0-3.86599325 3.13400675-7 7-7zm3.2615892 3.73841079c-.31788105-.31788105-.83326807-.31788105-1.15114912 0l-2.11044008 2.11058921-2.11044008-2.11058921c-.31788105-.31788105-.83326807-.31788105-1.15114913 0-.31788105.31788106-.31788105.83326808 0 1.15114913l2.11058921 2.11044008-2.11058921 2.11044008-.07284774.08335637c-.24282581.31905095-.21854323.77640179.07284774 1.06779275l.08335638.0728478c.31905094.2428258.77640179.2185432 1.06779275-.0728478l2.11044008-2.1105892 2.11044008 2.1105892c.31788105.3178811.83326807.3178811 1.15114912 0 .3178811-.31788105.3178811-.83326807 0-1.15114912l-2.1105892-2.11044008 2.1105892-2.11044008.0728478-.08335637c.2428258-.31905095.2185432-.77640179-.0728478-1.06779276z" fill="#fff" fill-opacity=".6" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 951 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import{r as i,a as _}from"./mt-metricskit-utils-private.esm~DkzakSThTT.js";function f(t){this.key=t}f.prototype.toString=function(){return this.key};var l={flagArguments:{INCLUDE_CALL_STACK:new f("INCLUDE_CALL_STACK"),MIRROR_TO_SERVER:new f("MIRROR_TO_SERVER"),SUPPRESS_CLIENT_OUTPUT:new f("SUPPRESS_CLIENT_OUTPUT")},setDelegate:function(e){return i.attachDelegate(this,e)},execute:function(e,n,o){var a=e.levelStringToIntMap[n];if(e.level()!==e.NONE&&e.level()<=a){var u=Array.prototype.slice.call(o),s=l.nonFlagLogArguments(u),v=l.logOptions(e,a,u),h=v.includeCallStack?new Error().stack:null,c=h?s.concat("\n"+h):s;if(e[n]._lastLog=c,v.mirrorToServer,v.throwInsteadOfPrint)throw new Error(s.toString());v.suppressClientOutput||(console[n]?console[n].apply(console,c):console.log.apply(console,c))}},isFlagObject:function(e){return e&&e===l.flagArguments[e.toString()]},nonFlagLogArguments:function(e){return e.filter(function(n){return!l.isFlagObject(n)})},logOptions:function(e,n,o){var a={},u;return o.forEach(function(s){l.isFlagObject(s)&&(u=_.snakeCaseToCamelCase(s.toString()),a[u]=!0)}),i.isFunction(e.mirrorToServerLevel)&&e.mirrorToServerLevel()!==e.NONE&&e.mirrorToServerLevel()<=n&&(a.mirrorToServer=!0),e.throwLevel()!==e.NONE&&e.throwLevel()<=n&&(a.throwInsteadOfPrint=!0),a},sendToServer:function(e,n,o,a){}},p={NONE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4},L={MIN_LEVEL:p.NONE,MAX_LEVEL:p.ERROR,levelIntToStringMap:{0:"none",1:"debug",2:"info",3:"warn",4:"error"},levelStringToIntMap:{none:0,debug:1,info:2,warn:3,error:4}};i.extend(L,p);var g={loggerName:"defaultLogger",level:L.INFO,throwLevel:L.NONE},E=!1,S={};function r(t){this._loggerName=t,this._level,this._throwLevel,E||(E=!0,i.extend(r.prototype,L),i.extend(r.prototype,l.flagArguments))}function O(t){t=t||g.loggerName;var e=S[t];return e||(e=new r(t),S[t]=e),e}r.level=function(){return g.level};r.throwLevel=function(){return g.throwLevel};r.prototype.setDelegate=function(e){return i.attachDelegate(this,e)};r.prototype.loggerName=function(){return this._loggerName};r.prototype.levelParameterAsInt=function(e){var n=null,o;return i.isString(e)?o=this.levelStringToIntMap[e.toLowerCase()]:i.isNumber(e)&&(o=e),o>=this.MIN_LEVEL&&o<=this.MAX_LEVEL&&(n=o),n};r.prototype.setLevel=function(e){var n=this.levelParameterAsInt(e);n!==null&&(this._level=n)};r.prototype.setThrowLevel=function(e){var n=this.levelParameterAsInt(e);n!==null&&(this._throwLevel=n)};r.prototype.level=function(){var e=this._level;return i.isNumber(e)?e:r.level()};r.prototype.levelString=function(){return this.levelIntToStringMap[this.level()]};r.prototype.throwLevel=function(){var e=this._throwLevel;return i.isNumber(e)?e:r.throwLevel()};r.prototype.debug=function(){l.execute(this,"debug",arguments)};r.prototype.info=function(){l.execute(this,"info",arguments)};r.prototype.warn=function(){l.execute(this,"warn",arguments)};r.prototype.error=function(){l.execute(this,"error",arguments)};r.prototype.lastLog=function(e){return this[e]?this[e]._lastLog:null};export{r as L,O as l};
//# sourceMappingURL=mt-client-logger-core.esm~-rJfHcY8Zf.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

17
node_modules/@amp/amd-apps/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./rankers"), exports);

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.demoteByEngagements = void 0;
/**
* Re-rank candidates by demoting candidates based on engagement events.
* @param candidates Recommendable entities
* @param engagementEvents Recorded engagement events
* @returns Reordered candidates
*/
function demoteByEngagements(candidates, _engagementEvents) {
return [...candidates];
}
exports.demoteByEngagements = demoteByEngagements;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,18 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./candidate"), exports);
__exportStar(require("./engagement-event"), exports);

18
node_modules/@amp/amd-apps/dist/rankers/index.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./entities"), exports);
__exportStar(require("./engagement-based-demotion"), exports);

View File

@@ -0,0 +1,23 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./models"), exports);
__exportStar(require("./types/globals"), exports);
__exportStar(require("./types/javascriptcore"), exports);
__exportStar(require("./types/metrics"), exports);
__exportStar(require("./types/models"), exports);
__exportStar(require("./types/optional"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1,250 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unexpectedNull = exports.catchingContext = exports.context = exports.recordValidationIncidents = exports.endContext = exports.getContextNames = exports.beginContext = exports.messageForRecoveryAction = exports.isValidatable = exports.unexpectedType = exports.extendedTypeof = void 0;
const optional_1 = require("../types/optional");
/**
* Returns a string containing the type of a given value.
* This function augments the built in `typeof` operator
* to return sensible values for arrays and null values.
*
* @privateRemarks
* This function is exported for testing.
*
* @param value - The value to find the type of.
* @returns A string containing the type of `value`.
*/
function extendedTypeof(value) {
if (Array.isArray(value)) {
return "array";
}
else if (value === null) {
return "null";
}
else {
return typeof value;
}
}
exports.extendedTypeof = extendedTypeof;
/**
* Reports a non-fatal validation failure, logging a message to the console.
* @param recovery - The recovery action taken when the bad type was found.
* @param expected - The expected type of the value.
* @param actual - The actual value.
* @param pathString - A string containing the path to the value on the object which failed type validation.
*/
function unexpectedType(recovery, expected, actual, pathString) {
const actualType = extendedTypeof(actual);
const prettyPath = (0, optional_1.isSome)(pathString) && pathString.length > 0 ? pathString : "<this>";
trackIncident({
type: "badType",
expected: expected,
// Our test assertions are matching the string interpolation of ${actual} value.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
actual: `${actualType} (${actual})`,
objectPath: prettyPath,
contextNames: getContextNames(),
recoveryAction: recovery,
stack: new Error().stack,
});
}
exports.unexpectedType = unexpectedType;
// endregion
/**
* Determines if a given object conforms to the Validatable interface
* @param possibleValidatable - An object that might be considered validatable
*
* @returns `true` if it is an instance of Validatable, `false` if not
*/
function isValidatable(possibleValidatable) {
if ((0, optional_1.isNothing)(possibleValidatable)) {
return false;
}
// MAINTAINER'S NOTE: We must check for either the existence of a pre-existing incidents
// property *or* the ability to add one. Failure to do so will cause
// problems for clients that either a) use interfaces to define their
// view models; or b) return collections from their service routes.
return (Object.prototype.hasOwnProperty.call(possibleValidatable, "$incidents") ||
Object.isExtensible(possibleValidatable));
}
exports.isValidatable = isValidatable;
/**
* Returns a developer-readable diagnostic message for a given recovery action.
* @param action - The recovery action to get the message for.
* @returns The message for `action`.
*/
function messageForRecoveryAction(action) {
switch (action) {
case "coercedValue":
return "Coerced format";
case "defaultValue":
return "Default value used";
case "ignoredValue":
return "Ignored value";
default:
return "Unknown";
}
}
exports.messageForRecoveryAction = messageForRecoveryAction;
// region Contexts
/**
* Shared validation context "stack".
*
* Because validation incidents propagate up the context stack,
* the representation used here is optimized for memory usage.
* A more literal representation of this would be a singly linked
* list describing a basic stack, but that will produce a large
* amount of unnecessary garbage and require copying `incidents`
* arrays backwards.
*/
const contextState = {
/// The names of each validation context on the stack.
nameStack: Array(),
/// All incidents reported so far. Cleared when the
/// context stack is emptied.
incidents: Array(),
// TODO: Removal of this is being tracked here:
// <rdar://problem/35015460> Intro Pricing: Un-suppress missing parent 'offers' error when server address missing key
/// The paths for incidents we wish to forgo tracking.
suppressedIncidentPaths: Array(),
};
/**
* Begin a new validation context with a given name,
* pushing it onto the validation context stack.
* @param name - The name for the validation context.
*/
function beginContext(name) {
contextState.nameStack.push(name);
}
exports.beginContext = beginContext;
/**
* Traverses the validation context stack and collects all of the context names.
* @returns The names of all validation contexts on the stack, from oldest to newest.
*/
function getContextNames() {
if (contextState.nameStack.length === 0) {
return ["<empty stack>"];
}
return contextState.nameStack.slice(0);
}
exports.getContextNames = getContextNames;
/**
* Ends the current validation context
*/
function endContext() {
if (contextState.nameStack.length === 0) {
console.warn("endContext() called without active validation context, ignoring");
}
contextState.nameStack.pop();
}
exports.endContext = endContext;
/**
* Records validation incidents back into an object that implements Validatable.
*
* Note: This method has a side-effect that the incident queue and name stack are cleared
* to prepare for the next thread's invocation.
*
* @param possibleValidatable - An object that may conform to Validatable, onto which we
* want to stash our validation incidents
*/
function recordValidationIncidents(possibleValidatable) {
if (isValidatable(possibleValidatable)) {
possibleValidatable.$incidents = contextState.incidents;
}
contextState.incidents = [];
contextState.nameStack = [];
contextState.suppressedIncidentPaths = [];
}
exports.recordValidationIncidents = recordValidationIncidents;
/**
* Create a transient validation context, and call a function that will return a value.
*
* Prefer this function over manually calling begin/endContext,
* it is exception safe.
*
* @param name - The name of the context
* @param producer - A function that produces a result
* @returns <Result> The resulting type
*/
function context(name, producer, suppressingPath) {
let suppressingName = null;
if ((0, optional_1.isSome)(suppressingPath) && suppressingPath.length > 0) {
suppressingName = name;
contextState.suppressedIncidentPaths.push(suppressingPath);
}
let result;
try {
beginContext(name);
result = producer();
}
catch (e) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!e.hasThrown) {
unexpectedType("defaultValue", "no exception", e.message);
e.hasThrown = true;
}
throw e;
}
finally {
if (name === suppressingName) {
contextState.suppressedIncidentPaths.pop();
}
endContext();
}
return result;
}
exports.context = context;
/**
* Create a transient validation context, that catches errors and returns null
*
* @param name - The name of the context
* @param producer - A function that produces a result
* @param caught - An optional handler to provide a value when an error is caught
* @returns <Result> The resulting type
*/
function catchingContext(name, producer, caught) {
let result = null;
try {
result = context(name, producer);
}
catch (e) {
result = null;
if ((0, optional_1.isSome)(caught)) {
result = caught(e);
}
}
return result;
}
exports.catchingContext = catchingContext;
/**
* Track an incident within the current validation context.
* @param incident - An incident object describing the problem.
*/
function trackIncident(incident) {
if (contextState.suppressedIncidentPaths.includes(incident.objectPath)) {
return;
}
contextState.incidents.push(incident);
}
// endregion
// region Nullability
/**
* Reports a non-fatal error indicating a value was unexpectedly null.
* @param recovery - The recovery action taken when the null value was found.
* @param expected - The expected type of the value.
* @param pathString - A string containing the path to the value on the object which was null.
*/
function unexpectedNull(recovery, expected, pathString) {
const prettyPath = (0, optional_1.isSome)(pathString) && pathString.length > 0 ? pathString : "<this>";
trackIncident({
type: "nullValue",
expected: expected,
actual: "null",
objectPath: prettyPath,
contextNames: getContextNames(),
recoveryAction: recovery,
stack: new Error().stack,
});
}
exports.unexpectedNull = unexpectedNull;
// endregion
//# sourceMappingURL=validation.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=alert-action.js.map

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeCompoundAction = exports.isCompoundAction = void 0;
/**
* Custom type guard to determine if an action is a CompoundAction.
*/
function isCompoundAction(action) {
return (action === null || action === void 0 ? void 0 : action.$kind) === "compoundAction";
}
exports.isCompoundAction = isCompoundAction;
/**
* Helper that returns a CompoundAction, given an ActionMetrics and ActionModel[] of subactions.
*/
function makeCompoundAction(actionMetrics, subactions) {
return {
$kind: "compoundAction",
subactions,
actionMetrics,
};
}
exports.makeCompoundAction = makeCompoundAction;
//# sourceMappingURL=compound-action.js.map

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeEmptyAction = exports.isEmptyAction = void 0;
/**
* Custom type guard to determine if an action is an EmptyAction.
*/
function isEmptyAction(action) {
return (action === null || action === void 0 ? void 0 : action.$kind) === "emptyAction";
}
exports.isEmptyAction = isEmptyAction;
/**
* Helper that returns an EmptyAction, given an ActionMetrics.
*/
function makeEmptyAction(actionMetrics) {
return {
$kind: "emptyAction",
actionMetrics,
};
}
exports.makeEmptyAction = makeEmptyAction;
//# sourceMappingURL=empty-action.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=external-url-action.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=flow-action.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=flow-back-action.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=http-action.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=http-template-action.js.map

View File

@@ -0,0 +1,26 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./alert-action"), exports);
__exportStar(require("./compound-action"), exports);
__exportStar(require("./empty-action"), exports);
__exportStar(require("./external-url-action"), exports);
__exportStar(require("./flow-action"), exports);
__exportStar(require("./flow-back-action"), exports);
__exportStar(require("./http-action"), exports);
__exportStar(require("./http-template-action"), exports);
__exportStar(require("./toast-action"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=toast-action.js.map

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeArtworkURLTemplate = void 0;
const validation = require("../json/validation");
const optional_1 = require("../types/optional");
const urls_1 = require("../util/urls");
/**
* Regex to parse artwork URL template string.
*/
const URL_TEMPLATE_PARSER = new RegExp("^({w}|[0-9]+(?:.[0-9]*)?)x({h}|[0-9]+(?:.[0-9]*)?)({c}|[a-z]{2}).({f}|[a-z]+)$");
/**
* Create an instance of artwork URL template from string.
* @param fromString - String to create artwork URL template from.
* @returns A new artwork URL template or `null` if string
* does not represent a valid artwork URL template.
*/
function makeArtworkURLTemplate(fromString) {
// A valid URL that ends with '{w}x{h}{c}.{f}'
// with any of placeholders possibly resolved to an actual value.
const url = new urls_1.URL(fromString);
if (url.pathname === undefined) {
validation.context("makeArtworkURLTemplate", () => {
validation.unexpectedType("ignoredValue", "A valid URL string", fromString);
});
return null;
}
// Expecting 5 matches: whole string + width, height, crop code and format.
const lastPathComponent = fromString.substring(fromString.lastIndexOf("/") + 1);
const matches = URL_TEMPLATE_PARSER.exec(lastPathComponent);
if ((0, optional_1.isNothing)(matches) || matches.length !== 5) {
validation.context("makeArtworkURLTemplate", () => {
validation.unexpectedType("ignoredValue", "A valid artwork URL template ending with {w}x{h}{c}.{f} format", lastPathComponent);
});
return null;
}
return fromString;
}
exports.makeArtworkURLTemplate = makeArtworkURLTemplate;
//# sourceMappingURL=artwork.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=button.js.map

View File

@@ -0,0 +1,196 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.areEqual = exports.luminanceFrom = exports.dynamicWith = exports.named = exports.rgbWith = exports.htmlWith = exports.Color = void 0;
const optional_1 = require("../types/optional");
// endregion
// region Constructors
// eslint-disable-next-line no-redeclare, @typescript-eslint/no-redeclare
exports.Color = {
/**
* Create new `HTMLColor` from hexadecimal string representation.
*
* @param hexString - Hexadecimal string representation.
*/
fromHex(string) {
if ((0, optional_1.isNothing)(string)) {
return null;
}
return {
$kind: "html",
value: string,
};
},
/**
* Create new `RBGColor` with RGB components and opacity value.
*
* @param red - Red color value.
* @param green - Green color value.
* @param blue - Blue color value.
* @param alpha - Opacity value.
*/
fromRGB(red, green, blue, alpha = 1.0) {
const newColor = {
$kind: "rgb",
red: red,
green: green,
blue: blue,
alpha: alpha,
};
return newColor;
},
/**
* Create new named color using the color name.
*
* @param name - The name of the color.
*/
named(name) {
const newColor = {
$kind: "named",
name: name,
};
return newColor;
},
/**
* Create new dynamic color with light and dark color variants.
*
* @param lightColor - The light color variant.
* @param lightHighContrastColor - The light hight-contrast color variant.
* @param darkColor - The dark color variant.
* @param darkHighContrastColor - The dark hight-contrast color variant.
*/
dynamicWith(lightColor, lightHighContrastColor, darkColor, darkHighContrastColor) {
const newColor = {
$kind: "dynamic",
lightColor: lightColor,
lightHighContrastColor: lightHighContrastColor,
darkColor: darkColor,
darkHighContrastColor: darkHighContrastColor,
};
return newColor;
},
// endregion
// region Properties
/**
* Get the luminance of the color.
*
* @param rgbColor - The RGB color to get luminance for.
*/
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;
},
// endregion
// region Identity
/**
* Compare two colors for equality.
*
* @param color1 - Left hand side color to compare.
* @param color2 - Right hand side color to compare.
* @returns A Boolean indicating whether the colors are equal.
*/
areEqual(color1, color2) {
if ((0, optional_1.isNothing)(color1)) {
return (0, optional_1.isNothing)(color2);
}
else if ((0, optional_1.isNothing)(color2)) {
return (0, optional_1.isNothing)(color1);
}
const kind1 = color1.$kind;
const kind2 = color2.$kind;
if (kind1 === "named" && kind2 === "named") {
const namedColor1 = color1;
const namedColor2 = color2;
return namedColor1.name === namedColor2.name;
}
else if (kind1 === "rgb" && kind2 === "rgb") {
const rgbColor1 = color1;
const rgbColor2 = color2;
return (rgbColor1.red === rgbColor2.red &&
rgbColor1.green === rgbColor2.green &&
rgbColor1.blue === rgbColor2.blue &&
rgbColor1.alpha === rgbColor2.alpha);
}
else if (kind1 === "dynamic" && kind2 === "dynamic") {
const dynamicColor1 = color1;
const dynamicColor2 = color2;
return (exports.Color.areEqual(dynamicColor1.lightColor, dynamicColor2.lightColor) &&
exports.Color.areEqual(dynamicColor1.lightHighContrastColor, dynamicColor2.lightHighContrastColor) &&
exports.Color.areEqual(dynamicColor1.darkColor, dynamicColor2.darkColor) &&
exports.Color.areEqual(dynamicColor1.darkHighContrastColor, dynamicColor2.darkHighContrastColor));
}
else {
return false;
}
},
};
/**
* Create new `HTMLColor` from hexadecimal string representation.
*
* @param hexString - Hexadecimal string representation.
*
* @deprecated This symbol has been moved to `Color.fromHex` and will be removed
* in the future.
*/
const htmlWith = exports.Color.fromHex;
exports.htmlWith = htmlWith;
/**
* Create new `RBGColor` with RGB components and opacity value.
*
* @param red - Red color value.
* @param green - Green color value.
* @param blue - Blue color value.
* @param alpha - Opacity value.
*
* @deprecated This symbol has been moved to `Color.fromRGB` and will be removed
* in the future.
*/
const rgbWith = exports.Color.fromRGB;
exports.rgbWith = rgbWith;
/**
* Create new named color using the color name.
*
* @param name - The name of the color.
*
* @deprecated This symbol has been moved to `Color.named` and will be removed
* in the future.
*/
const named = exports.Color.named;
exports.named = named;
/**
* Create new dynamic color with light and dark color variants.
*
* @param lightColor - The light color variant.
* @param lightHighContrastColor - The light hight-contrast color variant.
* @param darkColor - The dark color variant.
* @param darkHighContrastColor - The dark hight-contrast color variant.
*
* @deprecated This symbol has been moved to `Color.dynamicWith` and will be removed
* in the future.
*/
const dynamicWith = exports.Color.dynamicWith;
exports.dynamicWith = dynamicWith;
/**
* Get the luminance of the color.
*
* @param rgbColor - The RGB color to get luminance for.
*
* @deprecated This symbol has been moved to `Color.luminanceFrom` and will be removed
* in the future.
*/
const luminanceFrom = exports.Color.luminanceFrom;
exports.luminanceFrom = luminanceFrom;
/**
* Compare two colors for equality.
*
* @param color1 - Left hand side color to compare.
* @param color2 - Right hand side color to compare.
* @returns A Boolean indicating whether the colors are equal.
*
* @deprecated This symbol has been moved to `Color.areEqual` and will be removed
* in the future.
*/
const areEqual = exports.Color.areEqual;
exports.areEqual = areEqual;
// endregion
//# sourceMappingURL=color.js.map

View File

@@ -0,0 +1,25 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./actions"), exports);
__exportStar(require("./artwork"), exports);
__exportStar(require("./button"), exports);
__exportStar(require("./color"), exports);
__exportStar(require("./menu"), exports);
__exportStar(require("./paragraph"), exports);
__exportStar(require("./programmed-text"), exports);
__exportStar(require("./video"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.menuSeparatorID = void 0;
/**
* A standard identifier for including a separator in a menu.
*/
exports.menuSeparatorID = "com.apple.JetEngine.separator";
//# sourceMappingURL=menu.js.map

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// endregion
//# sourceMappingURL=paragraph.js.map

View File

@@ -0,0 +1,5 @@
"use strict";
// region ProgrammedText
Object.defineProperty(exports, "__esModule", { value: true });
// endregion
//# sourceMappingURL=programmed-text.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=video.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=bag.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=bundle.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=cookie-provider.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=cryptography.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=host.js.map

View File

@@ -0,0 +1,55 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
/* `preprocessor` and `testContent` are normally replaced by inline literals while bundling an app's JS.
*
* If these values have not been set we want to provide defaults however
* attempting to access them can trigger a ReferenceError as the
* variables are undefined (distinct from a defined variable being set to
* `undefined`).
*
* `typeof` checks can safely test undefined variables, note that these checks will become:
* `typeof { DEBUG_BUILD: true, ... }` when webpack's DefinePlugin is used in @jet/build's webpack task.
* When these variables have not been replaced we need to use `globalThis` to set them on the global scope
* in order to avoid ReferenceErrors attempting to access them.
*/
if (typeof preprocessor === "undefined") {
globalThis.preprocessor = {
PRODUCTION_BUILD: false,
CARRY_BUILD: false,
DEBUG_BUILD: false,
INTERNAL_BUILD: false,
};
}
if (typeof testContent === "undefined") {
globalThis.testContent = {
INCLUDE_TEST_CONTENT: false,
};
}
__exportStar(require("./bag"), exports);
__exportStar(require("./bundle"), exports);
__exportStar(require("./cookie-provider"), exports);
__exportStar(require("./cryptography"), exports);
__exportStar(require("./host"), exports);
__exportStar(require("./jscookie"), exports);
__exportStar(require("./net"), exports);
__exportStar(require("./platform"), exports);
__exportStar(require("./plist"), exports);
__exportStar(require("./preprocessor"), exports);
__exportStar(require("./random"), exports);
__exportStar(require("./service"), exports);
__exportStar(require("./types"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=jscookie.js.map

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProxiedNetwork = void 0;
/**
* `Network` object designed to wrap the bridged native network object,
* which automatically includes page intent instrumentation data in the fetch request.
*/
class ProxiedNetwork {
constructor(original, pageIntentInstrumentation) {
this.original = original;
this.pageIntentInstrumentation = pageIntentInstrumentation;
}
async fetch(request) {
request["$pageIntentInstrumentation"] = this.pageIntentInstrumentation;
return await this.original.fetch(request);
}
}
exports.ProxiedNetwork = ProxiedNetwork;
//# sourceMappingURL=net.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=platform.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=plist.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=preprocessor.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=random.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=service.js.map

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.services = exports.random = exports.plist = exports.platform = exports.net = exports.localizer = exports.host = exports.cryptography = exports.cookieProvider = exports.bundle = exports.bag = void 0;
const metatype_1 = require("../../util/metatype");
exports.bag = (0, metatype_1.makeMetatype)("jet-engine:bag");
exports.bundle = (0, metatype_1.makeMetatype)("jet-engine:bundle");
exports.cookieProvider = (0, metatype_1.makeMetatype)("jet-engine:cookieProvider");
exports.cryptography = (0, metatype_1.makeMetatype)("jet-engine:cryptography");
exports.host = (0, metatype_1.makeMetatype)("jet-engine:host");
exports.localizer = (0, metatype_1.makeMetatype)("jet-engine:localizer");
exports.net = (0, metatype_1.makeMetatype)("jet-engine:net");
exports.platform = (0, metatype_1.makeMetatype)("jet-engine:platform");
exports.plist = (0, metatype_1.makeMetatype)("jet-engine:plist");
exports.random = (0, metatype_1.makeMetatype)("jet-engine:random");
exports.services = (0, metatype_1.makeMetatype)("jet-engine:services");
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1,14 @@
"use strict";
/*
* Describes standard functionality available in JSContexts
*
* Types are defined here to allow us to match the behavior available in JSContext in the target OS
* which may not exactly match the definitions in standard TypeScript lib files, particularly on a
* pre-release OS.
*
* The living standard for the Console API is available at https://console.spec.whatwg.org
* The WebKit team has documented their interfaces at https://webkit.org/web-inspector/console-object-api/
* The equivalent interface in Node is https://nodejs.org/api/console.html
*/
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=console.js.map

View File

@@ -0,0 +1,18 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./console"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.notInstrumented = exports.PageInvocationPoint = exports.EMPTY_LINTED_METRICS_EVENT = void 0;
/**
* An empty linted metrics event.
*
* The empty events should be skipped from recording
* by metrics event recorders.
*/
exports.EMPTY_LINTED_METRICS_EVENT = {
fields: {},
issues: [],
};
var PageInvocationPoint;
(function (PageInvocationPoint) {
PageInvocationPoint["pageEnter"] = "pageEnter";
PageInvocationPoint["pageExit"] = "pageExit";
PageInvocationPoint["appExit"] = "appExit";
PageInvocationPoint["appEnter"] = "appEnter";
PageInvocationPoint["backButton"] = "backButton";
})(PageInvocationPoint = exports.PageInvocationPoint || (exports.PageInvocationPoint = {}));
/**
* Returns an empty metrics instance of the specified metrics type.
* @param metricsType - Type of the metrics data to return.
*
* @deprecated Do not use, all metrics events should be instrumented.
*/
function notInstrumented(metricsType) {
switch (metricsType) {
case 0 /* NotInstrumentedMetricsType.ActionMetrics */:
return {
data: [],
custom: {},
};
case 1 /* NotInstrumentedMetricsType.FetchTimingMetrics */:
return {};
case 2 /* NotInstrumentedMetricsType.PageMetrics */:
return {
instructions: [],
custom: {},
};
case 3 /* NotInstrumentedMetricsType.ImpressionMetrics */:
return {
id: {
id: "",
impressionIndex: NaN,
},
fields: {},
custom: {},
};
default:
return {};
}
}
exports.notInstrumented = notInstrumented;
// endregion
//# sourceMappingURL=metrics.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=models.js.map

View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.flatMapOptional = exports.mapOptional = exports.unsafeUnwrapOptional = exports.unwrapOptional = exports.isSome = exports.isNothing = exports.unsafeUninitialized = void 0;
/**
* Bypass the protection provided by the `Optional` type
* and pretend to produce a value of `Some<T>` while
* actually returning `Nothing`.
*/
function unsafeUninitialized() {
return undefined;
}
exports.unsafeUninitialized = unsafeUninitialized;
/**
* Test whether an optional does not contain a value.
*
* @param value - An optional value to test.
*/
function isNothing(value) {
return value === undefined || value === null;
}
exports.isNothing = isNothing;
/**
* Test whether an optional contains a value.
* @param value - An optional value to test.
*/
function isSome(value) {
return value !== undefined && value !== null;
}
exports.isSome = isSome;
/**
* Unwrap the value contained in a given optional,
* throwing an error if there is no value.
*
* @param value - A value to unwrap.
*/
function unwrapOptional(value) {
if (isNothing(value)) {
throw new ReferenceError();
}
return value;
}
exports.unwrapOptional = unwrapOptional;
/**
* Unwrap the value contained in a given optional
* without checking if the value exists.
*
* @param value - A value to unwrap.
*/
function unsafeUnwrapOptional(value) {
return value;
}
exports.unsafeUnwrapOptional = unsafeUnwrapOptional;
function mapOptional(value, body) {
if (isSome(value)) {
return body(value);
}
else {
return value;
}
}
exports.mapOptional = mapOptional;
function flatMapOptional(value, body) {
if (isSome(value)) {
return body(value);
}
else {
return value;
}
}
exports.flatMapOptional = flatMapOptional;
//# sourceMappingURL=optional.js.map

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeMetatype = void 0;
function makeMetatype(name) {
return {
name: name,
};
}
exports.makeMetatype = makeMetatype;
//# sourceMappingURL=metatype.js.map

View File

@@ -0,0 +1,373 @@
"use strict";
// MARK: - Parsing Regular Expressions
Object.defineProperty(exports, "__esModule", { value: true });
exports.URL = void 0;
const optional_1 = require("../types/optional");
const protocolRegex = /^([a-z][a-z0-9.+-]*:)(\/\/)?([\S\s]*)/i;
const queryParamRegex = /([^=?&]+)=?([^&]*)/g;
const componentOrder = ["hash", "query", "pathname", "host"];
class URL {
constructor(url) {
var _a;
this.query = {};
if ((0, optional_1.isNothing)(url)) {
return;
}
// Split the protocol from the rest of the urls
let remainder = url;
const match = protocolRegex.exec(url);
if ((0, optional_1.isSome)(match)) {
// Pull out the protocol
let protocol = match[1];
if (protocol !== null && protocol !== undefined) {
protocol = protocol.split(":")[0];
}
this.protocol = protocol !== null && protocol !== void 0 ? protocol : undefined;
// Save the remainder
remainder = (_a = match[3]) !== null && _a !== void 0 ? _a : undefined;
}
// Then match each component in a specific order
let parse = { remainder: remainder, result: undefined };
for (const component of componentOrder) {
if (parse === undefined || parse.remainder === undefined) {
break;
}
switch (component) {
case "hash": {
parse = splitUrlComponent(parse.remainder, "#", "suffix");
this.hash = parse === null || parse === void 0 ? void 0 : parse.result;
break;
}
case "query": {
parse = splitUrlComponent(parse.remainder, "?", "suffix");
if ((parse === null || parse === void 0 ? void 0 : parse.result) !== undefined) {
this.query = URL.queryFromString(parse.result);
}
break;
}
case "pathname": {
parse = splitUrlComponent(parse.remainder, "/", "suffix");
if ((parse === null || parse === void 0 ? void 0 : parse.result) !== undefined) {
// Replace the initial /, since paths require it
this.pathname = "/" + parse.result;
}
break;
}
case "host": {
const authorityParse = splitUrlComponent(parse.remainder, "@", "prefix");
const userInfo = authorityParse === null || authorityParse === void 0 ? void 0 : authorityParse.result;
const hostPort = authorityParse === null || authorityParse === void 0 ? void 0 : authorityParse.remainder;
if (userInfo !== undefined) {
const userInfoSplit = userInfo.split(":");
this.username = decodeURIComponent(userInfoSplit[0]);
this.password = decodeURIComponent(userInfoSplit[1]);
}
if (hostPort !== undefined) {
const hostPortSplit = hostPort.split(":");
this.host = hostPortSplit[0];
this.port = hostPortSplit[1];
}
break;
}
default: {
throw new Error("Unhandled case!");
}
}
}
}
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];
}
}
set(component, value) {
if (value === undefined) {
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;
}
append(component, value) {
let existingValue = this.get(component);
let newValue;
if (component === "query") {
if (existingValue === undefined) {
existingValue = {};
}
if (typeof value === "string") {
value = URL.queryFromString(value);
}
if (typeof existingValue === "string") {
newValue = { existingValue, ...value };
}
else {
newValue = { ...existingValue, ...value };
}
}
else {
if (existingValue === undefined) {
existingValue = "";
}
let existingValueString = existingValue;
if (existingValueString === undefined) {
existingValueString = "";
}
let newValueString = existingValueString;
if (component === "pathname") {
const pathLength = existingValueString.length;
if (pathLength === 0 || existingValue[pathLength - 1] !== "/") {
newValueString += "/";
}
}
// The component is not "query" so we treat value as string.
// eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-plus-operands
newValueString += value;
newValue = newValueString;
}
return this.set(component, newValue);
}
param(key, value) {
if (key === null) {
return this;
}
if (this.query === undefined) {
this.query = {};
}
if (value === undefined) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this.query[key];
}
else {
this.query[key] = value;
}
return this;
}
removeParam(key) {
if (key === undefined || this.query === undefined) {
return this;
}
if (key in this.query) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this.query[key];
}
return this;
}
path(value) {
return this.append("pathname", value);
}
pathExtension() {
var _a, _b;
// Extract path extension if one exists
if (this.pathname === undefined) {
return undefined;
}
const lastFilenameComponents = (_b = (_a = this.pathname
.split("/")
.filter((item) => item.length > 0) // Remove any double or trailing slashes
.pop()) === null || _a === void 0 ? void 0 : _a.split(".")) !== null && _b !== void 0 ? _b : [];
if (lastFilenameComponents.filter(function (part) {
return part !== "";
}).length < 2 // Remove any empty parts (e.g. .ssh_config -> ["ssh_config"])
) {
return undefined;
}
return lastFilenameComponents.pop();
}
/**
* Returns the path components of the URL
* @returns An array of non-empty path components from `urls`.
*/
pathComponents() {
if (this.pathname === undefined) {
return [];
}
return this.pathname.split("/").filter((component) => component.length > 0);
}
/**
* Same as toString
*
* @returns A string representation of the URL
*/
build() {
return this.toString();
}
/**
* Converts the URL to a string
*
* @returns A string representation of the URL
*/
toString() {
let url = "";
if (this.protocol !== undefined) {
url += this.protocol + "://";
}
if (this.username !== undefined) {
url += encodeURIComponent(this.username);
if (this.password !== undefined) {
url += ":" + encodeURIComponent(this.password);
}
url += "@";
}
if (this.host !== undefined) {
url += this.host;
if (this.port !== undefined) {
url += ":" + this.port;
}
}
if (this.pathname !== undefined) {
url += this.pathname;
}
if (this.query !== undefined && Object.keys(this.query).length !== 0) {
url += "?" + URL.toQueryString(this.query);
}
if (this.hash !== undefined) {
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 !== null && parseResult.length >= 3) {
// We support the legacy query format for "application/x-www-form-urlencoded" which can represent spaces as "+" symbols.
// https://url.spec.whatwg.org/#concept-urlencoded-parser
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#decoding_query_parameters_from_a_url
const key = decodeURIComponent(parseResult[1].replace(/\+/g, " "));
const value = decodeURIComponent(parseResult[2].replace(/\+/g, " "));
result[key] = value;
parseResult = queryParamRegex.exec(query);
}
return result;
}
/**
* Converts a query dictionary into a query string
*
* @param query - The query dictionary
* @returns 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 !== null && value.length > 0) {
queryString += "=" + encodeURIComponent(value);
}
}
return queryString;
}
/**
* Convenience method to instantiate a URL from a string
* @param url - The URL string to parse
* @returns 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 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 !== null && query !== void 0 ? query : {};
url.hash = hash;
return url;
}
}
exports.URL = URL;
// MARK: - Helpers
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;
}
}
return {
result: result,
remainder: remainder,
};
}
//# sourceMappingURL=urls.js.map

View File

@@ -0,0 +1,289 @@
import { Opt, isNothing } from "@jet/environment/types/optional";
import * as serverData from "./server-data";
import * as media from "./data-structure";
import { JSONValue, MapLike, JSONData } from "./json-types";
import * as errors from "./errors";
// 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<Type extends JSONValue>(
data: media.Data,
attributePath?: serverData.ObjectPath,
defaultValue?: MapLike<Type>,
): MapLike<Type> | null {
if (serverData.isNull(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<Interface>(
data: media.Data,
attributePath?: serverData.ObjectPath,
defaultValue?: JSONData,
): Interface | null {
return attributeAsDictionary(data, attributePath, defaultValue) as unknown as Interface;
}
/**
* 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<T extends JSONValue>(
data: media.Data,
attributePath?: serverData.ObjectPath,
): T[] {
if (serverData.isNull(data)) {
return [];
}
return serverData.asArrayOrEmpty(data.attributes, attributePath);
}
/**
* 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: media.Data,
attributePath?: serverData.ObjectPath,
policy: serverData.ValidationPolicy = "coercible",
): Opt<string> {
if (serverData.isNull(data)) {
return null;
}
return serverData.asString(data.attributes, attributePath, policy);
}
/**
* Retrieve the specified meta from the data as a string.
*
* @param data The data from which to retrieve the attribute.
* @param metaPath The object path for the meta.
* @param policy The validation policy to use when resolving this value.
* @returns {string} The meta value as a string.
*/
export function metaAsString(
data: media.Data,
metaPath?: serverData.ObjectPath,
policy: serverData.ValidationPolicy = "coercible",
): Opt<string> {
if (serverData.isNull(data)) {
return null;
}
return serverData.asString(data.meta, metaPath, policy);
}
/**
* Retrieve the specified attribute from the data as a date.
*
* @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 {Date} The attribute value as a date.
*/
export function attributeAsDate(
data: media.Data,
attributePath?: serverData.ObjectPath,
policy: serverData.ValidationPolicy = "coercible",
): Opt<Date> {
if (serverData.isNull(data)) {
return null;
}
const dateString = serverData.asString(data.attributes, attributePath, policy);
if (isNothing(dateString)) {
return null;
}
return new Date(dateString);
}
/**
* 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: media.Data,
attributePath?: serverData.ObjectPath,
policy: serverData.ValidationPolicy = "coercible",
): boolean | null {
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: media.Data, attributePath?: serverData.ObjectPath): boolean {
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: media.Data,
attributePath?: serverData.ObjectPath,
policy: serverData.ValidationPolicy = "coercible",
): Opt<number> {
if (serverData.isNull(data)) {
return null;
}
return serverData.asNumber(data.attributes, attributePath, policy);
}
export function hasAttributes(data: media.Data): boolean {
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: media.Data): boolean {
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: string): string | undefined {
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: string): boolean {
return customAttribute === "customArtwork" || customAttribute === "customIconArtwork"; // Only the icon artwork.
}
/**
* Defines mapping of attribute to custom attribute.
*/
const customAttributeMapping: { [key: string]: string } = {
artwork: "customArtwork",
iconArtwork: "customIconArtwork",
screenshotsByType: "customScreenshotsByType",
promotionalText: "customPromotionalText",
videoPreviewsByType: "customVideoPreviewsByType",
customScreenshotsByTypeForAd: "customScreenshotsByTypeForAd",
customVideoPreviewsByTypeForAd: "customVideoPreviewsByTypeForAd",
};
export function requiredAttributeAsString(data: media.Data, attributePath: serverData.ObjectPath): string {
const value = attributeAsString(data, attributePath);
if (isNothing(value)) {
throw new errors.MissingFieldError(data, concatObjectPaths("attributes", attributePath));
}
return value;
}
export function requiredAttributeAsDate(data: media.Data, attributePath: serverData.ObjectPath): Date {
const value = attributeAsDate(data, attributePath);
if (isNothing(value)) {
throw new errors.MissingFieldError(data, concatObjectPaths("attributes", attributePath));
}
return value;
}
export function requiredAttributeAsDictionary<Type extends JSONValue>(
data: media.Data,
attributePath: serverData.ObjectPath,
): MapLike<Type> {
const value: MapLike<Type> | null = attributeAsDictionary(data, attributePath);
if (isNothing(value)) {
throw new errors.MissingFieldError(data, concatObjectPaths("attributes", attributePath));
}
return value;
}
export function requiredMeta(data: media.Data): MapLike<JSONValue> {
const value = serverData.asDictionary(data, "meta");
if (isNothing(value)) {
throw new errors.MissingFieldError(data, "meta");
}
return value;
}
export function requiredMetaAttributeAsString(data: media.Data, attributePath: serverData.ObjectPath): string {
const meta = requiredMeta(data);
const value = serverData.asString(meta, attributePath);
if (isNothing(value)) {
throw new errors.MissingFieldError(data, concatObjectPaths("meta", attributePath));
}
return value;
}
export function requiredMetaAttributeAsNumber(data: media.Data, attributePath: serverData.ObjectPath): number {
const meta = requiredMeta(data);
const value = serverData.asNumber(meta, attributePath);
if (isNothing(value)) {
throw new errors.MissingFieldError(data, concatObjectPaths("meta", attributePath));
}
return value;
}
export function concatObjectPaths(prefix: serverData.ObjectPath, suffix: serverData.ObjectPath): serverData.ObjectPath {
let finalPath: string[];
if (Array.isArray(prefix)) {
finalPath = prefix;
} else {
finalPath = [prefix];
}
if (Array.isArray(suffix)) {
finalPath.push(...suffix);
} else {
finalPath.push(suffix);
}
return finalPath;
}
// endregion

View File

@@ -0,0 +1,476 @@
//
// server-data.ts
// AppStoreKit
//
// Created by Kevin MacWhinnie on 8/17/16.
// Copyright (c) 2016 Apple Inc. All rights reserved.
//
// TODO: Replace this utility for JSON Parsing
import * as validation from "@jet/environment/json/validation";
import { Nothing, Opt, isNothing } from "@jet/environment/types/optional";
import { JSONArray, JSONData, JSONValue, MapLike } from "./json-types";
// region Traversal
/**
* Union type that describes the possible representations for an object traversal path.
*/
export type ObjectPath = string | string[];
/**
* 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: Opt<ObjectPath>): Opt<string> {
if (isNull(path)) {
return null;
} else if (Array.isArray(path)) {
return path.join(".");
} else {
return path;
}
}
const PARSED_PATH_CACHE: { [key: string]: string[] } = {};
/**
* 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: JSONValue, path?: ObjectPath, defaultValue?: JSONValue): JSONValue {
if (object === undefined || object === null) {
return defaultValue;
}
if (isNullOrEmpty(path)) {
return object;
}
let components: string[];
if (typeof path === "string") {
components = PARSED_PATH_CACHE[path];
if (isNullOrEmpty(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: JSONValue = 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<Type>(object: Type | Nothing): object is Nothing {
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<Type>(object: Type | Nothing): object is Nothing {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return isNull(object) || Object.keys(object as any).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<Type>(object: Type | null | undefined): object is Type {
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<Type>(object: Type | Nothing): object is Type {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return isDefinedNonNull(object) && Object.keys(object as any).length !== 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: number | string | null | undefined): value is number {
if (isNull(value)) {
return false;
}
let valueToCheck;
if (typeof value === "string") {
valueToCheck = parseInt(value);
} else {
valueToCheck = value;
}
return !Number.isNaN(valueToCheck);
}
/**
* 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<Type extends JSONArray>(object: Type | null | undefined): object is Type {
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<T extends JSONValue>(object: JSONValue, path?: ObjectPath): T[] {
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 as T[];
} else {
if (!isNull(target)) {
validation.context("asArrayOrEmpty", () => {
validation.unexpectedType("defaultValue", "array", target, objectPathToString(path));
});
}
return [];
}
}
/**
* 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: JSONValue, path?: ObjectPath): boolean {
const target = traverse(object, path, null);
if (typeof target === "boolean") {
return target;
} else {
if (!isNull(target)) {
validation.context("asBooleanOrFalse", () => {
validation.unexpectedType("defaultValue", "boolean", target, objectPathToString(path));
});
}
return false;
}
}
// endregion
// region Coercing Casts
export type ValidationPolicy = "strict" | "coercible" | "none";
/**
* 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: JSONValue, path?: ObjectPath, policy: ValidationPolicy = "coercible"): Opt<string> {
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 date.
* @param object The object to coerce.
* @param path The path to traverse on `object` to find a date.
* @param policy The validation policy to use when resolving this value
* @returns A date from `object`, or `null` if `object` is null.
*/
export function asDate(object: JSONValue, path?: ObjectPath, policy: ValidationPolicy = "coercible"): Opt<Date> {
const dateString = asString(object, path, policy);
if (isNothing(dateString)) {
return null;
}
return new Date(dateString);
}
/**
* 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: JSONValue, path?: ObjectPath, policy: ValidationPolicy = "coercible"): Opt<number> {
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<Type extends JSONValue>(
object: JSONValue,
path?: ObjectPath,
defaultValue?: MapLike<Type>,
): MapLike<Type> | null {
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 as MapLike<Type>;
} else {
if (!isNull(target)) {
validation.context("asDictionary", () => {
validation.unexpectedType("defaultValue", "object", 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<Interface>(
object: JSONValue,
path?: ObjectPath,
defaultValue?: JSONData,
): Interface | null {
return asDictionary(object, path, defaultValue) as unknown as Interface;
}
/**
* 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: JSONValue,
path?: ObjectPath,
policy: ValidationPolicy = "coercible",
): boolean | null {
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: unknown): JSONValue | null {
if (value === null || value === undefined) {
return null;
}
switch (typeof value) {
case "string":
case "number":
case "boolean":
return value as JSONValue;
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 as JSONValue;
}
// Note: It's too expensive to actually validate this is a dictionary of { string : JSONValue } at run time
return value as JSONValue;
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: unknown): JSONData | null {
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 as JSONData;
}
validation.context("asJSONValue", () => {
validation.unexpectedType("defaultValue", "object", typeof value);
});
return null;
}
// endregion

View File

@@ -0,0 +1,469 @@
/**
* Created by keithpk on 12/2/16.
*/
import { isNothing, Nothing, Opt } from "@jet/environment/types/optional";
import { isDefinedNonNullNonEmpty, isNullOrEmpty } from "./server-data";
export type Query = {
[key: string]: string | undefined;
};
export type URLComponent = "protocol" | "username" | "password" | "host" | "port" | "pathname" | "query" | "hash";
const protocolRegex = /^([a-z][a-z0-9.+-]*:)(\/\/)?([\S\s]*)/i;
const queryParamRegex = /([^=?&]+)=?([^&]*)/g;
const componentOrder: URLComponent[] = ["hash", "query", "pathname", "host"];
type URLSplitStyle = "prefix" | "suffix";
type URLSplitResult = {
result?: string;
remainder: string;
};
function splitUrlComponent(input: string, marker: string, style: URLSplitStyle): URLSplitResult {
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 {
protocol?: Opt<string>;
username: string;
password: string;
host?: Opt<string>;
port: string;
pathname?: Opt<string>;
query?: Query = {};
hash?: string;
constructor(url?: string) {
if (isNullOrEmpty(url)) {
return;
}
// Split the protocol from the rest of the urls
let remainder = url;
const match = protocolRegex.exec(url);
if (match != null) {
// 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: URLSplitResult = { 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 (isDefinedNonNullNonEmpty(parse.result)) {
this.query = URL.queryFromString(parse.result);
}
break;
}
case "pathname": {
parse = splitUrlComponent(parse.remainder, "/", "suffix");
if (isDefinedNonNullNonEmpty(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 (isDefinedNonNullNonEmpty(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: URLComponent, value: string | Query): URL {
if (isNullOrEmpty(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 as string;
break;
case "username":
this.username = value as string;
break;
case "password":
this.password = value as string;
break;
case "port":
this.port = value as string;
break;
case "pathname":
this.pathname = value as string;
break;
case "query":
this.query = value as Query;
break;
case "hash":
this.hash = value as string;
break;
default:
// The fallback for component which is not a property of URL object.
this[component] = value as string;
break;
}
return this;
}
private get(component: URLComponent): string | Query | Nothing {
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: URLComponent, value: string | Query): URL {
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 as string;
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: string, value?: string): URL {
if (!key) {
return this;
}
if (this.query == null) {
this.query = {};
}
this.query[key] = value;
return this;
}
removeParam(key: string): URL {
if (!key || this.query == null) {
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: string): URL {
return this.append("pathname", value);
}
pathExtension(): Opt<string> {
// Extract path extension if one exists
if (isNothing(this.pathname)) {
return null;
}
const lastFilenameComponents = this.pathname
.split("/")
.filter((item) => item.length > 0) // Remove any double or trailing slashes
.pop()
?.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(): string[] {
if (isNullOrEmpty(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(): string | null {
if (isNullOrEmpty(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(): string {
return this.toString();
}
/**
* Converts the URL to a string
*
* @returns {string} A string representation of the URL
*/
toString(): string {
let url = "";
if (isDefinedNonNullNonEmpty(this.protocol)) {
url += this.protocol + "://";
}
if (this.username) {
url += encodeURIComponent(this.username);
if (this.password) {
url += ":" + encodeURIComponent(this.password);
}
url += "@";
}
if (isDefinedNonNullNonEmpty(this.host)) {
url += this.host;
if (this.port) {
url += ":" + this.port;
}
}
if (isDefinedNonNullNonEmpty(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 != null && Object.keys(this.query).length > 0) {
url += "?" + URL.toQueryString(this.query);
}
if (isDefinedNonNullNonEmpty(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: string): Query {
const result = {};
let parseResult = queryParamRegex.exec(query);
while (parseResult != null) {
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: 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 (isDefinedNonNullNonEmpty(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: string): 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?: Opt<string>,
host?: Opt<string>,
path?: Opt<string>,
query?: Query,
hash?: string,
): URL {
const url = new URL();
url.protocol = protocol;
url.host = host;
url.pathname = path;
url.query = query;
url.hash = hash;
return url;
}
}

View File

@@ -0,0 +1,403 @@
/**
* 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 { FetchRequest, FetchResponse, HTTPTimingMetrics } from "@jet/environment/types/globals/net";
import { FetchTimingMetrics, MetricsFields } from "@jet/environment/types/metrics";
import { FetchTimingMetricsBuilder } from "@jet/environment/metrics";
import { isSome, Opt } from "@jet/environment/types/optional";
import * as serverData from "./models/server-data";
import { ParsedNetworkResponse } from "./models/data-structure";
import { Request } from "./models/request";
import * as urls from "./models/urls";
import * as urlBuilder from "./url-builder";
import { MediaConfigurationType, MediaTokenService } from "./models/mediapi-configuration";
import { HTTPMethod, HTTPCachePolicy, HTTPSigningStyle, HTTPHeaders } from "@jet/environment";
/** @public */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ResponseMetadata {
export const requestedUrl = "_jet-internal:metricsHelpers_requestedUrl";
/**
* Symbol used to place timing metrics values onto fetch responses
* without interfering with the data returned by the server.
*/
export const timingValues = "_jet-internal:metricsHelpers_timingValues";
/**
* Key used to access the page information gathered from a response's headers
*/
export const pageInformation = "_jet-internal:metricsHelpers_pageInformation";
/**
* Key used to access the content max-age gathered from a response's headers.
*/
export const contentMaxAge = "_jet-internal:responseMetadata_contentMaxAge";
}
/**
* 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.
*/
export async function fetch<Type>(
configuration: MediaConfigurationType,
request: FetchRequest,
parser: (value: Opt<string>) => Type,
): Promise<Type & ParsedNetworkResponse> {
const response = await configuration.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) as Type & ParsedNetworkResponse;
const parseEndTime = Date.now();
// 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<Type>(configuration: MediaConfigurationType, request: FetchRequest): Promise<Type> {
return await fetch(configuration, request, (body) => {
if (isSome(body)) {
return JSON.parse(body) as Type;
} else {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {} as Type;
}
});
}
/**
* 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<Type>(configuration: MediaConfigurationType, request: FetchRequest): Promise<Type> {
return await fetch(configuration, request, (body) => {
if (isSome(body)) {
return configuration.plist.parse(body) as Type;
} 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: HTTPTimingMetrics[] | null,
parseStartTime: number,
parseEndTime: number,
): FetchTimingMetrics | null {
// 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: FetchTimingMetrics = {
...responseMetrics[0],
parseStartTime: parseStartTime,
parseEndTime: parseEndTime,
};
// Timing metrics with all properties populated.
return firstPartialTimingMetrics;
}
export type FetchOptions = {
headers?: { [key: string]: string };
method?: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH";
requestBodyString?: string;
timeout?: number; // in seconds. Check for feature 'supportsRequestTimeoutOption'.
/// When true the fetch wont throw if we dont get any data back for given request.
allowEmptyDataResponse?: boolean;
excludeIdentifierHeadersForAccount?: boolean; // Defaults to false
alwaysIncludeAuthKitHeaders?: boolean; // Defaults to true
alwaysIncludeMMeClientInfoAndDeviceHeaders?: boolean; // Defaults to true
};
/**
* Implements the MAPI fetch, building URL from MAPI Request and opaquely managing initial token request and refreshes.
*
* @param {MediaConfigurationType} configuration Base media API configuration.
* @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<Type>(
configuration: MediaConfigurationType,
mediaToken: MediaTokenService,
request: Request,
options?: FetchOptions,
): Promise<Type & ParsedNetworkResponse> {
const url = urlBuilder.buildURLFromRequest(configuration, request).toString();
const startTime = Date.now();
const token = await mediaToken.refreshToken();
const response = await fetchWithToken<Type>(
configuration,
mediaToken,
url,
token,
options,
false,
configuration.fetchTimingMetricsBuilder,
);
const endTime = Date.now();
if (request.canonicalUrl) {
response[ResponseMetadata.requestedUrl] = request.canonicalUrl;
}
const roundTripTimeIncludingWaiting = endTime - startTime;
if (roundTripTimeIncludingWaiting > 500) {
console.warn("Fetch took too long (" + roundTripTimeIncludingWaiting.toString() + "ms) " + url);
}
return response;
}
export function redirectParametersInUrl(configuration: MediaConfigurationType, url: urls.URL): string[] {
const redirectURLParams = configuration.redirectUrlWhitelistedQueryParams;
return redirectURLParams.filter((param) => serverData.isDefinedNonNull(url.query?.[param]));
}
export type MediaAPIFetchRequest = {
url: string;
excludeIdentifierHeadersForAccount?: boolean;
alwaysIncludeAuthKitHeaders?: boolean;
alwaysIncludeMMeClientInfoAndDeviceHeaders?: boolean;
method?: Opt<HTTPMethod>;
cache?: Opt<HTTPCachePolicy>;
signingStyle?: Opt<HTTPSigningStyle>;
headers?: Opt<HTTPHeaders>;
timeout?: Opt<number>;
body?: Opt<string>;
};
/**
* 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} 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<Type>(
configuration: MediaConfigurationType,
mediaToken: MediaTokenService,
url: string,
token: string,
options: FetchOptions = {},
isRetry = false,
fetchTimingMetricsBuilder: Opt<FetchTimingMetricsBuilder>,
): Promise<Type & ParsedNetworkResponse> {
// Removes all affiliate/redirect params for caching (https://connectme.apple.com/docs/DOC-577671)
const filteredURL = new urls.URL(url);
const redirectParameters = redirectParametersInUrl(configuration, filteredURL);
for (const param of redirectParameters) {
filteredURL.removeParam(param);
}
const filteredUrlString = filteredURL.toString();
let headers = options.headers;
if (headers == null) {
headers = {};
}
headers["Authorization"] = "Bearer " + token;
const fetchRequest: MediaAPIFetchRequest = {
url: filteredUrlString,
excludeIdentifierHeadersForAccount: options.excludeIdentifierHeadersForAccount ?? false,
alwaysIncludeAuthKitHeaders: options.alwaysIncludeAuthKitHeaders ?? true,
alwaysIncludeMMeClientInfoAndDeviceHeaders: options.alwaysIncludeMMeClientInfoAndDeviceHeaders ?? true,
headers: headers,
method: options.method,
body: options.requestBodyString,
timeout: options.timeout,
};
const response = await configuration.network.fetch(fetchRequest);
try {
if (response.status === 401 || response.status === 403) {
if (isRetry) {
throw Error("We refreshed the token but we still get 401 from the API");
}
mediaToken.resetToken();
return await mediaToken.refreshToken().then(async (newToken) => {
// Explicitly re-fetch with the original request so logging and metrics are correct
return await fetchWithToken<Type>(
configuration,
mediaToken,
url,
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 correlationKey = response.headers["x-apple-jingle-correlation-key"] ?? "N/A";
const error = new NetworkError(
`Bad Status code ${response.status} (correlationKey: ${correlationKey}) for ${filteredUrlString}, original ${url}`,
);
error.statusCode = response.status;
throw error;
}
const parser = (resp: FetchResponse) => {
const parseStartTime = Date.now();
let result: Type & ParsedNetworkResponse;
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: ParsedNetworkResponse = {};
result = emptyData as Type & ParsedNetworkResponse;
} else {
throw noContentError();
}
} else {
result = JSON.parse(resp.body) as Type & ParsedNetworkResponse;
}
const parseEndTime = Date.now();
result[ResponseMetadata.pageInformation] = serverData.asJSONData(
getPageInformationFromResponse(configuration, resp),
);
if (resp.metrics.length > 0) {
const metrics: FetchTimingMetrics = {
...resp.metrics[0],
parseStartTime: parseStartTime,
parseEndTime: parseEndTime,
};
result[ResponseMetadata.timingValues] = metrics;
} else {
const fallbackMetrics: FetchTimingMetrics = {
pageURL: resp.url,
parseStartTime,
parseEndTime,
};
result[ResponseMetadata.timingValues] = fallbackMetrics;
}
result[ResponseMetadata.contentMaxAge] = getContentTimeToLiveFromResponse(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();
}
result[ResponseMetadata.requestedUrl] = url;
return result;
};
if (isSome(fetchTimingMetricsBuilder)) {
return fetchTimingMetricsBuilder.measureParsing(response, parser);
} else {
return parser(response);
}
} catch (e) {
if (e instanceof NetworkError) {
throw e;
}
throw new Error(`Error Fetching - filtered: ${filteredUrlString}, original: ${url}, ${e.name}, ${e.message}`);
}
}
export class NetworkError extends Error {
statusCode?: number;
}
function noContentError(): NetworkError {
const error = new NetworkError(`No content`);
error.statusCode = 204;
return error;
}
const serverInstanceHeader = "x-apple-application-instance";
const environmentDataCenterHeader = "x-apple-application-site";
function getPageInformationFromResponse(
configuration: MediaConfigurationType,
response: FetchResponse,
): MetricsFields | null {
const storeFrontHeader: string = configuration.storefrontIdentifier;
let storeFront: Opt<string> = null;
if (serverData.isDefinedNonNullNonEmpty(storeFrontHeader)) {
const storeFrontHeaderComponents: string[] = storeFrontHeader.split("-");
if (serverData.isDefinedNonNullNonEmpty(storeFrontHeaderComponents)) {
storeFront = storeFrontHeaderComponents[0];
}
}
return {
serverInstance: response.headers[serverInstanceHeader],
storeFrontHeader: storeFrontHeader,
language: configuration.bagLanguage,
storeFront: storeFront,
environmentDataCenter: response.headers[environmentDataCenterHeader],
};
}
function getContentTimeToLiveFromResponse(response: FetchResponse): Opt<number> {
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]);
}

1046
node_modules/@floating-ui/core/dist/floating-ui.core.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

759
node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs generated vendored Normal file
View File

@@ -0,0 +1,759 @@
import { rectToClientRect, arrow as arrow$1, autoPlacement as autoPlacement$1, detectOverflow as detectOverflow$1, flip as flip$1, hide as hide$1, inline as inline$1, limitShift as limitShift$1, offset as offset$1, shift as shift$1, size as size$1, computePosition as computePosition$1 } from '@floating-ui/core';
import { round, createCoords, max, min, floor } from '@floating-ui/utils';
import { getComputedStyle, isHTMLElement, isElement, getWindow, isWebKit, getFrameElement, getNodeScroll, getDocumentElement, isTopLayer, getNodeName, isOverflowElement, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';
export { getOverflowAncestors } from '@floating-ui/utils/dom';
function getCssDimensions(element) {
const css = getComputedStyle(element);
// In testing environments, the `width` and `height` properties are empty
// strings for SVG elements, returning NaN. Fallback to `0` in this case.
let width = parseFloat(css.width) || 0;
let height = parseFloat(css.height) || 0;
const hasOffset = isHTMLElement(element);
const offsetWidth = hasOffset ? element.offsetWidth : width;
const offsetHeight = hasOffset ? element.offsetHeight : height;
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height,
$: shouldFallback
};
}
function unwrapElement(element) {
return !isElement(element) ? element.contextElement : element;
}
function getScale(element) {
const domElement = unwrapElement(element);
if (!isHTMLElement(domElement)) {
return createCoords(1);
}
const rect = domElement.getBoundingClientRect();
const {
width,
height,
$
} = getCssDimensions(domElement);
let x = ($ ? round(rect.width) : rect.width) / width;
let y = ($ ? round(rect.height) : rect.height) / height;
// 0, NaN, or Infinity should always fallback to 1.
if (!x || !Number.isFinite(x)) {
x = 1;
}
if (!y || !Number.isFinite(y)) {
y = 1;
}
return {
x,
y
};
}
const noOffsets = /*#__PURE__*/createCoords(0);
function getVisualOffsets(element) {
const win = getWindow(element);
if (!isWebKit() || !win.visualViewport) {
return noOffsets;
}
return {
x: win.visualViewport.offsetLeft,
y: win.visualViewport.offsetTop
};
}
function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
if (isFixed === void 0) {
isFixed = false;
}
if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {
return false;
}
return isFixed;
}
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
if (includeScale === void 0) {
includeScale = false;
}
if (isFixedStrategy === void 0) {
isFixedStrategy = false;
}
const clientRect = element.getBoundingClientRect();
const domElement = unwrapElement(element);
let scale = createCoords(1);
if (includeScale) {
if (offsetParent) {
if (isElement(offsetParent)) {
scale = getScale(offsetParent);
}
} else {
scale = getScale(element);
}
}
const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
let x = (clientRect.left + visualOffsets.x) / scale.x;
let y = (clientRect.top + visualOffsets.y) / scale.y;
let width = clientRect.width / scale.x;
let height = clientRect.height / scale.y;
if (domElement) {
const win = getWindow(domElement);
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
let currentWin = win;
let currentIFrame = getFrameElement(currentWin);
while (currentIFrame && offsetParent && offsetWin !== currentWin) {
const iframeScale = getScale(currentIFrame);
const iframeRect = currentIFrame.getBoundingClientRect();
const css = getComputedStyle(currentIFrame);
const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
x *= iframeScale.x;
y *= iframeScale.y;
width *= iframeScale.x;
height *= iframeScale.y;
x += left;
y += top;
currentWin = getWindow(currentIFrame);
currentIFrame = getFrameElement(currentWin);
}
}
return rectToClientRect({
width,
height,
x,
y
});
}
// If <html> has a CSS width greater than the viewport, then this will be
// incorrect for RTL.
function getWindowScrollBarX(element, rect) {
const leftScroll = getNodeScroll(element).scrollLeft;
if (!rect) {
return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;
}
return rect.left + leftScroll;
}
function getHTMLOffset(documentElement, scroll, ignoreScrollbarX) {
if (ignoreScrollbarX === void 0) {
ignoreScrollbarX = false;
}
const htmlRect = documentElement.getBoundingClientRect();
const x = htmlRect.left + scroll.scrollLeft - (ignoreScrollbarX ? 0 :
// RTL <body> scrollbar.
getWindowScrollBarX(documentElement, htmlRect));
const y = htmlRect.top + scroll.scrollTop;
return {
x,
y
};
}
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
let {
elements,
rect,
offsetParent,
strategy
} = _ref;
const isFixed = strategy === 'fixed';
const documentElement = getDocumentElement(offsetParent);
const topLayer = elements ? isTopLayer(elements.floating) : false;
if (offsetParent === documentElement || topLayer && isFixed) {
return rect;
}
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
let scale = createCoords(1);
const offsets = createCoords(0);
const isOffsetParentAnElement = isHTMLElement(offsetParent);
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent);
scale = getScale(offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
}
}
const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll, true) : createCoords(0);
return {
width: rect.width * scale.x,
height: rect.height * scale.y,
x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,
y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y
};
}
function getClientRects(element) {
return Array.from(element.getClientRects());
}
// Gets the entire size of the scrollable document area, even extending outside
// of the `<html>` and `<body>` rect bounds if horizontally scrollable.
function getDocumentRect(element) {
const html = getDocumentElement(element);
const scroll = getNodeScroll(element);
const body = element.ownerDocument.body;
const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
const y = -scroll.scrollTop;
if (getComputedStyle(body).direction === 'rtl') {
x += max(html.clientWidth, body.clientWidth) - width;
}
return {
width,
height,
x,
y
};
}
function getViewportRect(element, strategy) {
const win = getWindow(element);
const html = getDocumentElement(element);
const visualViewport = win.visualViewport;
let width = html.clientWidth;
let height = html.clientHeight;
let x = 0;
let y = 0;
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height;
const visualViewportBased = isWebKit();
if (!visualViewportBased || visualViewportBased && strategy === 'fixed') {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width,
height,
x,
y
};
}
// Returns the inner client rect, subtracting scrollbars if present.
function getInnerBoundingClientRect(element, strategy) {
const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
const top = clientRect.top + element.clientTop;
const left = clientRect.left + element.clientLeft;
const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);
const width = element.clientWidth * scale.x;
const height = element.clientHeight * scale.y;
const x = left * scale.x;
const y = top * scale.y;
return {
width,
height,
x,
y
};
}
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
let rect;
if (clippingAncestor === 'viewport') {
rect = getViewportRect(element, strategy);
} else if (clippingAncestor === 'document') {
rect = getDocumentRect(getDocumentElement(element));
} else if (isElement(clippingAncestor)) {
rect = getInnerBoundingClientRect(clippingAncestor, strategy);
} else {
const visualOffsets = getVisualOffsets(element);
rect = {
x: clippingAncestor.x - visualOffsets.x,
y: clippingAncestor.y - visualOffsets.y,
width: clippingAncestor.width,
height: clippingAncestor.height
};
}
return rectToClientRect(rect);
}
function hasFixedPositionAncestor(element, stopNode) {
const parentNode = getParentNode(element);
if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
return false;
}
return getComputedStyle(parentNode).position === 'fixed' || hasFixedPositionAncestor(parentNode, stopNode);
}
// A "clipping ancestor" is an `overflow` element with the characteristic of
// clipping (or hiding) child elements. This returns all clipping ancestors
// of the given element up the tree.
function getClippingElementAncestors(element, cache) {
const cachedResult = cache.get(element);
if (cachedResult) {
return cachedResult;
}
let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');
let currentContainingBlockComputedStyle = null;
const elementIsFixed = getComputedStyle(element).position === 'fixed';
let currentNode = elementIsFixed ? getParentNode(element) : element;
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
const computedStyle = getComputedStyle(currentNode);
const currentNodeIsContaining = isContainingBlock(currentNode);
if (!currentNodeIsContaining && computedStyle.position === 'fixed') {
currentContainingBlockComputedStyle = null;
}
const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
if (shouldDropCurrentNode) {
// Drop non-containing blocks.
result = result.filter(ancestor => ancestor !== currentNode);
} else {
// Record last containing block for next iteration.
currentContainingBlockComputedStyle = computedStyle;
}
currentNode = getParentNode(currentNode);
}
cache.set(element, result);
return result;
}
// Gets the maximum area that the element is visible in due to any number of
// clipping ancestors.
function getClippingRect(_ref) {
let {
element,
boundary,
rootBoundary,
strategy
} = _ref;
const elementClippingAncestors = boundary === 'clippingAncestors' ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
const firstClippingAncestor = clippingAncestors[0];
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
accRect.top = max(rect.top, accRect.top);
accRect.right = min(rect.right, accRect.right);
accRect.bottom = min(rect.bottom, accRect.bottom);
accRect.left = max(rect.left, accRect.left);
return accRect;
}, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
return {
width: clippingRect.right - clippingRect.left,
height: clippingRect.bottom - clippingRect.top,
x: clippingRect.left,
y: clippingRect.top
};
}
function getDimensions(element) {
const {
width,
height
} = getCssDimensions(element);
return {
width,
height
};
}
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const isFixed = strategy === 'fixed';
const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offsets = createCoords(0);
// If the <body> scrollbar appears on the left (e.g. RTL systems). Use
// Firefox with layout.scrollbar.side = 3 in about:config to test this.
function setLeftRTLScrollbarOffset() {
offsets.x = getWindowScrollBarX(documentElement);
}
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isOffsetParentAnElement) {
const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
} else if (documentElement) {
setLeftRTLScrollbarOffset();
}
}
if (isFixed && !isOffsetParentAnElement && documentElement) {
setLeftRTLScrollbarOffset();
}
const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;
const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;
return {
x,
y,
width: rect.width,
height: rect.height
};
}
function isStaticPositioned(element) {
return getComputedStyle(element).position === 'static';
}
function getTrueOffsetParent(element, polyfill) {
if (!isHTMLElement(element) || getComputedStyle(element).position === 'fixed') {
return null;
}
if (polyfill) {
return polyfill(element);
}
let rawOffsetParent = element.offsetParent;
// Firefox returns the <html> element as the offsetParent if it's non-static,
// while Chrome and Safari return the <body> element. The <body> element must
// be used to perform the correct calculations even if the <html> element is
// non-static.
if (getDocumentElement(element) === rawOffsetParent) {
rawOffsetParent = rawOffsetParent.ownerDocument.body;
}
return rawOffsetParent;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent(element, polyfill) {
const win = getWindow(element);
if (isTopLayer(element)) {
return win;
}
if (!isHTMLElement(element)) {
let svgOffsetParent = getParentNode(element);
while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {
if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {
return svgOffsetParent;
}
svgOffsetParent = getParentNode(svgOffsetParent);
}
return win;
}
let offsetParent = getTrueOffsetParent(element, polyfill);
while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {
offsetParent = getTrueOffsetParent(offsetParent, polyfill);
}
if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {
return win;
}
return offsetParent || getContainingBlock(element) || win;
}
const getElementRects = async function (data) {
const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
const getDimensionsFn = this.getDimensions;
const floatingDimensions = await getDimensionsFn(data.floating);
return {
reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),
floating: {
x: 0,
y: 0,
width: floatingDimensions.width,
height: floatingDimensions.height
}
};
};
function isRTL(element) {
return getComputedStyle(element).direction === 'rtl';
}
const platform = {
convertOffsetParentRelativeRectToViewportRelativeRect,
getDocumentElement,
getClippingRect,
getOffsetParent,
getElementRects,
getClientRects,
getDimensions,
getScale,
isElement,
isRTL
};
function rectsAreEqual(a, b) {
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
}
// https://samthor.au/2021/observing-dom/
function observeMove(element, onMove) {
let io = null;
let timeoutId;
const root = getDocumentElement(element);
function cleanup() {
var _io;
clearTimeout(timeoutId);
(_io = io) == null || _io.disconnect();
io = null;
}
function refresh(skip, threshold) {
if (skip === void 0) {
skip = false;
}
if (threshold === void 0) {
threshold = 1;
}
cleanup();
const elementRectForRootMargin = element.getBoundingClientRect();
const {
left,
top,
width,
height
} = elementRectForRootMargin;
if (!skip) {
onMove();
}
if (!width || !height) {
return;
}
const insetTop = floor(top);
const insetRight = floor(root.clientWidth - (left + width));
const insetBottom = floor(root.clientHeight - (top + height));
const insetLeft = floor(left);
const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px";
const options = {
rootMargin,
threshold: max(0, min(1, threshold)) || 1
};
let isFirstUpdate = true;
function handleObserve(entries) {
const ratio = entries[0].intersectionRatio;
if (ratio !== threshold) {
if (!isFirstUpdate) {
return refresh();
}
if (!ratio) {
// If the reference is clipped, the ratio is 0. Throttle the refresh
// to prevent an infinite loop of updates.
timeoutId = setTimeout(() => {
refresh(false, 1e-7);
}, 1000);
} else {
refresh(false, ratio);
}
}
if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {
// It's possible that even though the ratio is reported as 1, the
// element is not actually fully within the IntersectionObserver's root
// area anymore. This can happen under performance constraints. This may
// be a bug in the browser's IntersectionObserver implementation. To
// work around this, we compare the element's bounding rect now with
// what it was at the time we created the IntersectionObserver. If they
// are not equal then the element moved, so we refresh.
refresh();
}
isFirstUpdate = false;
}
// Older browsers don't support a `document` as the root and will throw an
// error.
try {
io = new IntersectionObserver(handleObserve, {
...options,
// Handle <iframe>s
root: root.ownerDocument
});
} catch (_e) {
io = new IntersectionObserver(handleObserve, options);
}
io.observe(element);
}
refresh(true);
return cleanup;
}
/**
* Automatically updates the position of the floating element when necessary.
* Should only be called when the floating element is mounted on the DOM or
* visible on the screen.
* @returns cleanup function that should be invoked when the floating element is
* removed from the DOM or hidden from the screen.
* @see https://floating-ui.com/docs/autoUpdate
*/
function autoUpdate(reference, floating, update, options) {
if (options === void 0) {
options = {};
}
const {
ancestorScroll = true,
ancestorResize = true,
elementResize = typeof ResizeObserver === 'function',
layoutShift = typeof IntersectionObserver === 'function',
animationFrame = false
} = options;
const referenceEl = unwrapElement(reference);
const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...getOverflowAncestors(floating)] : [];
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.addEventListener('scroll', update, {
passive: true
});
ancestorResize && ancestor.addEventListener('resize', update);
});
const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update) : null;
let reobserveFrame = -1;
let resizeObserver = null;
if (elementResize) {
resizeObserver = new ResizeObserver(_ref => {
let [firstEntry] = _ref;
if (firstEntry && firstEntry.target === referenceEl && resizeObserver) {
// Prevent update loops when using the `size` middleware.
// https://github.com/floating-ui/floating-ui/issues/1740
resizeObserver.unobserve(floating);
cancelAnimationFrame(reobserveFrame);
reobserveFrame = requestAnimationFrame(() => {
var _resizeObserver;
(_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);
});
}
update();
});
if (referenceEl && !animationFrame) {
resizeObserver.observe(referenceEl);
}
resizeObserver.observe(floating);
}
let frameId;
let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
if (animationFrame) {
frameLoop();
}
function frameLoop() {
const nextRefRect = getBoundingClientRect(reference);
if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {
update();
}
prevRefRect = nextRefRect;
frameId = requestAnimationFrame(frameLoop);
}
update();
return () => {
var _resizeObserver2;
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.removeEventListener('scroll', update);
ancestorResize && ancestor.removeEventListener('resize', update);
});
cleanupIo == null || cleanupIo();
(_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();
resizeObserver = null;
if (animationFrame) {
cancelAnimationFrame(frameId);
}
};
}
/**
* Resolves with an object of overflow side offsets that determine how much the
* element is overflowing a given clipping boundary on each side.
* - positive = overflowing the boundary by that number of pixels
* - negative = how many pixels left before it will overflow
* - 0 = lies flush with the boundary
* @see https://floating-ui.com/docs/detectOverflow
*/
const detectOverflow = detectOverflow$1;
/**
* Modifies the placement by translating the floating element along the
* specified axes.
* A number (shorthand for `mainAxis` or distance), or an axes configuration
* object may be passed.
* @see https://floating-ui.com/docs/offset
*/
const offset = offset$1;
/**
* Optimizes the visibility of the floating element by choosing the placement
* that has the most space available automatically, without needing to specify a
* preferred placement. Alternative to `flip`.
* @see https://floating-ui.com/docs/autoPlacement
*/
const autoPlacement = autoPlacement$1;
/**
* Optimizes the visibility of the floating element by shifting it in order to
* keep it in view when it will overflow the clipping boundary.
* @see https://floating-ui.com/docs/shift
*/
const shift = shift$1;
/**
* Optimizes the visibility of the floating element by flipping the `placement`
* in order to keep it in view when the preferred placement(s) will overflow the
* clipping boundary. Alternative to `autoPlacement`.
* @see https://floating-ui.com/docs/flip
*/
const flip = flip$1;
/**
* Provides data that allows you to change the size of the floating element —
* for instance, prevent it from overflowing the clipping boundary or match the
* width of the reference element.
* @see https://floating-ui.com/docs/size
*/
const size = size$1;
/**
* Provides data to hide the floating element in applicable situations, such as
* when it is not in the same clipping context as the reference element.
* @see https://floating-ui.com/docs/hide
*/
const hide = hide$1;
/**
* Provides data to position an inner element of the floating element so that it
* appears centered to the reference element.
* @see https://floating-ui.com/docs/arrow
*/
const arrow = arrow$1;
/**
* Provides improved positioning for inline reference elements that can span
* over multiple lines, such as hyperlinks or range selections.
* @see https://floating-ui.com/docs/inline
*/
const inline = inline$1;
/**
* Built-in `limiter` that will stop `shift()` at a certain point.
*/
const limitShift = limitShift$1;
/**
* Computes the `x` and `y` coordinates that will place the floating element
* next to a given reference element.
*/
const computePosition = (reference, floating, options) => {
// This caches the expensive `getClippingElementAncestors` function so that
// multiple lifecycle resets re-use the same result. It only lives for a
// single call. If other functions become expensive, we can add them as well.
const cache = new Map();
const mergedOptions = {
platform,
...options
};
const platformWithCache = {
...mergedOptions.platform,
_c: cache
};
return computePosition$1(reference, floating, {
...mergedOptions,
platform: platformWithCache
});
};
export { arrow, autoPlacement, autoUpdate, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, platform, shift, size };

View File

@@ -0,0 +1,154 @@
function hasWindow() {
return typeof window !== 'undefined';
}
function getNodeName(node) {
if (isNode(node)) {
return (node.nodeName || '').toLowerCase();
}
// Mocked nodes in testing environments may not be instances of Node. By
// returning `#document` an infinite loop won't occur.
// https://github.com/floating-ui/floating-ui/issues/2317
return '#document';
}
function getWindow(node) {
var _node$ownerDocument;
return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getDocumentElement(node) {
var _ref;
return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
}
function isNode(value) {
if (!hasWindow()) {
return false;
}
return value instanceof Node || value instanceof getWindow(value).Node;
}
function isElement(value) {
if (!hasWindow()) {
return false;
}
return value instanceof Element || value instanceof getWindow(value).Element;
}
function isHTMLElement(value) {
if (!hasWindow()) {
return false;
}
return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
}
function isShadowRoot(value) {
if (!hasWindow() || typeof ShadowRoot === 'undefined') {
return false;
}
return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
}
function isOverflowElement(element) {
const {
overflow,
overflowX,
overflowY,
display
} = getComputedStyle(element);
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
}
function isTableElement(element) {
return ['table', 'td', 'th'].includes(getNodeName(element));
}
function isTopLayer(element) {
return [':popover-open', ':modal'].some(selector => {
try {
return element.matches(selector);
} catch (e) {
return false;
}
});
}
function isContainingBlock(elementOrCss) {
const webkit = isWebKit();
const css = isElement(elementOrCss) ? getComputedStyle(elementOrCss) : elementOrCss;
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
// https://drafts.csswg.org/css-transforms-2/#individual-transforms
return ['transform', 'translate', 'scale', 'rotate', 'perspective'].some(value => css[value] ? css[value] !== 'none' : false) || (css.containerType ? css.containerType !== 'normal' : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== 'none' : false) || !webkit && (css.filter ? css.filter !== 'none' : false) || ['transform', 'translate', 'scale', 'rotate', 'perspective', 'filter'].some(value => (css.willChange || '').includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => (css.contain || '').includes(value));
}
function getContainingBlock(element) {
let currentNode = getParentNode(element);
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
if (isContainingBlock(currentNode)) {
return currentNode;
} else if (isTopLayer(currentNode)) {
return null;
}
currentNode = getParentNode(currentNode);
}
return null;
}
function isWebKit() {
if (typeof CSS === 'undefined' || !CSS.supports) return false;
return CSS.supports('-webkit-backdrop-filter', 'none');
}
function isLastTraversableNode(node) {
return ['html', 'body', '#document'].includes(getNodeName(node));
}
function getComputedStyle(element) {
return getWindow(element).getComputedStyle(element);
}
function getNodeScroll(element) {
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.scrollX,
scrollTop: element.scrollY
};
}
function getParentNode(node) {
if (getNodeName(node) === 'html') {
return node;
}
const result =
// Step into the shadow DOM of the parent of a slotted node.
node.assignedSlot ||
// DOM Element detected.
node.parentNode ||
// ShadowRoot detected.
isShadowRoot(node) && node.host ||
// Fallback.
getDocumentElement(node);
return isShadowRoot(result) ? result.host : result;
}
function getNearestOverflowAncestor(node) {
const parentNode = getParentNode(node);
if (isLastTraversableNode(parentNode)) {
return node.ownerDocument ? node.ownerDocument.body : node.body;
}
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
return parentNode;
}
return getNearestOverflowAncestor(parentNode);
}
function getOverflowAncestors(node, list, traverseIframes) {
var _node$ownerDocument2;
if (list === void 0) {
list = [];
}
if (traverseIframes === void 0) {
traverseIframes = true;
}
const scrollableAncestor = getNearestOverflowAncestor(node);
const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
const win = getWindow(scrollableAncestor);
if (isBody) {
const frameElement = getFrameElement(win);
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
}
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
}
function getFrameElement(win) {
return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;
}
export { getComputedStyle, getContainingBlock, getDocumentElement, getFrameElement, getNearestOverflowAncestor, getNodeName, getNodeScroll, getOverflowAncestors, getParentNode, getWindow, isContainingBlock, isElement, isHTMLElement, isLastTraversableNode, isNode, isOverflowElement, isShadowRoot, isTableElement, isTopLayer, isWebKit };

View File

@@ -0,0 +1,138 @@
/**
* Custom positioning reference element.
* @see https://floating-ui.com/docs/virtual-elements
*/
const sides = ['top', 'right', 'bottom', 'left'];
const alignments = ['start', 'end'];
const placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + "-" + alignments[0], side + "-" + alignments[1]), []);
const min = Math.min;
const max = Math.max;
const round = Math.round;
const floor = Math.floor;
const createCoords = v => ({
x: v,
y: v
});
const oppositeSideMap = {
left: 'right',
right: 'left',
bottom: 'top',
top: 'bottom'
};
const oppositeAlignmentMap = {
start: 'end',
end: 'start'
};
function clamp(start, value, end) {
return max(start, min(value, end));
}
function evaluate(value, param) {
return typeof value === 'function' ? value(param) : value;
}
function getSide(placement) {
return placement.split('-')[0];
}
function getAlignment(placement) {
return placement.split('-')[1];
}
function getOppositeAxis(axis) {
return axis === 'x' ? 'y' : 'x';
}
function getAxisLength(axis) {
return axis === 'y' ? 'height' : 'width';
}
function getSideAxis(placement) {
return ['top', 'bottom'].includes(getSide(placement)) ? 'y' : 'x';
}
function getAlignmentAxis(placement) {
return getOppositeAxis(getSideAxis(placement));
}
function getAlignmentSides(placement, rects, rtl) {
if (rtl === void 0) {
rtl = false;
}
const alignment = getAlignment(placement);
const alignmentAxis = getAlignmentAxis(placement);
const length = getAxisLength(alignmentAxis);
let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
if (rects.reference[length] > rects.floating[length]) {
mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
}
return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
}
function getExpandedPlacements(placement) {
const oppositePlacement = getOppositePlacement(placement);
return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
}
function getOppositeAlignmentPlacement(placement) {
return placement.replace(/start|end/g, alignment => oppositeAlignmentMap[alignment]);
}
function getSideList(side, isStart, rtl) {
const lr = ['left', 'right'];
const rl = ['right', 'left'];
const tb = ['top', 'bottom'];
const bt = ['bottom', 'top'];
switch (side) {
case 'top':
case 'bottom':
if (rtl) return isStart ? rl : lr;
return isStart ? lr : rl;
case 'left':
case 'right':
return isStart ? tb : bt;
default:
return [];
}
}
function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
const alignment = getAlignment(placement);
let list = getSideList(getSide(placement), direction === 'start', rtl);
if (alignment) {
list = list.map(side => side + "-" + alignment);
if (flipAlignment) {
list = list.concat(list.map(getOppositeAlignmentPlacement));
}
}
return list;
}
function getOppositePlacement(placement) {
return placement.replace(/left|right|bottom|top/g, side => oppositeSideMap[side]);
}
function expandPaddingObject(padding) {
return {
top: 0,
right: 0,
bottom: 0,
left: 0,
...padding
};
}
function getPaddingObject(padding) {
return typeof padding !== 'number' ? expandPaddingObject(padding) : {
top: padding,
right: padding,
bottom: padding,
left: padding
};
}
function rectToClientRect(rect) {
const {
x,
y,
width,
height
} = rect;
return {
width,
height,
top: y,
left: x,
right: x + width,
bottom: y + height,
x,
y
};
}
export { alignments, clamp, createCoords, evaluate, expandPaddingObject, floor, getAlignment, getAlignmentAxis, getAlignmentSides, getAxisLength, getExpandedPlacements, getOppositeAlignmentPlacement, getOppositeAxis, getOppositeAxisPlacements, getOppositePlacement, getPaddingObject, getSide, getSideAxis, max, min, placements, rectToClientRect, round, sides };

View File

@@ -0,0 +1,14 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
const APP_EVENT_PAGE_INTENT_KIND = "AppEventPageIntent";
export function makeAppEventPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
$kind: APP_EVENT_PAGE_INTENT_KIND,
platform: normalizePreviewPlaform(platform),
};
}
export function isAppEventPageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === APP_EVENT_PAGE_INTENT_KIND;
}
//# sourceMappingURL=app-event-page-intent.js.map

View File

@@ -0,0 +1,10 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
export function makeArcadeGroupingPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: "ArcadeGroupingPageIntent",
};
}
//# sourceMappingURL=arcade-grouping-page-intent.js.map

View File

@@ -0,0 +1,16 @@
import { normalizeAdamID } from "../util";
import { normalizePreviewPlaform } from "../models/preview-platform";
export function makeBundlePageIntent(opts) {
const { id, platform, lic, ...rest } = opts;
return {
$kind: "BundlePageIntent",
id: normalizeAdamID(id),
platform: normalizePreviewPlaform(platform),
lic,
...rest,
};
}
export function isBundlePageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === "BundlePageIntent";
}
//# sourceMappingURL=bundle-page-intent.js.map

View File

@@ -0,0 +1,8 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
export function makeCategoryTabsIntent(opts) {
return {
$kind: "CategoryTabsIntent",
platform: normalizePreviewPlaform(opts.platform),
};
}
//# sourceMappingURL=category-tabs-intent.js.map

View File

@@ -0,0 +1,13 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
/**
* Create a {@linkcode ChartsHubPageIntent}
*/
export function makeChartsHubPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: "ChartsHubPageIntent",
};
}
//# sourceMappingURL=charts-hub-page-intent.js.map

View File

@@ -0,0 +1,13 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
const CHARTS_PAGE_INTENT_KIND = "ChartsPageIntent";
export function makeChartsPageIntent({ platform, ...rest }) {
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: CHARTS_PAGE_INTENT_KIND,
};
}
export function isChartsPageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === CHARTS_PAGE_INTENT_KIND;
}
//# sourceMappingURL=charts-page-intent.js.map

View File

@@ -0,0 +1,19 @@
import { normalizeAdamID } from "../util";
const DEVELOPER_PAGE_INTENT_KIND = "DeveloperPageIntent";
/**
* Determines if {@linkcode intent} is a {@linkcode DeveloperPageIntent}
*/
export function isDeveloperPageIntent(intent) {
return intent.$kind === DEVELOPER_PAGE_INTENT_KIND;
}
/**
* Creates a {@linkcode DeveloperPageIntent}
*/
export function makeDeveloperPageIntent({ id, ...rest }) {
return {
...rest,
id: normalizeAdamID(id),
$kind: DEVELOPER_PAGE_INTENT_KIND,
};
}
//# sourceMappingURL=developer-page-intent.js.map

View File

@@ -0,0 +1,37 @@
import { normalizePreviewPlaform } from "../../models/preview-platform";
export function isEditorialPageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === "EditorialPageIntent";
}
export function isEditorialPageIntentByID(intent) {
return isEditorialPageIntent(intent) && "id" in intent;
}
export function isEditorialPageIntentByName(intent) {
return isEditorialPageIntent(intent) && "name" in intent;
}
/**
* Creates an {@link EditorialPageIntent} that fetches an Editorial Page by "name"
*
* @param options the properties of the created {@link EditorialPageIntentByName}
*/
export function makeEditorialPageIntentByName(options) {
const { platform, ...rest } = options;
return {
...rest,
$kind: "EditorialPageIntent",
platform: normalizePreviewPlaform(platform),
};
}
/**
* Creates an {@link EditorialPageIntent} that fetches an Editorial Page by "id"
*
* @param options the properties of the created {@linkcode EditorialPageIntentById}
*/
export function makeEditorialPageIntentByID(options) {
const { platform, ...rest } = options;
return {
$kind: "EditorialPageIntent",
platform: normalizePreviewPlaform(platform),
...rest,
};
}
//# sourceMappingURL=editorial-page-intent.js.map

View File

@@ -0,0 +1,14 @@
import { normalizePreviewPlaform } from "../../models/preview-platform";
const EDITORIAL_SHELF_COLLECTION_PAGE_INTENT_KIND = "EditorialShelfCollectionPageIntent";
export function isEditorialShelfCollectionPageIntent(intent) {
return intent.$kind === EDITORIAL_SHELF_COLLECTION_PAGE_INTENT_KIND;
}
export function makeEditorialShelfCollectionPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
$kind: EDITORIAL_SHELF_COLLECTION_PAGE_INTENT_KIND,
platform: normalizePreviewPlaform(platform),
};
}
//# sourceMappingURL=editorial-shelf-collection-page-intent.js.map

View File

@@ -0,0 +1,19 @@
const EULA_PAGE_INTENT_KIND = "EulaPageIntent";
/**
* Determines if {@linkcode intent} is a {@linkcode EulaPageIntent}
*/
export function isEulaPageIntent(intent) {
return intent.$kind === EULA_PAGE_INTENT_KIND;
}
/**
* Creates a {@linkcode EulaPageIntent}
*/
export function makeEulaPageIntent({ resourceId, resourceType, ...rest }) {
return {
...rest,
resourceId,
resourceType,
$kind: EULA_PAGE_INTENT_KIND,
};
}
//# sourceMappingURL=eula-page-intent.js.map

View File

@@ -0,0 +1,33 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
import { normalizeAdamID } from "../util";
export function isGroupingPageIntent(intent) {
return intent.$kind === "GroupingPageIntent";
}
/**
* Creates a {@link GroupingPageIntent} "by name"
*
* @param options the properties of the created {@link GroupingPageIntent}
*/
export function makeGroupingPageIntentByName(options) {
const { platform, ...rest } = options;
return {
...rest,
$kind: "GroupingPageIntent",
platform: normalizePreviewPlaform(platform),
};
}
/**
* Creates a {@link GroupingPageIntent} "by ID"
*
* @param options the properties of the created {@link GroupingPageIntent}
*/
export function makeGroupingPageIntentByID(options) {
const { platform, id, ...rest } = options;
return {
...rest,
$kind: "GroupingPageIntent",
id: normalizeAdamID(id),
platform: normalizePreviewPlaform(platform),
};
}
//# sourceMappingURL=grouping-page-intent.js.map

View File

@@ -0,0 +1,17 @@
import { normalizeAdamID } from "../util";
import { normalizePreviewPlaform } from "../models/preview-platform";
export function makeProductPageIntent(opts) {
const { id, platform, ppid, lic, ...rest } = opts;
return {
...rest,
$kind: "ProductPageIntent",
id: normalizeAdamID(id),
platform: normalizePreviewPlaform(platform),
ppid,
lic,
};
}
export function isProductPageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === "ProductPageIntent";
}
//# sourceMappingURL=product-page-intent.js.map

View File

@@ -0,0 +1,16 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
import { normalizeAdamID } from "../util";
const ROOM_PAGE_INTENT_KIND = "RoomPageIntent";
export function makeRoomPageIntent(opts) {
const { platform, id, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
id: normalizeAdamID(id),
$kind: ROOM_PAGE_INTENT_KIND,
};
}
export function isRoomPageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === ROOM_PAGE_INTENT_KIND;
}
//# sourceMappingURL=room-page-intent.js.map

View File

@@ -0,0 +1,11 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
const ROUTABLE_ARCADE_SEE_ALL_PAGE_INTENT_KIND = "RoutableArcadeSeeAllPageIntent";
export function makeRoutableArcadeSeeAllPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: "RoutableArcadeSeeAllPageIntent",
};
}
//# sourceMappingURL=routable-arcade-see-all-page-intent.js.map

View File

@@ -0,0 +1,13 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
import { normalizeAdamID } from "../util";
const ROUTABLE_ARTICLE_PAGE_INTENT_KIND = "RoutableArticlePageIntent";
export function makeRoutableArticlePageIntent(opts) {
const { id, platform, ...rest } = opts;
return {
...rest,
id: normalizeAdamID(id),
platform: normalizePreviewPlaform(platform),
$kind: ROUTABLE_ARTICLE_PAGE_INTENT_KIND,
};
}
//# sourceMappingURL=routable-article-page-intent.js.map

View File

@@ -0,0 +1,4 @@
export function isRoutableIntent(intent) {
return "storefront" in intent && "language" in intent;
}
//# sourceMappingURL=routable-intent.js.map

View File

@@ -0,0 +1,10 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
export function makeRoutableTodayPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: "RoutableTodayPageIntent",
};
}
//# sourceMappingURL=routable-today-page-intent.js.map

View File

@@ -0,0 +1,19 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
export function makeSearchResultsPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: "SearchResultsPageIntent",
};
}
export function makeSearchResultsPageIntentFromURLParams(opts) {
return makeSearchResultsPageIntent({
...opts,
origin: "userText",
});
}
export function isSearchResultsPageIntent(intent) {
return intent.$kind === "SearchResultsPageIntent";
}
//# sourceMappingURL=search-results-page-intent.js.map

View File

@@ -0,0 +1,14 @@
import { normalizePreviewPlaform } from "../../models/preview-platform";
const SEARCH_LANDING_PAGE_INTENT_KIND = "SearchLandingPageIntent";
export function makeSearchLandingPageIntent(opts) {
const { platform, ...rest } = opts;
return {
...rest,
platform: normalizePreviewPlaform(platform),
$kind: SEARCH_LANDING_PAGE_INTENT_KIND,
};
}
export function isSearchLandingPageIntent(intent) {
return (intent === null || intent === void 0 ? void 0 : intent.$kind) === SEARCH_LANDING_PAGE_INTENT_KIND;
}
//# sourceMappingURL=search-landing-page-intent.js.map

View File

@@ -0,0 +1,13 @@
import { normalizePreviewPlaform } from "../models/preview-platform";
import { normalizeAdamID } from "../util";
export const SEE_ALL_TYPES = ["reviews", "customers-also-bought-apps", "developer-other-apps"];
export function makeSeeAllPageIntent(opts) {
return {
...opts,
"$kind": "SeeAllPageIntent",
"id": normalizeAdamID(opts.id),
"platform": normalizePreviewPlaform(opts.platform),
"see-all": opts["see-all"],
};
}
//# sourceMappingURL=see-all-page-intent.js.map

View File

@@ -0,0 +1,37 @@
import * as models from "./index";
/**
* @public
* A model that represents an accessibility feature of a product.
*/
export class AccessibilityFeature extends models.Model {
constructor(title, description, artwork) {
super();
this.title = title;
this.description = description;
this.artwork = artwork;
}
}
/**
* @public
* A model that represents a set of accessibility features of a product.
*/
export class AccessibilityFeatures extends models.ViewModel {
constructor(title, artwork, features) {
super();
this.title = title;
this.artwork = artwork;
this.features = features;
}
}
/**
* @public
* A model that represents a paragraph of linkable text, displayed in the accessibility section.
*/
export class AccessibilityParagraph extends models.ViewModel {
constructor(text, actions) {
super();
this.text = text;
this.actions = actions;
}
}
//# sourceMappingURL=accessibility.js.map

View File

@@ -0,0 +1,525 @@
/**
* Created by km on 2/13/17.
*/
import { isSome } from "@jet/environment/types/optional";
import * as base from "../base";
import { Action } from "./base-action";
import { ActionMetrics } from "../metrics/metrics";
/** @public */
export class TabChangeAction extends Action {
constructor(navigationTab) {
super("TabChangeAction");
this.navigationTab = navigationTab;
this.actions = [];
this.popToRoot = false;
}
}
/** @public */
export class TabBadgeAction extends Action {
constructor(navigationTab, text) {
super("TabBadgeAction");
this.navigationTab = navigationTab;
this.text = text;
}
}
/** @public */
export class ExternalUrlAction extends Action {
constructor(url, isSensitive = true, actionMetrics = new ActionMetrics()) {
super("ExternalUrlAction", actionMetrics);
this.url = url;
this.isSensitive = isSensitive;
}
}
/** @public */
export class CompoundAction extends Action {
constructor(actions) {
super("CompoundAction");
const sanitizedActions = [];
for (const action of actions) {
if (isSome(action.actionMetrics)) {
this.actionMetrics.addManyMetricsData(action.actionMetrics.data);
Object.assign(this.actionMetrics.custom, action.actionMetrics.custom);
action.actionMetrics.clearAll();
}
sanitizedActions.push(action);
}
this.actions = sanitizedActions;
}
}
/** @public */
export class OfferAction extends Action {
constructor(title, adamId, purchaseConfiguration, parentAdamId) {
super("OfferAction");
this.title = title;
this.adamId = adamId;
this.parentAdamId = parentAdamId;
this.purchaseConfiguration = purchaseConfiguration;
if (purchaseConfiguration) {
this.bundleId = purchaseConfiguration.bundleId;
this.lineItem = purchaseConfiguration.lineItem;
}
this.includeBetaApps = false;
}
}
/** @public */
export class OfferConfirmationAction extends Action {
constructor(buyAction, confirmationInitiationAction) {
super("OfferConfirmationAction");
this.buyAction = buyAction;
this.confirmationInitiationAction = confirmationInitiationAction;
}
}
/** @public */
export class OfferAlertAction extends Action {
constructor() {
super("OfferAlertAction");
this.title = null;
this.message = null;
this.footerMessage = null;
this.isCancelable = true;
this.shouldCheckForAvailableDiskSpace = false;
this.checkRestrictionsForContentRating = null;
this.remoteControllerRequirement = "NO_BADGE";
this.spatialControllerRequirement = null;
this.shouldCheckForGameController = false;
this.shouldIncludeActiveAccountInFooterMessage = false;
this.shouldPromptForConfirmation = false;
this.completionAction = null;
}
}
/** @public */
export class CancelPreorderAction extends Action {
constructor(preorderAdamId, isArcade) {
super("CancelPreorderAction");
this.preorderAdamId = preorderAdamId;
this.isArcade = isArcade;
}
}
/** @public */
export class InAppPurchaseAction extends Action {
constructor(productIdentifier, appAdamId, appBundleId, installRequiredAction, minimumShortVersionSupportingInAppPurchaseFlow) {
super("InAppPurchaseAction");
this.productIdentifier = productIdentifier;
this.appAdamId = appAdamId;
this.appBundleId = appBundleId;
this.installRequiredAction = installRequiredAction;
this.minimumShortVersionSupportingInAppPurchaseFlow = minimumShortVersionSupportingInAppPurchaseFlow;
}
}
/** @public */
export class OfferStateAction extends Action {
constructor(adamId, defaultAction) {
super("OfferStateAction");
this.title = defaultAction.title;
this.adamId = adamId;
this.defaultAction = defaultAction;
this.includeBetaApps = false;
}
}
/** @public */
export class OpenAppAction extends Action {
constructor(adamId, destination = "app") {
super("OpenAppAction");
this.adamId = adamId;
this.destination = destination;
}
}
/** @public */
export class OpenGamesUIAction extends Action {
constructor(target) {
super("OpenGamesUIAction");
this.target = target;
}
}
/** @public
* Purchase action that contains multiple offers for a set of Arcade games.
* It is used for "Get all" button in Arcade Download/Starter Pack feature
* to make a single purchase via App Store Daemon.
* The action implementation doesn't execute its `offerActions`
* but uses them as data objects, extracting required parameters.
* */
export class ArcadePackOfferAction extends Action {
constructor(offerActions) {
super("ArcadePackOfferAction");
this.offerActions = offerActions;
}
}
/** @public
* An action representing the "selection" of an app, from the offer button.
* The intention is to communicate this app was selected to another process, not actually
* purchase/download/etc.
* Currently used for the browser selection flow.
*/
export class SelectAppAction extends Action {
constructor(adamId) {
super("SelectAppAction");
this.adamId = adamId;
}
}
/** @public */
export class HttpAction extends Action {
constructor(url) {
super("HttpAction");
this.url = url;
this.method = "GET";
this.headers = {};
this.body = null;
this.isStoreRequest = false;
this.needsAuthentication = false;
this.needsMediaToken = false;
this.retryCount = 0;
this.disableCache = false;
this.successAction = null;
this.failureAction = null;
}
}
/** @public */
export class HttpTemplateParameter {
constructor(key, target, inputType, title) {
this.key = key;
this.target = target;
this.inputType = inputType;
this.title = title;
this.isRequired = true;
this.maximumLength = null;
}
}
/** @public */
export class HttpTemplateAction extends Action {
constructor(url) {
super("HttpTemplateAction");
this.message = null;
this.url = url;
this.method = "GET";
this.headers = {};
this.body = null;
this.bodyDictionary = {};
this.parameters = [];
this.isStoreRequest = false;
this.needsAuthentication = false;
this.needsMediaToken = false;
this.retryCount = 0;
this.disableCache = false;
this.successAction = null;
this.failureAction = null;
}
}
/** @public */
export class RateAction extends HttpTemplateAction {
}
export class ShowSettingsAction extends Action {
constructor() {
super("ShowSettingsAction");
}
}
/** @public */
export class WriteReviewAction extends Action {
constructor(adamId, url) {
super("WriteReviewAction");
this.adamId = adamId;
this.url = url;
}
}
/** @public */
export class PageTabChangeAction extends Action {
constructor(selectedTabId, title, actionClass) {
super(actionClass !== null && actionClass !== void 0 ? actionClass : "PageTabChangeAction");
this.selectedTabId = selectedTabId;
this.title = title;
}
}
/** @public */
export class SearchPageSegmentChangeAction extends PageTabChangeAction {
constructor(selectedTabId, selectedTabTitle, switchToOtherSegmentText) {
super(selectedTabId, "SearchPageSegmentChangeAction");
this.switchToOtherSegmentText = switchToOtherSegmentText;
this.title = selectedTabTitle;
}
}
/** @public */
export class GameCenterDashboardAction extends Action {
constructor() {
super("GameCenterDashboardAction");
}
}
/** @public */
export class SheetAction extends Action {
constructor(actions) {
super("SheetAction");
this.actions = actions;
this.isCancelable = false;
this.cancelTitle = null;
this.message = null;
this.style = "default";
this.isCustom = false;
this.destructiveActionIndex = null;
}
}
/** @public */
export class AlertAction extends Action {
constructor(style) {
super("AlertAction");
this.style = style;
this.title = null;
this.message = null;
this.isCancelable = false;
this.cancelTitle = null;
this.cancelAction = null;
this.buttonActions = [];
this.buttonTitles = [];
this.destructiveActionIndex = null;
this.imageName = null;
}
}
/** @public */
export class ScrollingAlertAction extends Action {
constructor(title, message) {
super("ScrollingAlertAction");
this.title = title;
this.message = message;
}
}
/**
* @public
* Describes a unique activity as used in a Share Sheet.
*/
export class ShareSheetActivity extends base.ViewModel {
constructor(activityType, action) {
super();
this.activityType = activityType;
this.action = action;
}
}
/** @public */
export class ShareSheetAction extends Action {
constructor(data, activities, style = "expanded") {
super("ShareSheetAction");
this.data = data;
this.activities = activities;
this.shareSheetStyle = style;
}
}
/** @public */
export class ReportConcernAction extends Action {
constructor(reasons) {
super("ReportConcernAction");
this.reasons = reasons;
}
}
/** @public */
export class ReviewSummaryReportConcernAction extends Action {
constructor(concerns, title, explanation, sendAction) {
super("ReviewSummaryReportConcernAction");
this.concerns = concerns;
this.title = title;
this.explanation = explanation;
this.sendAction = sendAction;
}
}
/** @public */
export class ProductPageScrollAction extends Action {
constructor(section, clicksOnScroll) {
super("ProductPageScrollAction");
this.section = section;
this.clicksOnScroll = clicksOnScroll;
}
}
/** @public */
export class ArcadeAction extends Action {
constructor(productIdentifier, appAdamId, subscriptionToken, postSubscribeAction) {
super("ArcadeAction");
this.productIdentifier = productIdentifier;
this.appAdamId = appAdamId;
this.postSubscribeAction = postSubscribeAction;
this.subscriptionToken = subscriptionToken;
}
}
export class FamilyCircleAction extends Action {
constructor(clientName, eventType, additionalParameters) {
super("FamilyCircleAction");
this.clientName = clientName;
this.eventType = eventType;
this.additionalParameters = additionalParameters;
}
}
/** @public */
export class BlankAction extends Action {
constructor() {
super("BlankAction");
}
}
/** @public */
export class SearchAdAction extends Action {
constructor(action) {
super("SearchAdAction");
this.action = action;
}
}
/** @public */
export class RateLimitedAction extends Action {
constructor(actionKey, primaryAction) {
super("RateLimitedAction");
this.actionKey = actionKey;
this.primaryAction = primaryAction;
}
}
/** @public */
export class ArcadeSubscriptionStateAction extends Action {
constructor(notSubscribedAction, purchasingAction, subscribedAction, unknownAction) {
super("ArcadeSubscriptionStateAction");
this.notSubscribedAction = notSubscribedAction;
this.purchasingAction = purchasingAction;
this.subscribedAction = subscribedAction;
this.unknownAction = unknownAction;
}
}
/**
* @public
* An action for changing the page segment in the Arcade See All Games page.
*/
export class ArcadeSeeAllGamesPageSegmentChangeAction extends Action {
constructor(facet, selectedOption) {
super("ArcadeSeeAllGamesPageSegmentChangeAction");
this.facet = facet;
this.selectedOption = selectedOption;
}
}
/** @public */
export class GameCenterPlayerProfileAction extends Action {
constructor(playerId) {
super("GameCenterPlayerProfileAction");
this.playerId = playerId;
}
}
/** @public */
export class GameCenterAchievementsAction extends Action {
constructor(bundleId) {
super("GameCenterAchievementsAction");
this.bundleId = bundleId;
}
}
/** @public */
export class LegacyGameCenterInvitePlayerAction extends Action {
constructor(contactId) {
super("GameCenterInvitePlayerAction");
this.contactId = contactId;
}
}
/** @public */
export class GameCenterInvitePlayerAction extends Action {
constructor(invitationType) {
super("GameCenterInvitePlayerAction");
this.invitationType = invitationType;
}
}
/** @public */
export class GameCenterDenylistPlayerAction extends Action {
constructor(contactId) {
super("GameCenterDenylistPlayerAction");
this.contactId = contactId;
}
}
/** @public */
export class PageFacetsChangeAction extends Action {
constructor(filterParameter) {
super("PageFacetsChangeAction");
this.filterParameter = filterParameter;
}
}
/** @public */
export class EngagementToggleAction extends Action {
constructor(identifier, value) {
super("EngagementToggleAction");
this.identifier = identifier;
this.value = value;
}
}
/** @public */
export class ShelfBasedPageScrollAction extends Action {
constructor(shelfId, notPurchasedShelfId, purchasedShelfId, adamId, index, clicksOnScroll) {
super("ShelfBasedPageScrollAction");
this.shelfId = shelfId;
this.notPurchasedShelfId = notPurchasedShelfId;
this.purchasedShelfId = purchasedShelfId;
this.adamId = adamId;
this.index = index;
this.clicksOnScroll = clicksOnScroll;
}
}
/** @public */
export class InvalidateAllWidgetsAction extends Action {
constructor() {
super("InvalidateAllWidgetsAction");
}
}
/** @public */
export class AppLaunchTrampolineAction extends Action {
constructor(bundleId, payloadUrl, fallbackAction) {
super("AppLaunchTrampolineAction");
this.bundleId = bundleId;
this.payloadUrl = payloadUrl;
this.fallbackAction = fallbackAction;
}
}
/** @public */
export class CreateCalendarEventAction extends Action {
constructor(startDate, endDate, isAllDay, name, location, notes, url, notAuthorizedAction, availability) {
super("CreateCalendarEventAction");
this.startDate = startDate;
this.endDate = endDate;
this.isAllDay = isAllDay;
this.name = name;
this.location = location;
this.notes = notes;
this.url = url;
this.notAuthorizedAction = notAuthorizedAction;
this.availability = availability;
}
}
/** @public
*
*/
export class CopyTextAction extends Action {
constructor(text) {
super("CopyTextAction");
this.text = text;
}
}
/** @public
*
*/
export class ClearAppUsageDataAction extends Action {
constructor() {
super("ClearAppUsageDataAction");
}
}
/** @public
* An action describing a request to delete all recent searches from on-device storage.
*/
export class ClearSearchHistoryAction extends Action {
constructor() {
super("ClearSearchHistoryAction");
}
}
/**
* @public
* An action describing an ad interaction, that passes data on to native ad instrumentation
* via Ad Platforms frameworks.
*/
export class AdInteractionAction extends Action {
constructor(adActionMetrics) {
super("AdInteractionAction");
this.adActionMetrics = adActionMetrics;
}
}
/**
* @public
* An action describes a begin of crossfire referral flow, that will pass the `ReferrerData` on to native to mark the beginning of crossfire flow.
*/
export class CrossfireReferralAction extends Action {
constructor(referrerData) {
super("CrossfireReferralAction");
this.referrerData = referrerData;
}
}
//# sourceMappingURL=actions.js.map

View File

@@ -0,0 +1,15 @@
import * as base from "../base";
import { ActionMetrics } from "../metrics/metrics";
/** @public */
export class Action extends base.ViewModel {
constructor(actionClass, actionMetrics = new ActionMetrics()) {
super();
this.title = null;
this.artwork = null;
this.presentationStyle = [];
this.actionClass = actionClass;
this.$kind = actionClass;
this.actionMetrics = actionMetrics;
}
}
//# sourceMappingURL=base-action.js.map

View File

@@ -0,0 +1,26 @@
import { Action } from "./base-action";
/** @public */
export class FlowBackAction extends Action {
constructor(dismissal) {
super("FlowBackAction");
this.dismissal = dismissal;
}
}
/** @public */
export class FlowAction extends Action {
constructor(flowPage, pageUrl) {
super("FlowAction");
this.$kind = "flowAction";
this.page = flowPage;
this.pageUrl = pageUrl;
this.pageData = null;
this.referrerData = undefined;
this.presentationContext = "infer";
this.animationBehavior = "infer";
this.origin = "inapp";
}
}
export function isFlowAction(action) {
return action.$kind === "flowAction";
}
//# sourceMappingURL=flow-action.js.map

View File

@@ -0,0 +1,26 @@
/**
* Models for Ad Incidents that may occur while building model.
*/
import { Model } from "./base";
/**
* @public
* Base Incident Type
*/
export class AdIncident extends Model {
constructor(incidentType) {
super();
this.incidentType = incidentType;
}
}
/**
* @public
* Type of Ad Incident where Ad was discarded due to some reason
*/
export class DiscardAdIncident extends AdIncident {
constructor(instanceId, reason) {
super("discard");
this.instanceId = instanceId;
this.reason = reason;
}
}
//# sourceMappingURL=ad-incidents.js.map

View File

@@ -0,0 +1,50 @@
import * as base from "./base";
/**
* The `Annotation` model describes a category of information about an app,
* such as supported languages, app size, content ratings, in-app-purchase
* info, etc.
*
* `Annotations` always have a title and summary, and most also have an `items`
* array of `AnnotationItem`s with more fine-grained details.
*
* @public
*/
export class Annotation extends base.Model {
constructor(title, items, summary, linkAction) {
super();
this.title = title;
this.summary = summary;
this.items = items;
this.items_V3 = [];
this.shouldAlwaysPresentExpanded = false;
this.linkAction = linkAction;
}
}
/**
* A single item used to show more details in an `Annotation`. This could be an
* In-App Purchase, supported language list, a link to an editorial item, etc.
* @public
*/
export class AnnotationItem extends base.Model {
constructor(text, options = {}) {
super();
this.text = text;
this.heading = options.heading;
this.headingArtworks = options.headingArtworks;
this.listText = options.listText;
this.textPairs = options.textPairs;
}
}
/**
* Used on tvOS to visually group related `Annotation` objects into a column.
* @public
*/
export class AnnotationGroup extends base.Model {
constructor(title, annotations, forceExpanded) {
super();
this.title = title;
this.annotations = annotations;
this.forceExpanded = forceExpanded;
}
}
//# sourceMappingURL=annotation.js.map

View File

@@ -0,0 +1,186 @@
import * as base from "./base";
import * as models from "./index";
import * as metrics from "./metrics/metrics";
/** @public */
export class AppEventFormattedDate extends models.Model {
constructor(showLiveIndicator, displayFromDate, displayText, countdownToDate, countdownStringKey) {
super();
this.showLiveIndicator = showLiveIndicator;
this.displayFromDate = displayFromDate;
this.displayText = displayText;
this.countdownToDate = countdownToDate;
this.countdownStringKey = countdownStringKey;
}
}
/** @public */
export var AppEventBadgeKind;
(function (AppEventBadgeKind) {
AppEventBadgeKind["available"] = "available";
AppEventBadgeKind["happening"] = "happening";
AppEventBadgeKind["live"] = "live";
})(AppEventBadgeKind || (AppEventBadgeKind = {}));
export var AppPromotionType;
(function (AppPromotionType) {
AppPromotionType["AppEvent"] = "appEvent";
AppPromotionType["ContingentOffer"] = "contingentOffer";
AppPromotionType["OfferItem"] = "offerItem";
})(AppPromotionType || (AppPromotionType = {}));
/** @public */
export class AppPromotion extends base.ViewModel {
constructor(promotionType) {
super();
this.promotionType = promotionType;
}
}
/** @public */
export class AppEvent extends AppPromotion {
constructor(appEventId, moduleArtwork, moduleVideo, title, subtitle, detail, startDate, endDate, appEventBadgeKind, kind, requirements, lockup, hideLockupWhenNotInstalled, formattedDates, mediaOverlayStyle, includeBorderInDarkMode) {
super(AppPromotionType.AppEvent);
this.appEventId = appEventId;
this.moduleArtwork = moduleArtwork;
this.moduleVideo = moduleVideo;
this.title = title;
this.subtitle = subtitle;
this.detail = detail;
this.startDate = startDate;
this.endDate = endDate;
this.kind = kind;
this.appEventBadgeKind = appEventBadgeKind;
this.requirements = requirements;
this.lockup = lockup;
this.hideLockupWhenNotInstalled = hideLockupWhenNotInstalled;
this.formattedDates = formattedDates;
this.mediaOverlayStyle = mediaOverlayStyle;
this.includeBorderInDarkMode = includeBorderInDarkMode;
this.clickAction = null;
}
}
/** @public */
export class AppEventDetailPage extends models.Model {
constructor(appEvent, artwork, video, shareAction, mediaOverlayStyle, includeBorderInDarkMode) {
super();
this.appEvent = appEvent;
this.artwork = artwork;
this.video = video;
this.shareAction = shareAction;
this.mediaOverlayStyle = mediaOverlayStyle;
this.includeBorderInDarkMode = includeBorderInDarkMode;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
}
}
/**
* Determines if a {@linkcode shelves.Shelf} is actually a {@linkcode AppEventDetailShelf}
*/
export function isAppEventDetailShelf(shelf) {
return shelf.contentType === "appEventDetail";
}
/**
* `Shelf` definition that wraps a single {@linkcode AppEventDetailPage}
*
* This is used to power the "web" client UI for an `app-event` detail page
*
* @public
*/
export class AppEventDetailShelf extends models.Shelf {
constructor(detailPage) {
super("appEventDetail", null, [detailPage]);
}
}
/** @public */
export class AppEventNotificationConfig extends models.Model {
constructor(appEventId, title, detail, artworkUrl, displayTime, scheduledAction, notAuthorizedAction, failureAction, destinationUrl, scheduleClickEvent, cancelScheduleClickEvent) {
super();
this.appEventId = appEventId;
this.title = title;
this.detail = detail;
this.artworkUrl = artworkUrl;
this.displayTime = displayTime;
this.scheduledAction = scheduledAction;
this.notAuthorizedAction = notAuthorizedAction;
this.failureAction = failureAction;
this.destinationUrl = destinationUrl;
this.scheduleClickEvent = scheduleClickEvent;
this.cancelScheduleClickEvent = cancelScheduleClickEvent;
}
}
/** @public */
export class ContingentOffer extends AppPromotion {
constructor(backgroundArtwork, mediaOverlayStyle, isStreamlinedBuy, learnMoreTitle, titleFormatted, subtitle, description, label, badge, additionalInfo, trunkAppIcon, offerLockup) {
super(AppPromotionType.ContingentOffer);
this.backgroundArtwork = backgroundArtwork;
this.isStreamlinedBuy = isStreamlinedBuy;
this.mediaOverlayStyle = mediaOverlayStyle;
this.titleFormatted = titleFormatted;
this.subtitle = subtitle;
this.additionalInfo = additionalInfo;
this.description = description;
this.label = label;
this.badge = badge;
this.offerLockup = offerLockup;
this.trunkAppIcon = trunkAppIcon;
this.learnMoreTitle = learnMoreTitle;
this.clickAction = null;
}
}
/** @public */
export class AppPromotionDetailPage extends base.ViewModel {
constructor(promotionType) {
super();
this.promotionType = promotionType;
}
}
/** @public */
export class ContingentOfferDetailPage extends AppPromotionDetailPage {
constructor(contingentOffer, artwork, mediaOverlayStyle) {
super(AppPromotionType.ContingentOffer);
this.contingentOffer = contingentOffer;
this.artwork = artwork;
this.mediaOverlayStyle = mediaOverlayStyle;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
this.learnMoreActionMetrics = new metrics.ActionMetrics();
this.backButtonActionMetrics = new metrics.ActionMetrics();
this.closeButtonActionMetrics = new metrics.ActionMetrics();
}
}
/** @public
*
*
*/
export class OfferItem extends AppPromotion {
constructor(moduleArtwork, moduleVideo, mediaOverlayStyle, isStreamlinedBuy, titleFormatted, expiryDateFormatted, subtitle, description, badge, endDate, iapArtwork, offerLockup) {
super(AppPromotionType.OfferItem);
this.moduleArtwork = moduleArtwork;
this.moduleVideo = moduleVideo;
this.isStreamlinedBuy = isStreamlinedBuy;
this.mediaOverlayStyle = mediaOverlayStyle;
this.titleFormatted = titleFormatted;
this.description = description;
this.badge = badge;
this.offerLockup = offerLockup;
this.subtitle = subtitle;
this.endDate = endDate;
this.expiryDateFormatted = expiryDateFormatted;
this.iapArtwork = iapArtwork;
this.clickAction = null;
}
}
/** @public */
export class OfferItemDetailPage extends AppPromotionDetailPage {
constructor(offerItem, artwork, video, mediaOverlayStyle, includeBorderInDarkMode, learnMoreTitle, additionalInfo) {
super(AppPromotionType.OfferItem);
this.offerItem = offerItem;
this.artwork = artwork;
this.learnMoreTitle = learnMoreTitle;
this.video = video;
this.mediaOverlayStyle = mediaOverlayStyle;
this.pageMetrics = new metrics.PageMetrics();
this.additionalInfo = additionalInfo;
this.pageRenderMetrics = {};
this.learnMoreActionMetrics = new metrics.ActionMetrics();
this.backButtonActionMetrics = new metrics.ActionMetrics();
this.closeButtonActionMetrics = new metrics.ActionMetrics();
}
}
//# sourceMappingURL=app-promotions.js.map

View File

@@ -0,0 +1,18 @@
/**
* Created by Jellenbogen on 11/19/19.
*/
import * as base from "./base";
/** @public */
export class UpsellBreakout extends base.ViewModel {
constructor(details, offerDisplayProperties, displayProperties, offerButtonAction, buttonCallToAction, artwork, video) {
super();
this.details = details;
this.displayProperties = displayProperties;
this.offerButtonAction = offerButtonAction;
this.buttonCallToAction = buttonCallToAction;
this.offerDisplayProperties = offerDisplayProperties;
this.artwork = artwork;
this.video = video;
}
}
//# sourceMappingURL=arcade-upsell.js.map

View File

@@ -0,0 +1,240 @@
import * as models from "./index";
import * as metrics from "./metrics/metrics";
export function marketingItemContextFromString(context) {
var _a, _b;
if (preprocessor.GAMES_TARGET) {
switch (context) {
case "generic":
return "gameCenterGeneric";
case "groupingLockup":
return "gameCenterLockup";
case "arcadeTabHeader":
return "gameCenterEditorialPageHeader";
case "productPage":
return "gameCenterProductPage";
case "arcadeComingSoon":
return "gameCenterComingSoon";
case "gameCenterEditorialPage": // TBD
return "gameCenterEditorialPage";
default:
return (_a = context) !== null && _a !== void 0 ? _a : "gameCenterGeneric";
}
}
switch (context) {
case "askToBuy":
return "arcadeAskToBuy";
case "generic":
return "arcadeGeneric";
case "groupingLockup":
return "arcadeGroupingLockup";
case "launchRepair":
return "arcadeLaunchRepair";
case "productPage":
return "arcadeProductPage";
case "topShelfATV":
return "arcadeTopShelfATV";
case "topShelfATVClickThrough":
return "arcadeTopShelfATVClickThrough";
case "editorialItem":
return "arcadeTodayCard";
case "editorialItemCanvas":
return "arcadeStoryCanvas";
case "arcadeComingSoon":
return "arcadeComingSoon";
case "arcadeTabHeader":
return "arcadeTabHeader";
case "arcadeTabNavBar":
return "arcadeTabNavBar";
default:
return (_b = context) !== null && _b !== void 0 ? _b : "arcadeGeneric";
}
}
export function isContextualUpsellContext(context) {
if (preprocessor.GAMES_TARGET) {
switch (context) {
case "gameCenterLockup":
case "gameCenterProductPage":
return true;
default:
return false;
}
}
switch (context) {
case "arcadeGroupingLockup":
case "arcadeProductPage":
case "productPage":
case "groupingLockup":
return true;
default:
return false;
}
}
/** @public */
export class ArcadeSubscribePage extends models.Model {
constructor(details, dismissButtonTitle, offerButtonAction, offerDisplayProperties) {
super();
this.details = details;
this.dismissButtonTitle = dismissButtonTitle;
this.offerButtonAction = offerButtonAction;
this.offerDisplayProperties = offerDisplayProperties;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
}
}
/** @public */
export class ArcadeWelcomeItem extends models.Model {
constructor(headline, body, artwork) {
super();
this.headline = headline;
this.body = body;
this.artwork = artwork;
}
}
/** @public */
export class ArcadeWelcomeContent extends models.Model {
constructor(title, subtitle, items, continueAction, familyAction) {
super();
this.title = title;
this.subtitle = subtitle;
this.items = items;
this.continueAction = continueAction;
this.familyAction = familyAction;
}
}
/** @public */
export class ArcadeWelcomePage extends models.Model {
constructor(individualContent, familyMemberContent) {
super();
this.individualContent = individualContent;
this.familyMemberContent = familyMemberContent;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
}
}
/** @public */
export class MarketingItemRequestInfo extends models.Model {
constructor(serviceType, placement, metricsTopic, seed, clientOptions) {
super();
this.serviceType = serviceType;
this.placement = placement;
this.seed = seed;
this.clientOptions = clientOptions;
this.metricsOverlay = { topic: metricsTopic };
}
}
/** @public */
export class DynamicUIRequestInfo extends models.Model {
constructor(metricsTopic, clientOptions) {
super();
this.clientOptions = clientOptions;
this.metricsOverlay = { topic: metricsTopic };
}
}
/** @public */
export class UpsellGridContent extends models.Model {
constructor(primaryIcon, icons) {
super();
this.primaryIcon = primaryIcon;
this.icons = icons;
}
}
/** @public */
export class AppStoreEngagementTask extends models.Model {
constructor(action) {
super();
this.action = action;
}
}
/** @public
* A model for game category button on Arcade download pack screen.
*/
export class ArcadeDownloadPackCategory extends models.ViewModel {
constructor(id, title, artwork, gradientStartColor, gradientEndColor) {
super();
this.id = id;
this.title = title;
this.artwork = artwork;
this.gradientStartColor = gradientStartColor;
this.gradientEndColor = gradientEndColor;
this.selectActionMetrics = new models.ActionMetrics();
this.deselectActionMetrics = new models.ActionMetrics();
}
}
/** @public
* Initial Arcade download pack screen with game categories to select by user.
*/
export class ArcadeDownloadPackCategoriesPage extends models.Model {
constructor(title, categories, maxNumberOfCategoriesToChoose, maxNumberOfCategoriesToChooseTemplate, numberOfChosenCategoriesTemplate, primaryAction, dismissAction) {
super();
this.title = title;
this.categories = categories;
this.maxNumberOfCategoriesToChoose = maxNumberOfCategoriesToChoose;
this.maxNumberOfCategoriesToChooseTemplate = maxNumberOfCategoriesToChooseTemplate;
this.numberOfChosenCategoriesTemplate = numberOfChosenCategoriesTemplate;
this.primaryAction = primaryAction;
this.dismissAction = dismissAction;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
}
}
/** @public
* A lockup model for Arcade download suggestions pack screen with linked Arcade category.
*/
export class ArcadeDownloadPackSuggestion extends models.Model {
constructor(lockup, categoryId) {
super();
this.lockup = lockup;
this.categoryId = categoryId;
}
}
/** @public
* Follow up Arcade download pack screen with the list of suggested games.
*/
export class ArcadeDownloadPackSuggestionsPage extends models.Model {
constructor(title, suggestions, getAllAction, getAllButtonStyle, primaryAction, primaryActionKind, primaryActionSecondaryTitle) {
super();
this.title = title;
this.suggestions = suggestions;
this.getAllAction = getAllAction;
this.getAllButtonStyle = getAllButtonStyle;
this.primaryAction = primaryAction;
this.primaryActionKind = primaryActionKind;
this.primaryActionSecondaryTitle = primaryActionSecondaryTitle;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
}
}
/**
* Matches untyped string with `ArcadeOnboardingSubscriptionStatus` typed value.
* It is used to convert opaque `string` value that is passed across JS bridge from native code.
* @param value `string` value with the same content.
*/
export function arcadeOnboardingSubscriptionStatusFromString(value) {
switch (value) {
case "new":
return "new";
case "existing":
return "existing";
default:
return "unknown";
}
}
/** @public
* Purchase params to use in`ASDPurchaseManager.purchaseBatch` call.
*/
export class BatchPurchaseParams {
constructor(items, commonBuyParams) {
this.items = items;
this.commonBuyParams = commonBuyParams;
}
}
/** @public
* Decorated purchase for a product with a separate buyParams map.
*/
export class BatchPurchaseItem {
constructor(purchase, buyParams) {
this.purchase = purchase;
this.buyParams = buyParams;
}
}
//# sourceMappingURL=arcade.js.map

View File

@@ -0,0 +1,18 @@
/**
* Created by km on 2/13/17.
*/
import * as models from "./base";
import * as metrics from "./metrics/metrics";
/** @public */
export class ArticlePage extends models.Model {
constructor(card, shelfModels, shareAction) {
super();
this.card = card;
this.shelves = shelfModels;
this.shareAction = shareAction;
this.isIncomplete = false;
this.pageMetrics = new metrics.PageMetrics();
this.pageRenderMetrics = {};
}
}
//# sourceMappingURL=article-page.js.map

Some files were not shown because too many files have changed in this diff Show More