1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
import { noContentError, notFoundError } from "./network";
/**
* Validate an untrusted adam ID without contacting the server.
*
* This allows avoiding the work of calling the server with completely bogus
* IDs. It's scoped to web since ID formats can change and web can be updated
* easily (whereas backporting to older native clients is trickier).
*
* @param {AppStoreObjectGraph} objectGraph
* @param {string} id - the Adam ID to validate
* @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise
*/
export function validateAdamId(objectGraph, id) {
if (objectGraph.client.isWeb && !isAdamId(id)) {
throw noContentError();
}
}
/**
* Check if an ID is a valid Adam ID.
*
* @param {string} id - string to validate
* @return {boolean} true if valid, false otherwise
*/
function isAdamId(id) {
// Media API actually validates if the number <= 2^63-1. But doubles in
// JavaScript cannot precisely represent this (the closest number is >2^63)
// so checking this requires BigInt, which might not be available. At the
// end of the day, checking this is likely not worth the complexity. 2^63-1
// is 9223372036854775807 so we just restrict the number of digits.
//
// See: https://github.pie.apple.com/its/amp-enums/blob/f75500b44f871f35ba3ce459a5ff4c9f225e71b0/src/main/java/com/apple/jingle/store/IdSpace.java#L131-L140
// See: https://github.com/google/guava/blob/869a75a1e3ff85d36672a3cd154772dc90c7b3d2/guava/src/com/google/common/primitives/Longs.java#L400-L440
// See: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/Long.html#MAX_VALUE
return /^\d{1,19}$/.test(id);
}
/**
* Validate an untrusted Featured Content ID without contacting the server.
*
* This allows avoiding the work of calling the server with completely bogus
* IDs. It's scoped to web since ID formats can change and web can be updated
* easily (whereas backporting to older native clients is trickier).
*
* @param {AppStoreObjectGraph} objectGraph
* @param {string} id the FC ID to validate
* @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise
*/
export function validateFcId(objectGraph, id) {
if (objectGraph.client.isWeb && !isFcId(id)) {
throw noContentError();
}
}
/**
* Check if an ID is a valid Featured Content (FC) ID.
*
* @param {string} id - string to validate
* @return {boolean} true if valid, false otherwise
*/
function isFcId(id) {
// FcIds are actually Adam IDs under the hood.
// See: https://github.pie.apple.com/its/Jingle/blob/d2d051c9ef2891f72d5f02f5bbbf2d7748afa7b9/MZStoreComponents/src/main/java/com/apple/jingle/app/store/editorial/SFEditorialHelper.java#L293
return isAdamId(id);
}
/**
* Validate an untrusted (Chart) Genre ID without contacting the server.
*
* This allows avoiding the work of calling the server with completely bogus
* IDs. It's scoped to web since ID formats can change and web can be updated
* easily (whereas backporting to older native clients is trickier).
*
* @param {AppStoreObjectGraph} objectGraph
* @param {string} id the genre ID to validate
* @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise
*/
export function validateGenreId(objectGraph, id) {
if (objectGraph.client.isWeb && !isGenreId(id)) {
throw noContentError();
}
}
/**
* Check if an ID is a valid (Chart) Genre ID.
*
* @param {string} id - string to validate
* @return {boolean} true if valid, false otherwise
*/
function isGenreId(id) {
// Genre IDs are Java int (signed 32-bit).
//
// Technically negative is not excluded, but all charts are positive, so
// filter out negative.
//
// See: https://github.pie.apple.com/its/Jingle/blob/main/shared/reference-data-logic/MZReferenceDataLogic/src/main/java/com/apple/jingle/eo/MZGenreService.java#L555
return isPositiveJavaInt(id);
}
/**
* Validate an untrusted Grouping ID without contacting the server.
*
* This allows avoiding the work of calling the server with completely bogus
* IDs. It's scoped to web since ID formats can change and web can be updated
* easily (whereas backporting to older native clients is trickier).
*
* @param {AppStoreObjectGraph} objectGraph
* @param {string} id the grouping ID to validate
* @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise
*/
export function validateGroupingId(objectGraph, id) {
if (objectGraph.client.isWeb && !isGroupingId(id)) {
throw noContentError();
}
}
/**
* Check if an ID is a valid Grouping ID.
*
* @param {string} id - string to validate
* @return {boolean} true if valid, false otherwise
*/
function isGroupingId(id) {
// Grouping IDs are Java int (signed 32-bit)
//
// Technically negative does not fail to parse, but they're all positive in
// practice.
//
// See: https://github.pie.apple.com/its/Jingle/blob/aaccec936f1feed227fd171ae66bb160cf38e497/MZStorePlatform/src/main/java/com/apple/jingle/store/mediaapi/util/SFMediaAPIEditorialUtil.java#L634
return isPositiveJavaInt(id);
}
/**
* Test if a string contains a Java int.
*
* @param {string} s - the string to test
* @returns {boolean} true if the string is a stringified Java int, false otherwise
*/
function isPositiveJavaInt(s) {
// Java int is 32-bit signed. We can check bounds since signed integers are
// exactly representable as doubles (actually up to 2^53 is representable
// exactly).
//
// See: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/Integer.html#MAX_VALUE
return /^\d+$/.test(s) && parseInt(s, 10) <= 2147483647;
}
/**
* Validate an untrusted Editorial Shelf Collection ID without contacting the server.
*
* This allows avoiding the work of calling the server with completely bogus
* IDs. It's scoped to web since ID formats can change and web can be updated
* easily (whereas backporting to older native clients is trickier).
*
* @param {AppStoreObjectGraph} objectGraph
* @param {string} id the editorial shelf collection ID to validate
* @returns {void} undefined if valid, throws a `NetworkError` (204) otherwise
*/
export function validateEditorialShelfCollectionId(objectGraph, id) {
if (objectGraph.client.isWeb && !isEditorialShelfCollectionId(id)) {
throw noContentError();
}
}
/**
* Check if an ID is a valid editorial shelf collection ID.
*
* @param {string} id - string to validate
* @return {boolean} true if valid, false otherwise
*/
function isEditorialShelfCollectionId(id) {
// Ids are prefixed with "eds.". Beyond that they do have a UUID-type
// identifier that follows, but that seems more liable to change.
//
// See: https://github.pie.apple.com/its/amp-enums/blob/f75500b44f871f35ba3ce459a5ff4c9f225e71b0/src/main/java/com/apple/jingle/store/IdSpace.java#L43C5-L43C20
// See: https://github.pie.apple.com/its/amp-enums/blob/f75500b44f871f35ba3ce459a5ff4c9f225e71b0/src/main/java/com/apple/jingle/store/IdSpace.java#L250-L252
return id.startsWith("eds.");
}
/**
* Validates if a request can be performed for `vision` platform content, based on
* the feature flag is disabled.
*
* @param {AppStoreObjectGraph} objectGraph The application's state graph.
* @throws {NetworkError} Throws a 404 error if access is restricted.
* @returns {void}
*/
export function validateNeedsVisionRestriction(objectGraph) {
var _a;
if (objectGraph.client.isWeb &&
((_a = objectGraph.activeIntent) === null || _a === void 0 ? void 0 : _a.previewPlatform) === "vision" &&
!objectGraph.bag.enableVisionPlatform) {
throw notFoundError();
}
}
//# sourceMappingURL=util.js.map
|