@sitepong/fastnode 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -1
- package/dist/actions/index.cjs +891 -72
- package/dist/actions/index.cjs.map +1 -1
- package/dist/actions/index.mjs +892 -73
- package/dist/actions/index.mjs.map +1 -1
- package/dist/apple-session-jUzBrql5.d.cts +122 -0
- package/dist/apple-session-jUzBrql5.d.ts +122 -0
- package/dist/cert/index.cjs +17868 -24
- package/dist/cert/index.cjs.map +1 -1
- package/dist/cert/index.d.cts +5 -4
- package/dist/cert/index.d.ts +5 -4
- package/dist/cert/index.mjs +17853 -7
- package/dist/cert/index.mjs.map +1 -1
- package/dist/{cert-e5GAgAPb.d.ts → cert-RvXmdMke.d.ts} +2 -2
- package/dist/{cert-uLLdb5a7.d.cts → cert-eOoxGMN1.d.cts} +2 -2
- package/dist/cli/index.cjs +1364 -78
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +3 -2
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.mjs +1365 -79
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.cjs +480 -31
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +3 -2
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.mjs +480 -31
- package/dist/client/index.mjs.map +1 -1
- package/dist/deliver/index.cjs +334 -13
- package/dist/deliver/index.cjs.map +1 -1
- package/dist/deliver/index.d.cts +4 -3
- package/dist/deliver/index.d.ts +4 -3
- package/dist/deliver/index.mjs +330 -15
- package/dist/deliver/index.mjs.map +1 -1
- package/dist/{enums-B609j2rb.d.cts → enums-yXNVh3UI.d.cts} +8 -1
- package/dist/{enums-B609j2rb.d.ts → enums-yXNVh3UI.d.ts} +8 -1
- package/dist/gradle/index.cjs +222 -0
- package/dist/gradle/index.cjs.map +1 -0
- package/dist/gradle/index.d.cts +1 -0
- package/dist/gradle/index.d.ts +1 -0
- package/dist/gradle/index.mjs +217 -0
- package/dist/gradle/index.mjs.map +1 -0
- package/dist/gym/index.d.cts +2 -2
- package/dist/gym/index.d.ts +2 -2
- package/dist/index-8djTPW8O.d.ts +104 -0
- package/dist/{index-DrUpRXRG.d.cts → index-B6-MJpO4.d.cts} +1 -1
- package/dist/{index-Clyf4-lu.d.cts → index-BE7bwNff.d.cts} +1 -1
- package/dist/index-BGhV0gS-.d.ts +65 -0
- package/dist/index-BaNcf7jt.d.cts +201 -0
- package/dist/{index-Di4Ic0EE.d.ts → index-BvyZ4uiw.d.ts} +3 -3
- package/dist/{index-CEldrbyz.d.ts → index-C01Rkrir.d.ts} +2 -2
- package/dist/{index-jeI44Q5G.d.ts → index-C9mHBJED.d.ts} +1 -1
- package/dist/{index-BgHagzj1.d.cts → index-CAEiXVWd.d.ts} +9 -1
- package/dist/index-CBf0-NUQ.d.ts +201 -0
- package/dist/index-CXJlvmB4.d.cts +63 -0
- package/dist/index-CXJlvmB4.d.ts +63 -0
- package/dist/{index-BNP7Bpu5.d.ts → index-D1GHEM3c.d.ts} +32 -3
- package/dist/index-DFstwgGK.d.cts +65 -0
- package/dist/{index-DrCOh0Vh.d.cts → index-DGkgRnZp.d.cts} +2 -2
- package/dist/{index-CtydPNxA.d.cts → index-DLJso7Vs.d.cts} +32 -3
- package/dist/{index-BN0iWhuw.d.cts → index-DWOLZNlz.d.cts} +2 -2
- package/dist/{index-BjkOUQcQ.d.cts → index-DfkIz7i0.d.cts} +18 -4
- package/dist/{index-EKU_G1G5.d.ts → index-DjC_gmnL.d.ts} +18 -4
- package/dist/index-DnHQ2uT2.d.cts +104 -0
- package/dist/{index-CdfgEZwh.d.cts → index-DrmS802s.d.cts} +356 -130
- package/dist/index-Dv-Mek07.d.cts +84 -0
- package/dist/index-Dv-Mek07.d.ts +84 -0
- package/dist/{index-DHxi-QPC.d.cts → index-icIH5e5V.d.cts} +3 -3
- package/dist/{index-mddpvrRX.d.ts → index-laE3_wm9.d.cts} +9 -1
- package/dist/{index-BwwpAlk2.d.ts → index-lsuS2R1T.d.ts} +356 -130
- package/dist/{index-DImN9PWc.d.ts → index-mEEB7An6.d.ts} +1 -1
- package/dist/{index-C2YLoFVl.d.ts → index-rTZOdMRE.d.ts} +2 -2
- package/dist/index.cjs +20496 -217
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +278 -22
- package/dist/index.d.ts +278 -22
- package/dist/index.mjs +20489 -220
- package/dist/index.mjs.map +1 -1
- package/dist/lane/index.cjs +891 -72
- package/dist/lane/index.cjs.map +1 -1
- package/dist/lane/index.mjs +892 -73
- package/dist/lane/index.mjs.map +1 -1
- package/dist/match/index.d.cts +6 -5
- package/dist/match/index.d.ts +6 -5
- package/dist/pem/index.d.cts +4 -3
- package/dist/pem/index.d.ts +4 -3
- package/dist/pilot/index.cjs +8 -4
- package/dist/pilot/index.cjs.map +1 -1
- package/dist/pilot/index.d.cts +4 -3
- package/dist/pilot/index.d.ts +4 -3
- package/dist/pilot/index.mjs +10 -6
- package/dist/pilot/index.mjs.map +1 -1
- package/dist/playstore/index.cjs +207 -0
- package/dist/playstore/index.cjs.map +1 -0
- package/dist/playstore/index.d.cts +1 -0
- package/dist/playstore/index.d.ts +1 -0
- package/dist/playstore/index.mjs +203 -0
- package/dist/playstore/index.mjs.map +1 -0
- package/dist/precheck/index.d.cts +4 -3
- package/dist/precheck/index.d.ts +4 -3
- package/dist/privacy/index.cjs +207 -0
- package/dist/privacy/index.cjs.map +1 -0
- package/dist/privacy/index.d.cts +2 -0
- package/dist/privacy/index.d.ts +2 -0
- package/dist/privacy/index.mjs +195 -0
- package/dist/privacy/index.mjs.map +1 -0
- package/dist/produce/index.cjs +38 -18
- package/dist/produce/index.cjs.map +1 -1
- package/dist/produce/index.d.cts +4 -3
- package/dist/produce/index.d.ts +4 -3
- package/dist/produce/index.mjs +38 -19
- package/dist/produce/index.mjs.map +1 -1
- package/dist/review/index.cjs +141 -0
- package/dist/review/index.cjs.map +1 -0
- package/dist/review/index.d.cts +2 -0
- package/dist/review/index.d.ts +2 -0
- package/dist/review/index.mjs +137 -0
- package/dist/review/index.mjs.map +1 -0
- package/dist/sigh/index.d.cts +4 -3
- package/dist/sigh/index.d.ts +4 -3
- package/dist/xcode/index.cjs +1 -1
- package/dist/xcode/index.cjs.map +1 -1
- package/dist/xcode/index.mjs +1 -1
- package/dist/xcode/index.mjs.map +1 -1
- package/package.json +27 -1
- package/dist/index-BbY_H_f6.d.ts +0 -99
- package/dist/index-XsMsVc3O.d.cts +0 -99
package/dist/lane/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import * as x509 from '@peculiar/x509';
|
|
|
2
2
|
import { randomUUID, createHash, webcrypto, randomBytes, pbkdf2Sync, createCipheriv, createDecipheriv } from 'crypto';
|
|
3
3
|
import { spawn, execFile, execFileSync } from 'child_process';
|
|
4
4
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, copyFileSync, mkdtempSync, rmSync, chmodSync } from 'fs';
|
|
5
|
-
import { join, extname, resolve, basename } from 'path';
|
|
5
|
+
import { join, extname, resolve, basename, relative } from 'path';
|
|
6
6
|
import { homedir, tmpdir } from 'os';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { mkdir, writeFile, readFile, readdir, unlink, stat, rm, mkdtemp } from 'fs/promises';
|
|
@@ -153,8 +153,8 @@ async function revokeExpiredCertificates(options) {
|
|
|
153
153
|
return count;
|
|
154
154
|
}
|
|
155
155
|
async function importToKeychain(certData, keychainPath, keychainPassword) {
|
|
156
|
-
const { tmpdir:
|
|
157
|
-
const tempCertPath = join(
|
|
156
|
+
const { tmpdir: tmpdir7 } = await import('os');
|
|
157
|
+
const tempCertPath = join(tmpdir7(), `fastnode-cert-${Date.now()}.cer`);
|
|
158
158
|
await writeFile(tempCertPath, certData);
|
|
159
159
|
try {
|
|
160
160
|
const args = [
|
|
@@ -1723,6 +1723,20 @@ var init_srp = __esm({
|
|
|
1723
1723
|
};
|
|
1724
1724
|
}
|
|
1725
1725
|
});
|
|
1726
|
+
async function appleJson(res, what) {
|
|
1727
|
+
const text = await res.text();
|
|
1728
|
+
try {
|
|
1729
|
+
return JSON.parse(text);
|
|
1730
|
+
} catch {
|
|
1731
|
+
const contentType = (res.headers.get("content-type") ?? "").split(";")[0].trim();
|
|
1732
|
+
const title = /<title>([^<]{1,120})<\/title>/i.exec(text)?.[1]?.trim();
|
|
1733
|
+
const looksHtml = /^\s*<(!doctype|html)/i.test(text) || contentType === "text/html";
|
|
1734
|
+
const page = looksHtml ? "an HTML page" : contentType ? `a ${contentType} response` : "a non-JSON response";
|
|
1735
|
+
throw new Error(
|
|
1736
|
+
`Apple answered the ${what} with ${page} (HTTP ${res.status}${title ? `, "${title}"` : ""}) instead of JSON. Apple is refusing or rate-limiting sign-ins from this server right now; wait a few minutes and try again. If it keeps happening, sign in once at appleid.apple.com to clear any hold on the account.`
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1726
1740
|
function checkPortalResponse(body, session) {
|
|
1727
1741
|
if (!body || typeof body !== "object") return;
|
|
1728
1742
|
if (body.resultCode === 4100) {
|
|
@@ -1799,7 +1813,7 @@ async function beginLogin(config) {
|
|
|
1799
1813
|
}
|
|
1800
1814
|
async function fetchTwoFactorChallenge(pending) {
|
|
1801
1815
|
const r = await fetch(`${AUTH_BASE}`, { headers: buildAuthHeadersFromPending(pending) });
|
|
1802
|
-
const body = await r
|
|
1816
|
+
const body = await appleJson(r, "two-factor challenge");
|
|
1803
1817
|
const trustedPhoneNumbers = (body.trustedPhoneNumbers ?? []).map((p) => ({
|
|
1804
1818
|
id: p.id,
|
|
1805
1819
|
numberWithDialCode: p.numberWithDialCode,
|
|
@@ -1820,7 +1834,8 @@ async function requestSmsCode(input) {
|
|
|
1820
1834
|
body: JSON.stringify({ phoneNumber: { id: input.phoneId }, mode: input.mode ?? "sms" })
|
|
1821
1835
|
});
|
|
1822
1836
|
}
|
|
1823
|
-
async function submitTwoFactorCode(
|
|
1837
|
+
async function submitTwoFactorCode(rawInput) {
|
|
1838
|
+
const input = { ...rawInput, code: rawInput.code.replace(/[\s\u00a0]+/g, "") };
|
|
1824
1839
|
const log = input.onLog ?? (() => {
|
|
1825
1840
|
});
|
|
1826
1841
|
let path;
|
|
@@ -1932,9 +1947,12 @@ async function doSirp(user, password, serviceKey, cookieStr, hashcash) {
|
|
|
1932
1947
|
body: JSON.stringify({ a: aBase64, accountName: user, protocols: ["s2k", "s2k_fo"] }),
|
|
1933
1948
|
redirect: "manual"
|
|
1934
1949
|
});
|
|
1935
|
-
const initBody = await initRes
|
|
1950
|
+
const initBody = await appleJson(initRes, "sign-in init");
|
|
1936
1951
|
if (initBody.serviceErrors) throw new Error(`Apple auth error: ${JSON.stringify(initBody.serviceErrors)}`);
|
|
1937
1952
|
const { iteration: iterations, salt: saltB64, b: bB64, c, protocol } = initBody;
|
|
1953
|
+
if (!iterations || !saltB64 || !bB64 || !c || !protocol) {
|
|
1954
|
+
throw new Error(`Apple's sign-in init answered without a challenge (HTTP ${initRes.status}): ${JSON.stringify(initBody).slice(0, 300)}`);
|
|
1955
|
+
}
|
|
1938
1956
|
const salt = Buffer.from(saltB64, "base64");
|
|
1939
1957
|
const b = Buffer.from(bB64, "base64");
|
|
1940
1958
|
const encryptedPassword = applePbkdf2(password, salt, iterations, 32, protocol);
|
|
@@ -1977,7 +1995,7 @@ async function fetchOlympusSession(session) {
|
|
|
1977
1995
|
const cookieStr = Object.entries(session.cookies).map(([k, v]) => `${k}=${v}`).join("; ");
|
|
1978
1996
|
const res = await fetch(OLYMPUS_SESSION, { headers: { Cookie: cookieStr, Accept: "application/json" } });
|
|
1979
1997
|
if (res.ok) {
|
|
1980
|
-
const body = await res
|
|
1998
|
+
const body = await appleJson(res, "session lookup");
|
|
1981
1999
|
if (body?.user?.emailAddress) session.userEmail = body.user.emailAddress;
|
|
1982
2000
|
if (body?.provider?.providerId) session.teamId = String(body.provider.providerId);
|
|
1983
2001
|
}
|
|
@@ -1989,7 +2007,7 @@ async function fetchContractMessages(session) {
|
|
|
1989
2007
|
headers: { Cookie: cookieStr, Accept: "application/json" }
|
|
1990
2008
|
});
|
|
1991
2009
|
if (!res.ok) return [];
|
|
1992
|
-
const body = await res
|
|
2010
|
+
const body = await appleJson(res, "agreements lookup");
|
|
1993
2011
|
return (body ?? []).map((m) => ({
|
|
1994
2012
|
id: m.id ?? "",
|
|
1995
2013
|
group: m.group ?? "",
|
|
@@ -2000,10 +2018,22 @@ async function fetchContractMessages(session) {
|
|
|
2000
2018
|
return [];
|
|
2001
2019
|
}
|
|
2002
2020
|
}
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2021
|
+
function serviceKeyFromLocation(location) {
|
|
2022
|
+
if (!location) return null;
|
|
2023
|
+
try {
|
|
2024
|
+
const key = new URL(location).searchParams.get("widgetKey")?.trim();
|
|
2025
|
+
return key && /^[0-9a-f]{16,}$/i.test(key) ? key : null;
|
|
2026
|
+
} catch {
|
|
2027
|
+
return null;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
async function fetchServiceKey(fetchImpl = fetch) {
|
|
2031
|
+
const head = await fetchImpl(LOGOUT_URL, { method: "HEAD", redirect: "manual" });
|
|
2032
|
+
const fromRedirect = serviceKeyFromLocation(head.headers.get("location"));
|
|
2033
|
+
if (fromRedirect) return fromRedirect;
|
|
2034
|
+
const res = await fetchImpl(WIDGET_KEY_URL);
|
|
2035
|
+
const body = await appleJson(res, "service key request");
|
|
2036
|
+
if (!body?.authServiceKey) throw new Error("Could not fetch Apple service key: the sign-out redirect carried no widgetKey and the config endpoint had no authServiceKey.");
|
|
2007
2037
|
return body.authServiceKey;
|
|
2008
2038
|
}
|
|
2009
2039
|
function buildAuthHeaders(session) {
|
|
@@ -2051,7 +2081,7 @@ async function defaultPrompt(message) {
|
|
|
2051
2081
|
});
|
|
2052
2082
|
});
|
|
2053
2083
|
}
|
|
2054
|
-
var AUTH_BASE, OLYMPUS_SESSION, WIDGET_KEY_URL, ProgramLicenseAgreementError;
|
|
2084
|
+
var AUTH_BASE, OLYMPUS_SESSION, WIDGET_KEY_URL, ProgramLicenseAgreementError, LOGOUT_URL;
|
|
2055
2085
|
var init_apple_session = __esm({
|
|
2056
2086
|
"src/client/apple-session.ts"() {
|
|
2057
2087
|
init_srp();
|
|
@@ -2066,6 +2096,7 @@ var init_apple_session = __esm({
|
|
|
2066
2096
|
}
|
|
2067
2097
|
agreements;
|
|
2068
2098
|
};
|
|
2099
|
+
LOGOUT_URL = "https://appstoreconnect.apple.com/logout";
|
|
2069
2100
|
}
|
|
2070
2101
|
});
|
|
2071
2102
|
|
|
@@ -2382,9 +2413,10 @@ async function uploadWithTransporter(options) {
|
|
|
2382
2413
|
const transporterBin = await resolveTransporter();
|
|
2383
2414
|
const platform = options.platform ?? "ios";
|
|
2384
2415
|
const timeout = options.timeout ?? 18e5;
|
|
2385
|
-
const keyDir =
|
|
2416
|
+
const keyDir = join(homedir(), ".appstoreconnect", "private_keys");
|
|
2417
|
+
await mkdir(keyDir, { recursive: true });
|
|
2386
2418
|
const keyPath = join(keyDir, `AuthKey_${options.apiKeyId}.p8`);
|
|
2387
|
-
await writeFile(keyPath, options.apiPrivateKey);
|
|
2419
|
+
await writeFile(keyPath, options.apiPrivateKey, { mode: 384 });
|
|
2388
2420
|
return new Promise((resolve4) => {
|
|
2389
2421
|
const args = [
|
|
2390
2422
|
"-m",
|
|
@@ -2400,6 +2432,9 @@ async function uploadWithTransporter(options) {
|
|
|
2400
2432
|
"-WONoPause",
|
|
2401
2433
|
"true"
|
|
2402
2434
|
];
|
|
2435
|
+
if (options.assetDescriptionPath) {
|
|
2436
|
+
args.push("-assetDescription", options.assetDescriptionPath);
|
|
2437
|
+
}
|
|
2403
2438
|
if (platform !== "ios") {
|
|
2404
2439
|
args.push("-bundleType", platform);
|
|
2405
2440
|
}
|
|
@@ -2423,13 +2458,13 @@ async function uploadWithTransporter(options) {
|
|
|
2423
2458
|
child.stderr.on("data", onData);
|
|
2424
2459
|
child.on("close", async (code) => {
|
|
2425
2460
|
clearTimeout(timer);
|
|
2426
|
-
await rm(
|
|
2461
|
+
await rm(keyPath, { force: true }).catch(() => {
|
|
2427
2462
|
});
|
|
2428
2463
|
resolve4({ success: code === 0, output, backend: "transporter" });
|
|
2429
2464
|
});
|
|
2430
2465
|
child.on("error", async (err) => {
|
|
2431
2466
|
clearTimeout(timer);
|
|
2432
|
-
await rm(
|
|
2467
|
+
await rm(keyPath, { force: true }).catch(() => {
|
|
2433
2468
|
});
|
|
2434
2469
|
resolve4({
|
|
2435
2470
|
success: false,
|
|
@@ -2721,6 +2756,311 @@ var init_metadata = __esm({
|
|
|
2721
2756
|
};
|
|
2722
2757
|
}
|
|
2723
2758
|
});
|
|
2759
|
+
function categoryFor(placementType) {
|
|
2760
|
+
return placementType === "APP_SCREENSHOT" || placementType === "IMESSAGE_APP_SCREENSHOT" || placementType === "APP_PREVIEW" ? "APP_SCREENSHOTS_AND_PREVIEWS" : "CREATIVE_ASSETS";
|
|
2761
|
+
}
|
|
2762
|
+
function readImageSize(data) {
|
|
2763
|
+
if (data.length >= 26 && data.readUInt32BE(0) === 2303741511) {
|
|
2764
|
+
const colorType = data[25];
|
|
2765
|
+
return {
|
|
2766
|
+
width: data.readUInt32BE(16),
|
|
2767
|
+
height: data.readUInt32BE(20),
|
|
2768
|
+
alpha: colorType === 4 || colorType === 6
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
if (data.length >= 4 && data[0] === 255 && data[1] === 216) {
|
|
2772
|
+
let offset = 2;
|
|
2773
|
+
while (offset + 9 < data.length) {
|
|
2774
|
+
if (data[offset] !== 255) return null;
|
|
2775
|
+
const marker = data[offset + 1];
|
|
2776
|
+
const length = data.readUInt16BE(offset + 2);
|
|
2777
|
+
const isSof = marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204;
|
|
2778
|
+
if (isSof) {
|
|
2779
|
+
return { height: data.readUInt16BE(offset + 5), width: data.readUInt16BE(offset + 7), alpha: false };
|
|
2780
|
+
}
|
|
2781
|
+
offset += 2 + length;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
return null;
|
|
2785
|
+
}
|
|
2786
|
+
function fits(spec, width, height) {
|
|
2787
|
+
const d = spec.dimensions;
|
|
2788
|
+
return width >= d.minWidth && width <= d.maxWidth && height >= d.minHeight && height <= d.maxHeight;
|
|
2789
|
+
}
|
|
2790
|
+
function groupsForFeature(refData, feature, placementType) {
|
|
2791
|
+
const mappings = refData.placementTypes?.find((p) => p.placementTypeId === placementType)?.specMappings ?? [];
|
|
2792
|
+
const all = mappings.map((m) => m.placementGroupId);
|
|
2793
|
+
const policy = refData.features?.find((f) => f.featureId === feature)?.placementPolicies?.find((p) => p.placementType === placementType);
|
|
2794
|
+
const limited = policy?.groupLimits?.flatMap((l) => l.groupIds);
|
|
2795
|
+
return limited?.length ? all.filter((g) => limited.includes(g)) : all;
|
|
2796
|
+
}
|
|
2797
|
+
function maxCountFor(refData, feature, placementType, placementGroup) {
|
|
2798
|
+
return refData.features?.find((f) => f.featureId === feature)?.placementPolicies?.find((p) => p.placementType === placementType)?.groupLimits?.find((l) => l.groupIds.includes(placementGroup))?.maxCount;
|
|
2799
|
+
}
|
|
2800
|
+
function resolveImageGroups(refData, feature, placementType, size) {
|
|
2801
|
+
const specs = (refData.imageSpecs ?? []).filter(
|
|
2802
|
+
(s) => s.compatiblePlacementTypes.includes(placementType) && fits(s, size.width, size.height)
|
|
2803
|
+
);
|
|
2804
|
+
const specIds = new Set(specs.map((s) => s.specId));
|
|
2805
|
+
const allowed = new Set(groupsForFeature(refData, feature, placementType));
|
|
2806
|
+
const mappings = refData.placementTypes?.find((p) => p.placementTypeId === placementType)?.specMappings ?? [];
|
|
2807
|
+
const groups = mappings.filter((m) => allowed.has(m.placementGroupId) && m.specs.some((id) => specIds.has(id))).map((m) => m.placementGroupId);
|
|
2808
|
+
return { groups: [...new Set(groups)], spec: specs[0] };
|
|
2809
|
+
}
|
|
2810
|
+
async function runUploadOperations(ops, data) {
|
|
2811
|
+
for (const op of ops) {
|
|
2812
|
+
const headers = {};
|
|
2813
|
+
for (const h of op.requestHeaders) headers[h.name] = h.value;
|
|
2814
|
+
const res = await fetch(op.url, {
|
|
2815
|
+
method: op.method,
|
|
2816
|
+
headers,
|
|
2817
|
+
body: data.subarray(op.offset, op.offset + op.length)
|
|
2818
|
+
});
|
|
2819
|
+
if (!res.ok) {
|
|
2820
|
+
throw new Error(`Upload operation failed: ${op.method} ${new URL(op.url).host} \u2192 HTTP ${res.status}`);
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
async function uploadLibraryAsset(options) {
|
|
2825
|
+
const { client, libraryId, filePath, category } = options;
|
|
2826
|
+
const data = await readFile(filePath);
|
|
2827
|
+
const fileName = basename(filePath);
|
|
2828
|
+
const kind = VIDEO_EXTENSIONS.includes(extname(filePath).toLowerCase()) ? "video" : "image";
|
|
2829
|
+
const hash = createHash("sha256").update(data).digest("hex").slice(0, 12);
|
|
2830
|
+
const referenceName = `${options.referenceName ?? fileName} #${hash}`;
|
|
2831
|
+
const media = kind === "image" ? client.appAssetLibraryImages : client.appAssetLibraryVideos;
|
|
2832
|
+
const filter = { filter: { referenceName } };
|
|
2833
|
+
const existing = kind === "image" ? await client.appAssetLibraries.listImages(libraryId, filter) : await client.appAssetLibraries.listVideos(libraryId, filter);
|
|
2834
|
+
const reusable = existing.find(
|
|
2835
|
+
(a) => a.attributes.category === category && a.attributes.state && READY_STATES.includes(a.attributes.state)
|
|
2836
|
+
);
|
|
2837
|
+
if (reusable) {
|
|
2838
|
+
return { kind, id: reusable.id, state: reusable.attributes.state, reused: true };
|
|
2839
|
+
}
|
|
2840
|
+
const reserved = kind === "image" ? await client.appAssetLibraryImages.create({ libraryId, fileName, fileSize: data.length, category, referenceName }) : await client.appAssetLibraryVideos.create({
|
|
2841
|
+
libraryId,
|
|
2842
|
+
fileName,
|
|
2843
|
+
fileSize: data.length,
|
|
2844
|
+
category,
|
|
2845
|
+
referenceName,
|
|
2846
|
+
previewFrameTimeCode: options.previewFrameTimeCode
|
|
2847
|
+
});
|
|
2848
|
+
await runUploadOperations(reserved.attributes.uploadOperations ?? [], data);
|
|
2849
|
+
await media.update(reserved.id, { uploaded: true });
|
|
2850
|
+
options.log?.(`uploaded ${fileName} (${kind}, ${data.length} bytes)`);
|
|
2851
|
+
const state = await waitForAssetReady(
|
|
2852
|
+
() => media.get(reserved.id),
|
|
2853
|
+
options.pollIntervalMs ?? 3e3,
|
|
2854
|
+
options.timeoutMs ?? (kind === "video" ? 30 * 6e4 : 5 * 6e4)
|
|
2855
|
+
);
|
|
2856
|
+
return { kind, id: reserved.id, state, reused: false };
|
|
2857
|
+
}
|
|
2858
|
+
async function waitForAssetReady(get, intervalMs, timeoutMs) {
|
|
2859
|
+
const deadline = Date.now() + timeoutMs;
|
|
2860
|
+
for (; ; ) {
|
|
2861
|
+
const asset = await get();
|
|
2862
|
+
const state = asset.attributes.state;
|
|
2863
|
+
if (state === "FAILED") {
|
|
2864
|
+
const why = (asset.attributes.stateDetails ?? []).map((d) => d.message ?? d.code).filter(Boolean).join("; ");
|
|
2865
|
+
throw new Error(`Asset processing failed${why ? `: ${why}` : ""}`);
|
|
2866
|
+
}
|
|
2867
|
+
if (state && READY_STATES.includes(state)) return state;
|
|
2868
|
+
if (Date.now() > deadline) throw new Error(`Asset still ${state ?? "unknown"} after ${timeoutMs / 1e3}s`);
|
|
2869
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
async function listFiles(dir) {
|
|
2873
|
+
return (await readdir(dir)).filter((f) => !f.startsWith(".")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }));
|
|
2874
|
+
}
|
|
2875
|
+
async function isDir(path) {
|
|
2876
|
+
return (await stat(path)).isDirectory();
|
|
2877
|
+
}
|
|
2878
|
+
async function planLocale(localeDir, refData, feature, folders, warn) {
|
|
2879
|
+
const planned = [];
|
|
2880
|
+
for (const [folder, placementType] of Object.entries(folders)) {
|
|
2881
|
+
const typeDir = join(localeDir, folder);
|
|
2882
|
+
if (!existsSync(typeDir)) continue;
|
|
2883
|
+
const known = new Set(groupsForFeature(refData, feature, placementType));
|
|
2884
|
+
const entries = [];
|
|
2885
|
+
for (const entry of await listFiles(typeDir)) {
|
|
2886
|
+
const full = join(typeDir, entry);
|
|
2887
|
+
if (await isDir(full)) {
|
|
2888
|
+
if (!known.has(entry)) {
|
|
2889
|
+
warn(`${relative(localeDir, full)}: "${entry}" is not a ${placementType} placement group for ${feature} \u2014 skipped`);
|
|
2890
|
+
continue;
|
|
2891
|
+
}
|
|
2892
|
+
for (const f of await listFiles(full)) entries.push({ filePath: join(full, f), group: entry });
|
|
2893
|
+
} else {
|
|
2894
|
+
entries.push({ filePath: full });
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
for (const { filePath, group } of entries) {
|
|
2898
|
+
const ext = extname(filePath).toLowerCase();
|
|
2899
|
+
const isVideo = VIDEO_EXTENSIONS.includes(ext);
|
|
2900
|
+
if (!isVideo && !IMAGE_EXTENSIONS.includes(ext)) continue;
|
|
2901
|
+
const needsVideo = placementType === "APP_PREVIEW";
|
|
2902
|
+
const needsImage = placementType === "APP_SCREENSHOT" || placementType === "IMESSAGE_APP_SCREENSHOT";
|
|
2903
|
+
if (needsVideo && !isVideo || needsImage && isVideo) {
|
|
2904
|
+
warn(`${relative(localeDir, filePath)}: ${isVideo ? "video" : "image"} cannot be a ${placementType} \u2014 skipped`);
|
|
2905
|
+
continue;
|
|
2906
|
+
}
|
|
2907
|
+
if (group) {
|
|
2908
|
+
planned.push({ filePath, placementType, groups: [group] });
|
|
2909
|
+
continue;
|
|
2910
|
+
}
|
|
2911
|
+
if (isVideo) {
|
|
2912
|
+
if (known.size === 1) planned.push({ filePath, placementType, groups: [...known] });
|
|
2913
|
+
else warn(`${relative(localeDir, filePath)}: put videos in a placement-group folder (one of ${[...known].join(", ")}) \u2014 skipped`);
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
const size = readImageSize(await readFile(filePath));
|
|
2917
|
+
if (!size) {
|
|
2918
|
+
warn(`${relative(localeDir, filePath)}: could not read image size \u2014 skipped`);
|
|
2919
|
+
continue;
|
|
2920
|
+
}
|
|
2921
|
+
const { groups, spec } = resolveImageGroups(refData, feature, placementType, size);
|
|
2922
|
+
if (!groups.length) {
|
|
2923
|
+
warn(`${relative(localeDir, filePath)}: ${size.width}\xD7${size.height} matches no ${placementType} spec \u2014 skipped`);
|
|
2924
|
+
continue;
|
|
2925
|
+
}
|
|
2926
|
+
if (size.alpha && spec && spec.alphaAllowed === false) {
|
|
2927
|
+
warn(`${relative(localeDir, filePath)}: has an alpha channel, which Apple rejects for this spec`);
|
|
2928
|
+
}
|
|
2929
|
+
planned.push({ filePath, placementType, groups: groups.slice(0, 1) });
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
return planned;
|
|
2933
|
+
}
|
|
2934
|
+
async function uploadAssetLibrary(options) {
|
|
2935
|
+
const { client } = options;
|
|
2936
|
+
const log = options.log ?? (() => {
|
|
2937
|
+
});
|
|
2938
|
+
const feature = "APP_STORE_VERSIONS";
|
|
2939
|
+
const result = { uploaded: 0, reused: 0, placed: 0, removed: 0, warnings: [] };
|
|
2940
|
+
const warn = (line) => {
|
|
2941
|
+
result.warnings.push(line);
|
|
2942
|
+
log(`warning: ${line}`);
|
|
2943
|
+
};
|
|
2944
|
+
const [library, refData, localizations] = await Promise.all([
|
|
2945
|
+
client.appAssetLibraries.getForApp(options.appId),
|
|
2946
|
+
client.appAssetLibraries.refData({ features: [feature] }),
|
|
2947
|
+
client.appStoreVersions.getLocalizations(options.versionId)
|
|
2948
|
+
]);
|
|
2949
|
+
const folders = { ...options.folders ?? ASSET_FOLDERS };
|
|
2950
|
+
if (options.rootPlacementType) folders["."] = options.rootPlacementType;
|
|
2951
|
+
const uploads = /* @__PURE__ */ new Map();
|
|
2952
|
+
const uploadOnce = (filePath, placementType) => {
|
|
2953
|
+
const key = `${filePath}|${categoryFor(placementType)}`;
|
|
2954
|
+
if (!uploads.has(key)) {
|
|
2955
|
+
uploads.set(
|
|
2956
|
+
key,
|
|
2957
|
+
uploadLibraryAsset({
|
|
2958
|
+
client,
|
|
2959
|
+
libraryId: library.id,
|
|
2960
|
+
filePath,
|
|
2961
|
+
category: categoryFor(placementType),
|
|
2962
|
+
referenceName: relative(options.assetsPath, filePath),
|
|
2963
|
+
log
|
|
2964
|
+
}).then((a) => {
|
|
2965
|
+
if (a.reused) result.reused++;
|
|
2966
|
+
else result.uploaded++;
|
|
2967
|
+
return a;
|
|
2968
|
+
})
|
|
2969
|
+
);
|
|
2970
|
+
}
|
|
2971
|
+
return uploads.get(key);
|
|
2972
|
+
};
|
|
2973
|
+
for (const loc of localizations) {
|
|
2974
|
+
const localeDir = join(options.assetsPath, loc.attributes.locale);
|
|
2975
|
+
if (!existsSync(localeDir)) continue;
|
|
2976
|
+
const target = { kind: "appStoreVersionLocalization", id: loc.id };
|
|
2977
|
+
const plan = await planLocale(localeDir, refData, feature, folders, warn);
|
|
2978
|
+
const slots = /* @__PURE__ */ new Map();
|
|
2979
|
+
for (const p of plan) {
|
|
2980
|
+
for (const group of p.groups) {
|
|
2981
|
+
const key = `${p.placementType}|${group}`;
|
|
2982
|
+
if (!slots.has(key)) slots.set(key, { placementType: p.placementType, group, files: [] });
|
|
2983
|
+
slots.get(key).files.push(p.filePath);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
for (const slot of slots.values()) {
|
|
2987
|
+
const where = `${loc.attributes.locale} ${slot.placementType} ${slot.group}`;
|
|
2988
|
+
const max = maxCountFor(refData, feature, slot.placementType, slot.group);
|
|
2989
|
+
if (max !== void 0 && slot.files.length > max) {
|
|
2990
|
+
warn(`${where}: ${slot.files.length} files but Apple allows ${max} \u2014 keeping the first ${max}`);
|
|
2991
|
+
slot.files = slot.files.slice(0, max);
|
|
2992
|
+
}
|
|
2993
|
+
const existing = await client.appAssetLibraryPlacements.listForTarget(target, {
|
|
2994
|
+
placementType: slot.placementType,
|
|
2995
|
+
placementGroup: slot.group,
|
|
2996
|
+
include: "image,video"
|
|
2997
|
+
});
|
|
2998
|
+
if (options.overwrite) {
|
|
2999
|
+
for (const p of existing) {
|
|
3000
|
+
await client.appAssetLibraryPlacements.delete(p.id);
|
|
3001
|
+
result.removed++;
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
const kept = options.overwrite ? [] : existing;
|
|
3005
|
+
const keptAssetIds = new Set(kept.map((p) => placementAssetId(p)).filter(Boolean));
|
|
3006
|
+
const ours = [];
|
|
3007
|
+
for (const filePath of slot.files) {
|
|
3008
|
+
const asset = await uploadOnce(filePath, slot.placementType);
|
|
3009
|
+
if (keptAssetIds.has(asset.id)) continue;
|
|
3010
|
+
const placement = await client.appAssetLibraryPlacements.create({
|
|
3011
|
+
placementType: slot.placementType,
|
|
3012
|
+
placementGroup: slot.group,
|
|
3013
|
+
...asset.kind === "image" ? { imageId: asset.id } : { videoId: asset.id },
|
|
3014
|
+
target
|
|
3015
|
+
});
|
|
3016
|
+
ours.push(placement.id);
|
|
3017
|
+
result.placed++;
|
|
3018
|
+
}
|
|
3019
|
+
log(`${where}: placed ${ours.length}${kept.length ? `, kept ${kept.length}` : ""}`);
|
|
3020
|
+
const orderable = slot.placementType === "APP_SCREENSHOT" || slot.placementType === "APP_PREVIEW" || slot.placementType === "IMESSAGE_APP_SCREENSHOT";
|
|
3021
|
+
if (orderable && ours.length + kept.length > 1) {
|
|
3022
|
+
await client.appAssetLibraryPlacements.order({
|
|
3023
|
+
placementGroup: slot.group,
|
|
3024
|
+
placementIds: [...ours, ...kept.map((p) => p.id)],
|
|
3025
|
+
target
|
|
3026
|
+
});
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
return result;
|
|
3031
|
+
}
|
|
3032
|
+
function placementAssetId(p) {
|
|
3033
|
+
for (const rel of ["image", "video"]) {
|
|
3034
|
+
const data = p.relationships?.[rel]?.data;
|
|
3035
|
+
if (data?.id) return data.id;
|
|
3036
|
+
}
|
|
3037
|
+
return void 0;
|
|
3038
|
+
}
|
|
3039
|
+
var ASSET_FOLDERS, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS, READY_STATES;
|
|
3040
|
+
var init_assets = __esm({
|
|
3041
|
+
"src/deliver/assets.ts"() {
|
|
3042
|
+
ASSET_FOLDERS = {
|
|
3043
|
+
screenshots: "APP_SCREENSHOT",
|
|
3044
|
+
imessage_screenshots: "IMESSAGE_APP_SCREENSHOT",
|
|
3045
|
+
previews: "APP_PREVIEW",
|
|
3046
|
+
header: "PRODUCT_PAGE_HEADER_ASSET",
|
|
3047
|
+
search_results: "APP_STORE_SEARCH_RESULTS_ASSET",
|
|
3048
|
+
event_card: "EVENT_CARD_ASSET",
|
|
3049
|
+
event_details: "EVENT_DETAILS_PAGE_ASSET"
|
|
3050
|
+
};
|
|
3051
|
+
IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg"];
|
|
3052
|
+
VIDEO_EXTENSIONS = [".mp4", ".mov", ".m4v"];
|
|
3053
|
+
READY_STATES = [
|
|
3054
|
+
"COMPLETE",
|
|
3055
|
+
"PREPARE_FOR_SUBMISSION",
|
|
3056
|
+
"READY_FOR_REVIEW",
|
|
3057
|
+
"WAITING_FOR_REVIEW",
|
|
3058
|
+
"IN_REVIEW",
|
|
3059
|
+
"ACCEPTED",
|
|
3060
|
+
"APPROVED"
|
|
3061
|
+
];
|
|
3062
|
+
}
|
|
3063
|
+
});
|
|
2724
3064
|
|
|
2725
3065
|
// src/xcode/upload.ts
|
|
2726
3066
|
var upload_exports = {};
|
|
@@ -2786,8 +3126,14 @@ var init_upload = __esm({
|
|
|
2786
3126
|
// src/deliver/index.ts
|
|
2787
3127
|
var deliver_exports = {};
|
|
2788
3128
|
__export(deliver_exports, {
|
|
3129
|
+
ASSET_FOLDERS: () => ASSET_FOLDERS,
|
|
3130
|
+
categoryFor: () => categoryFor,
|
|
2789
3131
|
loadMetadata: () => loadMetadata,
|
|
3132
|
+
readImageSize: () => readImageSize,
|
|
3133
|
+
resolveImageGroups: () => resolveImageGroups,
|
|
2790
3134
|
submitForReview: () => submitForReview,
|
|
3135
|
+
uploadAssetLibrary: () => uploadAssetLibrary,
|
|
3136
|
+
uploadLibraryAsset: () => uploadLibraryAsset,
|
|
2791
3137
|
uploadMetadata: () => uploadMetadata,
|
|
2792
3138
|
uploadScreenshots: () => uploadScreenshots,
|
|
2793
3139
|
uploadToAppStore: () => uploadToAppStore
|
|
@@ -2826,11 +3172,36 @@ async function uploadToAppStore(options) {
|
|
|
2826
3172
|
copyright: options.copyright
|
|
2827
3173
|
});
|
|
2828
3174
|
}
|
|
3175
|
+
const log = (line) => options.onOutput?.(`${line}
|
|
3176
|
+
`);
|
|
2829
3177
|
if (!options.skipScreenshots && options.screenshotsPath && existsSync(options.screenshotsPath)) {
|
|
2830
|
-
|
|
3178
|
+
if (options.useAssetLibrary) {
|
|
3179
|
+
await uploadAssetLibrary({
|
|
3180
|
+
client: options.client,
|
|
3181
|
+
appId: app.id,
|
|
3182
|
+
versionId: version.id,
|
|
3183
|
+
assetsPath: options.screenshotsPath,
|
|
3184
|
+
folders: {},
|
|
3185
|
+
rootPlacementType: "APP_SCREENSHOT",
|
|
3186
|
+
overwrite: options.overwriteAssets,
|
|
3187
|
+
log
|
|
3188
|
+
});
|
|
3189
|
+
} else {
|
|
3190
|
+
await uploadScreenshots({
|
|
3191
|
+
client: options.client,
|
|
3192
|
+
versionId: version.id,
|
|
3193
|
+
screenshotsPath: options.screenshotsPath
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
if (!options.skipAssets && options.assetsPath && existsSync(options.assetsPath)) {
|
|
3198
|
+
await uploadAssetLibrary({
|
|
2831
3199
|
client: options.client,
|
|
3200
|
+
appId: app.id,
|
|
2832
3201
|
versionId: version.id,
|
|
2833
|
-
|
|
3202
|
+
assetsPath: options.assetsPath,
|
|
3203
|
+
overwrite: options.overwriteAssets,
|
|
3204
|
+
log
|
|
2834
3205
|
});
|
|
2835
3206
|
}
|
|
2836
3207
|
if (!options.skipBinaryUpload) {
|
|
@@ -2915,17 +3286,7 @@ async function uploadScreenshots(options) {
|
|
|
2915
3286
|
fileName: file,
|
|
2916
3287
|
fileSize: fileData.length
|
|
2917
3288
|
});
|
|
2918
|
-
|
|
2919
|
-
if (ops) {
|
|
2920
|
-
for (const op of ops) {
|
|
2921
|
-
const chunk = fileData.subarray(op.offset, op.offset + op.length);
|
|
2922
|
-
const headers = {};
|
|
2923
|
-
for (const h of op.requestHeaders) {
|
|
2924
|
-
headers[h.name] = h.value;
|
|
2925
|
-
}
|
|
2926
|
-
await fetch(op.url, { method: op.method, headers, body: chunk });
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
3289
|
+
await runUploadOperations(reserved.attributes.uploadOperations ?? [], fileData);
|
|
2929
3290
|
await options.client.appScreenshots.commitScreenshot(reserved.id, {
|
|
2930
3291
|
sourceFileChecksum: checksum,
|
|
2931
3292
|
uploaded: true
|
|
@@ -2945,7 +3306,9 @@ function detectDisplayType(filename) {
|
|
|
2945
3306
|
var init_deliver = __esm({
|
|
2946
3307
|
"src/deliver/index.ts"() {
|
|
2947
3308
|
init_metadata();
|
|
3309
|
+
init_assets();
|
|
2948
3310
|
init_metadata();
|
|
3311
|
+
init_assets();
|
|
2949
3312
|
}
|
|
2950
3313
|
});
|
|
2951
3314
|
|
|
@@ -2954,6 +3317,7 @@ var produce_exports = {};
|
|
|
2954
3317
|
__export(produce_exports, {
|
|
2955
3318
|
SERVICE_NAME_TO_CAPABILITY: () => SERVICE_NAME_TO_CAPABILITY,
|
|
2956
3319
|
addCapability: () => addCapability,
|
|
3320
|
+
associateAppGroups: () => associateAppGroups,
|
|
2957
3321
|
buildCapabilitySettings: () => buildCapabilitySettings,
|
|
2958
3322
|
createApp: () => createApp,
|
|
2959
3323
|
enableServices: () => enableServices,
|
|
@@ -3048,6 +3412,30 @@ async function enableServices(options) {
|
|
|
3048
3412
|
});
|
|
3049
3413
|
}
|
|
3050
3414
|
}
|
|
3415
|
+
async function associateAppGroups(options) {
|
|
3416
|
+
if (!options.appGroups?.length) return;
|
|
3417
|
+
await options.portalClient.enableAppGroups(options.bundleIdId).catch(() => {
|
|
3418
|
+
});
|
|
3419
|
+
const existing = await options.portalClient.listAppGroups();
|
|
3420
|
+
const appGroupIds = [];
|
|
3421
|
+
for (const group of options.appGroups) {
|
|
3422
|
+
let record = existing.find((ag) => ag.identifier === group.identifier);
|
|
3423
|
+
if (!record) {
|
|
3424
|
+
record = await options.portalClient.createAppGroup({
|
|
3425
|
+
name: group.name,
|
|
3426
|
+
identifier: group.identifier
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
const id = record?.applicationGroup ?? record?.applicationGroupId ?? record?.appGroupId ?? record?.id;
|
|
3430
|
+
if (id) appGroupIds.push(id);
|
|
3431
|
+
}
|
|
3432
|
+
if (appGroupIds.length) {
|
|
3433
|
+
await options.portalClient.assignAppGroups({
|
|
3434
|
+
appIdId: options.bundleIdId,
|
|
3435
|
+
appGroupIds
|
|
3436
|
+
});
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3051
3439
|
async function createApp(options) {
|
|
3052
3440
|
const existing = await options.client.apps.findByBundleId(options.bundleIdentifier);
|
|
3053
3441
|
if (existing) return existing;
|
|
@@ -3066,25 +3454,20 @@ async function createApp(options) {
|
|
|
3066
3454
|
});
|
|
3067
3455
|
}
|
|
3068
3456
|
}
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
data: { type: "bundleIds", id: bundleId.id }
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3457
|
+
if (options.appGroups?.length && options.portalClient) {
|
|
3458
|
+
await associateAppGroups({
|
|
3459
|
+
portalClient: options.portalClient,
|
|
3460
|
+
bundleIdId: bundleId.id,
|
|
3461
|
+
appGroups: options.appGroups
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
return options.client.apps.create({
|
|
3465
|
+
bundleIdentifier: options.bundleIdentifier,
|
|
3466
|
+
bundleIdId: bundleId.id,
|
|
3467
|
+
appName: options.appName,
|
|
3468
|
+
sku: options.sku ?? `${options.bundleIdentifier}.${Date.now()}`,
|
|
3469
|
+
primaryLocale: options.primaryLocale
|
|
3086
3470
|
});
|
|
3087
|
-
return response.data;
|
|
3088
3471
|
}
|
|
3089
3472
|
var SERVICE_NAME_TO_CAPABILITY;
|
|
3090
3473
|
var init_produce = __esm({
|
|
@@ -3887,10 +4270,10 @@ async function parseTestResults(options) {
|
|
|
3887
4270
|
} else if (inputPath.endsWith(".plist")) {
|
|
3888
4271
|
testSuites = parsePlistResults(inputPath);
|
|
3889
4272
|
} else {
|
|
3890
|
-
const
|
|
4273
|
+
const stat7 = await import('fs').then(
|
|
3891
4274
|
(fs) => fs.statSync(inputPath)
|
|
3892
4275
|
);
|
|
3893
|
-
if (
|
|
4276
|
+
if (stat7.isDirectory()) {
|
|
3894
4277
|
requireMacOS("xcresult parsing");
|
|
3895
4278
|
testSuites = await parseXCResult(inputPath);
|
|
3896
4279
|
} else {
|
|
@@ -4878,6 +5261,43 @@ var init_apps = __esm({
|
|
|
4878
5261
|
});
|
|
4879
5262
|
return results[0] ?? null;
|
|
4880
5263
|
}
|
|
5264
|
+
/**
|
|
5265
|
+
* Create an App Store Connect **app record** (the thing that shows up
|
|
5266
|
+
* under "My Apps" and that TestFlight / App Store uploads require).
|
|
5267
|
+
*
|
|
5268
|
+
* This is distinct from registering an App ID in the Developer Portal —
|
|
5269
|
+
* you need BOTH. The Developer Portal App ID unlocks provisioning
|
|
5270
|
+
* profiles and entitlements; the App Store Connect app record unlocks
|
|
5271
|
+
* uploads. `upload_to_testflight` fails with "Couldn't find app
|
|
5272
|
+
* 'com.foo.bar' on the account…" when this record is missing.
|
|
5273
|
+
*
|
|
5274
|
+
* `bundleIdId` is the REST API primary key of the bundle id resource
|
|
5275
|
+
* (not the identifier string) — fetch it via `bundleIds.find(identifier)`
|
|
5276
|
+
* or from the response of `bundleIds.create()`.
|
|
5277
|
+
*
|
|
5278
|
+
* `sku` is an arbitrary unique string Apple uses to identify the app in
|
|
5279
|
+
* their internal records. If you don't have a meaningful one, pass the
|
|
5280
|
+
* bundle identifier itself or append a timestamp for uniqueness.
|
|
5281
|
+
*/
|
|
5282
|
+
async create(params) {
|
|
5283
|
+
const response = await this.http.post("/v1/apps", {
|
|
5284
|
+
data: {
|
|
5285
|
+
type: "apps",
|
|
5286
|
+
attributes: {
|
|
5287
|
+
bundleId: params.bundleIdentifier,
|
|
5288
|
+
name: params.appName,
|
|
5289
|
+
sku: params.sku,
|
|
5290
|
+
primaryLocale: params.primaryLocale ?? "en-US"
|
|
5291
|
+
},
|
|
5292
|
+
relationships: {
|
|
5293
|
+
bundleId: {
|
|
5294
|
+
data: { type: "bundleIds", id: params.bundleIdId }
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
});
|
|
5299
|
+
return response.data;
|
|
5300
|
+
}
|
|
4881
5301
|
};
|
|
4882
5302
|
}
|
|
4883
5303
|
});
|
|
@@ -6030,9 +6450,22 @@ var init_pre_release_versions = __esm({
|
|
|
6030
6450
|
});
|
|
6031
6451
|
|
|
6032
6452
|
// src/resources/review-submission-items.ts
|
|
6033
|
-
var ReviewSubmissionItemsResource;
|
|
6453
|
+
var ITEM_RELATIONSHIPS, ReviewSubmissionItemsResource;
|
|
6034
6454
|
var init_review_submission_items = __esm({
|
|
6035
6455
|
"src/resources/review-submission-items.ts"() {
|
|
6456
|
+
ITEM_RELATIONSHIPS = {
|
|
6457
|
+
appStoreVersionId: ["appStoreVersion", "appStoreVersions"],
|
|
6458
|
+
appCustomProductPageVersionId: ["appCustomProductPageVersion", "appCustomProductPageVersions"],
|
|
6459
|
+
appStoreVersionExperimentId: ["appStoreVersionExperiment", "appStoreVersionExperiments"],
|
|
6460
|
+
appStoreVersionExperimentV2Id: ["appStoreVersionExperimentV2", "appStoreVersionExperiments"],
|
|
6461
|
+
appEventId: ["appEvent", "appEvents"],
|
|
6462
|
+
appAssetLibraryImageId: ["appAssetLibraryImage", "appAssetLibraryImages"],
|
|
6463
|
+
appAssetLibraryVideoId: ["appAssetLibraryVideo", "appAssetLibraryVideos"],
|
|
6464
|
+
backgroundAssetVersionId: ["backgroundAssetVersion", "backgroundAssetVersions"],
|
|
6465
|
+
inAppPurchaseVersionId: ["inAppPurchaseVersion", "inAppPurchaseVersions"],
|
|
6466
|
+
subscriptionVersionId: ["subscriptionVersion", "subscriptionVersions"],
|
|
6467
|
+
subscriptionGroupVersionId: ["subscriptionGroupVersion", "subscriptionGroupVersions"]
|
|
6468
|
+
};
|
|
6036
6469
|
ReviewSubmissionItemsResource = class {
|
|
6037
6470
|
constructor(http) {
|
|
6038
6471
|
this.http = http;
|
|
@@ -6054,32 +6487,20 @@ var init_review_submission_items = __esm({
|
|
|
6054
6487
|
);
|
|
6055
6488
|
return response.data;
|
|
6056
6489
|
}
|
|
6490
|
+
/**
|
|
6491
|
+
* Add one item to a review submission. Besides versions, asset library
|
|
6492
|
+
* images and videos can be submitted on their own (ASC API 4.5.1) so
|
|
6493
|
+
* creative assets are approved ahead of a campaign.
|
|
6494
|
+
*/
|
|
6057
6495
|
async create(params) {
|
|
6058
6496
|
const relationships = {
|
|
6059
6497
|
reviewSubmission: {
|
|
6060
6498
|
data: { type: "reviewSubmissions", id: params.reviewSubmissionId }
|
|
6061
6499
|
}
|
|
6062
6500
|
};
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
};
|
|
6067
|
-
}
|
|
6068
|
-
if (params.appCustomProductPageVersionId) {
|
|
6069
|
-
relationships.appCustomProductPageVersion = {
|
|
6070
|
-
data: {
|
|
6071
|
-
type: "appCustomProductPageVersions",
|
|
6072
|
-
id: params.appCustomProductPageVersionId
|
|
6073
|
-
}
|
|
6074
|
-
};
|
|
6075
|
-
}
|
|
6076
|
-
if (params.appStoreVersionExperimentId) {
|
|
6077
|
-
relationships.appStoreVersionExperiment = {
|
|
6078
|
-
data: {
|
|
6079
|
-
type: "appStoreVersionExperiments",
|
|
6080
|
-
id: params.appStoreVersionExperimentId
|
|
6081
|
-
}
|
|
6082
|
-
};
|
|
6501
|
+
for (const [param, [relationship, type]] of Object.entries(ITEM_RELATIONSHIPS)) {
|
|
6502
|
+
const id = params[param];
|
|
6503
|
+
if (id) relationships[relationship] = { data: { type, id } };
|
|
6083
6504
|
}
|
|
6084
6505
|
const response = await this.http.post(
|
|
6085
6506
|
"/v1/reviewSubmissionItems",
|
|
@@ -6310,6 +6731,204 @@ var init_xcode_metrics = __esm({
|
|
|
6310
6731
|
}
|
|
6311
6732
|
});
|
|
6312
6733
|
|
|
6734
|
+
// src/resources/app-asset-libraries.ts
|
|
6735
|
+
function mergeRefData(parts) {
|
|
6736
|
+
const merged = {
|
|
6737
|
+
features: [],
|
|
6738
|
+
placementProfileGroups: [],
|
|
6739
|
+
imageSpecs: [],
|
|
6740
|
+
videoSpecs: [],
|
|
6741
|
+
placementTypes: [],
|
|
6742
|
+
displayClasses: []
|
|
6743
|
+
};
|
|
6744
|
+
for (const part of parts) {
|
|
6745
|
+
for (const key of Object.keys(merged)) {
|
|
6746
|
+
merged[key].push(...part[key] ?? []);
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
return merged;
|
|
6750
|
+
}
|
|
6751
|
+
var AppAssetLibrariesResource;
|
|
6752
|
+
var init_app_asset_libraries = __esm({
|
|
6753
|
+
"src/resources/app-asset-libraries.ts"() {
|
|
6754
|
+
AppAssetLibrariesResource = class {
|
|
6755
|
+
constructor(http) {
|
|
6756
|
+
this.http = http;
|
|
6757
|
+
}
|
|
6758
|
+
http;
|
|
6759
|
+
async getForApp(appId) {
|
|
6760
|
+
const response = await this.http.get(
|
|
6761
|
+
`/v1/apps/${appId}/assetLibrary`
|
|
6762
|
+
);
|
|
6763
|
+
return response.data;
|
|
6764
|
+
}
|
|
6765
|
+
async listImages(libraryId, params = {}) {
|
|
6766
|
+
return this.http.all(`/v1/appAssetLibraries/${libraryId}/images`, params);
|
|
6767
|
+
}
|
|
6768
|
+
async listVideos(libraryId, params = {}) {
|
|
6769
|
+
return this.http.all(`/v1/appAssetLibraries/${libraryId}/videos`, params);
|
|
6770
|
+
}
|
|
6771
|
+
/**
|
|
6772
|
+
* Specs, placement groups and limits. Apple adds placement groups (new
|
|
6773
|
+
* devices) without an API version bump, so read them from here rather than
|
|
6774
|
+
* hard-coding display types.
|
|
6775
|
+
*/
|
|
6776
|
+
async refData(params = {}) {
|
|
6777
|
+
const filter = {};
|
|
6778
|
+
if (params.features?.length) filter.features = params.features;
|
|
6779
|
+
if (params.placementTypes?.length) filter.placementTypes = params.placementTypes;
|
|
6780
|
+
if (params.placementProfileGroups?.length) filter.placementProfileGroups = params.placementProfileGroups;
|
|
6781
|
+
const data = await this.http.all("/v1/appAssetLibraryRefData", { filter });
|
|
6782
|
+
return mergeRefData(data.map((d) => d.attributes));
|
|
6783
|
+
}
|
|
6784
|
+
};
|
|
6785
|
+
}
|
|
6786
|
+
});
|
|
6787
|
+
|
|
6788
|
+
// src/resources/app-asset-library-assets.ts
|
|
6789
|
+
var AssetLibraryMediaResource, AppAssetLibraryImagesResource, AppAssetLibraryVideosResource;
|
|
6790
|
+
var init_app_asset_library_assets = __esm({
|
|
6791
|
+
"src/resources/app-asset-library-assets.ts"() {
|
|
6792
|
+
AssetLibraryMediaResource = class {
|
|
6793
|
+
constructor(http) {
|
|
6794
|
+
this.http = http;
|
|
6795
|
+
}
|
|
6796
|
+
http;
|
|
6797
|
+
async get(id) {
|
|
6798
|
+
const response = await this.http.get(`/v1/${this.type}/${id}`);
|
|
6799
|
+
return response.data;
|
|
6800
|
+
}
|
|
6801
|
+
async reserve(params, extra = {}) {
|
|
6802
|
+
const { libraryId, ...attributes } = params;
|
|
6803
|
+
const response = await this.http.post(`/v1/${this.type}`, {
|
|
6804
|
+
data: {
|
|
6805
|
+
type: this.type,
|
|
6806
|
+
attributes: { ...attributes, ...extra },
|
|
6807
|
+
relationships: {
|
|
6808
|
+
assetLibrary: { data: { type: "appAssetLibraries", id: libraryId } }
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
});
|
|
6812
|
+
return response.data;
|
|
6813
|
+
}
|
|
6814
|
+
async patch(id, attributes) {
|
|
6815
|
+
const response = await this.http.patch(`/v1/${this.type}/${id}`, {
|
|
6816
|
+
data: { type: this.type, id, attributes }
|
|
6817
|
+
});
|
|
6818
|
+
return response.data;
|
|
6819
|
+
}
|
|
6820
|
+
/** Fails with 409 STATE_ERROR.ASSET_HAS_PLACEMENTS while the asset is still placed. */
|
|
6821
|
+
async delete(id) {
|
|
6822
|
+
await this.http.delete(`/v1/${this.type}/${id}`);
|
|
6823
|
+
}
|
|
6824
|
+
async listPlacements(id, params = {}) {
|
|
6825
|
+
return this.http.all(`/v1/${this.type}/${id}/placements`, params);
|
|
6826
|
+
}
|
|
6827
|
+
};
|
|
6828
|
+
AppAssetLibraryImagesResource = class extends AssetLibraryMediaResource {
|
|
6829
|
+
type = "appAssetLibraryImages";
|
|
6830
|
+
async create(params) {
|
|
6831
|
+
return this.reserve(params);
|
|
6832
|
+
}
|
|
6833
|
+
async update(id, attributes) {
|
|
6834
|
+
return this.patch(id, { ...attributes });
|
|
6835
|
+
}
|
|
6836
|
+
};
|
|
6837
|
+
AppAssetLibraryVideosResource = class extends AssetLibraryMediaResource {
|
|
6838
|
+
type = "appAssetLibraryVideos";
|
|
6839
|
+
async create(params) {
|
|
6840
|
+
const { previewFrameTimeCode, ...rest } = params;
|
|
6841
|
+
return this.reserve(rest, previewFrameTimeCode ? { previewFrameTimeCode } : {});
|
|
6842
|
+
}
|
|
6843
|
+
async update(id, attributes) {
|
|
6844
|
+
return this.patch(id, { ...attributes });
|
|
6845
|
+
}
|
|
6846
|
+
};
|
|
6847
|
+
}
|
|
6848
|
+
});
|
|
6849
|
+
|
|
6850
|
+
// src/resources/app-asset-library-placements.ts
|
|
6851
|
+
function targetRelationship(target) {
|
|
6852
|
+
return { [target.kind]: { data: { type: TARGET_TYPES[target.kind], id: target.id } } };
|
|
6853
|
+
}
|
|
6854
|
+
var TARGET_TYPES, AppAssetLibraryPlacementsResource;
|
|
6855
|
+
var init_app_asset_library_placements = __esm({
|
|
6856
|
+
"src/resources/app-asset-library-placements.ts"() {
|
|
6857
|
+
TARGET_TYPES = {
|
|
6858
|
+
appStoreVersionLocalization: "appStoreVersionLocalizations",
|
|
6859
|
+
appCustomProductPageLocalization: "appCustomProductPageLocalizations",
|
|
6860
|
+
appEventLocalization: "appEventLocalizations",
|
|
6861
|
+
appStoreVersionExperimentTreatmentLocalization: "appStoreVersionExperimentTreatmentLocalizations"
|
|
6862
|
+
};
|
|
6863
|
+
AppAssetLibraryPlacementsResource = class {
|
|
6864
|
+
constructor(http) {
|
|
6865
|
+
this.http = http;
|
|
6866
|
+
}
|
|
6867
|
+
http;
|
|
6868
|
+
/**
|
|
6869
|
+
* Place an image or video on one localization. Placements cannot be edited —
|
|
6870
|
+
* delete and recreate. The parent version/page must be editable.
|
|
6871
|
+
*/
|
|
6872
|
+
async create(params) {
|
|
6873
|
+
if (!params.imageId === !params.videoId) {
|
|
6874
|
+
throw new Error("A placement needs exactly one of imageId or videoId");
|
|
6875
|
+
}
|
|
6876
|
+
const media = params.imageId ? { image: { data: { type: "appAssetLibraryImages", id: params.imageId } } } : { video: { data: { type: "appAssetLibraryVideos", id: params.videoId } } };
|
|
6877
|
+
const response = await this.http.post(
|
|
6878
|
+
"/v1/appAssetLibraryPlacements",
|
|
6879
|
+
{
|
|
6880
|
+
data: {
|
|
6881
|
+
type: "appAssetLibraryPlacements",
|
|
6882
|
+
attributes: { placementType: params.placementType, placementGroup: params.placementGroup },
|
|
6883
|
+
relationships: { ...media, ...targetRelationship(params.target) }
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6886
|
+
);
|
|
6887
|
+
return response.data;
|
|
6888
|
+
}
|
|
6889
|
+
async get(id, params = {}) {
|
|
6890
|
+
const response = await this.http.get(
|
|
6891
|
+
`/v1/appAssetLibraryPlacements/${id}`,
|
|
6892
|
+
params.include ? { include: params.include } : void 0
|
|
6893
|
+
);
|
|
6894
|
+
return response.data;
|
|
6895
|
+
}
|
|
6896
|
+
async delete(id) {
|
|
6897
|
+
await this.http.delete(`/v1/appAssetLibraryPlacements/${id}`);
|
|
6898
|
+
}
|
|
6899
|
+
/** Placements on one localization, in display order by default. */
|
|
6900
|
+
async listForTarget(target, params = {}) {
|
|
6901
|
+
const filter = {};
|
|
6902
|
+
if (params.placementType) filter.placementType = params.placementType;
|
|
6903
|
+
if (params.placementGroup) filter.placementGroup = params.placementGroup;
|
|
6904
|
+
const listParams = { filter, include: params.include, sort: "placementGroupPosition" };
|
|
6905
|
+
return this.http.all(
|
|
6906
|
+
`/v1/${TARGET_TYPES[target.kind]}/${target.id}/placements`,
|
|
6907
|
+
listParams
|
|
6908
|
+
);
|
|
6909
|
+
}
|
|
6910
|
+
/** Set the display order of a placement group. In-app event localizations cannot be ordered. */
|
|
6911
|
+
async order(params) {
|
|
6912
|
+
if (params.target.kind === "appEventLocalization") {
|
|
6913
|
+
throw new Error("In-app event placements cannot be ordered");
|
|
6914
|
+
}
|
|
6915
|
+
await this.http.post("/v1/appAssetLibraryPlacementOrderingRequests", {
|
|
6916
|
+
data: {
|
|
6917
|
+
type: "appAssetLibraryPlacementOrderingRequests",
|
|
6918
|
+
attributes: { placementGroup: params.placementGroup },
|
|
6919
|
+
relationships: {
|
|
6920
|
+
orderedPlacements: {
|
|
6921
|
+
data: params.placementIds.map((id) => ({ type: "appAssetLibraryPlacements", id }))
|
|
6922
|
+
},
|
|
6923
|
+
...targetRelationship(params.target)
|
|
6924
|
+
}
|
|
6925
|
+
}
|
|
6926
|
+
});
|
|
6927
|
+
}
|
|
6928
|
+
};
|
|
6929
|
+
}
|
|
6930
|
+
});
|
|
6931
|
+
|
|
6313
6932
|
// src/client/portal.ts
|
|
6314
6933
|
function pickUniqueKeyName(base, taken) {
|
|
6315
6934
|
if (!taken.has(base)) return base;
|
|
@@ -6649,7 +7268,10 @@ var init_portal = __esm({
|
|
|
6649
7268
|
}
|
|
6650
7269
|
// ── Teams ──────────────────────────────────────────────────────────────
|
|
6651
7270
|
async listTeams() {
|
|
6652
|
-
const data = await this.post("account/listTeams.action", {
|
|
7271
|
+
const data = await this.post("account/listTeams.action", {
|
|
7272
|
+
pageSize: "500",
|
|
7273
|
+
pageNumber: "1"
|
|
7274
|
+
});
|
|
6653
7275
|
return data.teams ?? [];
|
|
6654
7276
|
}
|
|
6655
7277
|
// ── App IDs / Bundle IDs ───────────────────────────────────────────────
|
|
@@ -6663,11 +7285,12 @@ var init_portal = __esm({
|
|
|
6663
7285
|
return data.appIds ?? [];
|
|
6664
7286
|
}
|
|
6665
7287
|
async createAppId(params) {
|
|
7288
|
+
const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "App";
|
|
6666
7289
|
const reqParams = {
|
|
6667
7290
|
teamId: this.teamId,
|
|
6668
7291
|
type: "explicit",
|
|
6669
7292
|
identifier: params.identifier,
|
|
6670
|
-
name:
|
|
7293
|
+
name: safeName
|
|
6671
7294
|
};
|
|
6672
7295
|
if (params.enablePush) reqParams.push = "on";
|
|
6673
7296
|
if (params.enableIAP !== false) reqParams.inAppPurchase = "on";
|
|
@@ -6702,6 +7325,191 @@ var init_portal = __esm({
|
|
|
6702
7325
|
featureValue: params.enabled ? "on" : "off"
|
|
6703
7326
|
});
|
|
6704
7327
|
}
|
|
7328
|
+
// ── Application Groups ──────────────────────────────────────────────────
|
|
7329
|
+
//
|
|
7330
|
+
// App Groups (e.g. `group.com.playspina.com`) are the only clean way to
|
|
7331
|
+
// share data — an auth token, in our case — between a host app and an
|
|
7332
|
+
// embedded extension (Messages / Notification Service / etc.). App Store
|
|
7333
|
+
// Connect API keys (.p8) genuinely cannot manage them; Apple only exposes
|
|
7334
|
+
// group create/associate through this legacy session-cookie portal API,
|
|
7335
|
+
// which is why they live on PortalClient (Apple ID session) and not on the
|
|
7336
|
+
// ASC REST client. Endpoint shapes ported 1:1 from Spaceship's
|
|
7337
|
+
// portal_client.rb (listApplicationGroups / addApplicationGroup /
|
|
7338
|
+
// assignApplicationGroupToAppId).
|
|
7339
|
+
/** Apple's internal feature code for the App Groups capability. */
|
|
7340
|
+
static APP_GROUPS_FEATURE = "APG3427HIY";
|
|
7341
|
+
/** List all app groups in the team. */
|
|
7342
|
+
async listAppGroups() {
|
|
7343
|
+
const data = await this.post("account/ios/identifiers/listApplicationGroups.action", {
|
|
7344
|
+
teamId: this.teamId,
|
|
7345
|
+
pageNumber: "1",
|
|
7346
|
+
pageSize: "500",
|
|
7347
|
+
sort: "name=asc"
|
|
7348
|
+
});
|
|
7349
|
+
return data.applicationGroupList ?? [];
|
|
7350
|
+
}
|
|
7351
|
+
/**
|
|
7352
|
+
* Create an app group, returning the raw group record. Idempotent —
|
|
7353
|
+
* Apple rejects a duplicate group identifier ("Multiple App Groups may
|
|
7354
|
+
* not have the same identifier"), so we list first and reuse an existing
|
|
7355
|
+
* group when the identifier already exists in the team.
|
|
7356
|
+
*
|
|
7357
|
+
* Note the two ids on the returned record: `identifier` is the
|
|
7358
|
+
* `group.com.…` string you type, while `applicationGroup` is Apple's
|
|
7359
|
+
* opaque internal handle that association actually requires — do NOT pass
|
|
7360
|
+
* the human identifier to assignAppGroups.
|
|
7361
|
+
*/
|
|
7362
|
+
async createAppGroup(params) {
|
|
7363
|
+
const existing = await this.listAppGroups();
|
|
7364
|
+
const hit = existing.find((g) => g.identifier === params.identifier);
|
|
7365
|
+
if (hit) return hit;
|
|
7366
|
+
const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "App Group";
|
|
7367
|
+
const data = await this.post("account/ios/identifiers/addApplicationGroup.action", {
|
|
7368
|
+
teamId: this.teamId,
|
|
7369
|
+
identifier: params.identifier,
|
|
7370
|
+
name: safeName
|
|
7371
|
+
});
|
|
7372
|
+
return data.applicationGroup;
|
|
7373
|
+
}
|
|
7374
|
+
/**
|
|
7375
|
+
* Enable the App Groups capability on an App ID. Required before an
|
|
7376
|
+
* App ID can have a group associated with it — mirrors Spaceship
|
|
7377
|
+
* enabling `AppService::AppGroup` before `associate_groups`.
|
|
7378
|
+
*/
|
|
7379
|
+
async enableAppGroups(appIdId) {
|
|
7380
|
+
await this.post("account/ios/identifiers/updateService.action", {
|
|
7381
|
+
teamId: this.teamId,
|
|
7382
|
+
displayId: appIdId,
|
|
7383
|
+
featureType: _PortalClient.APP_GROUPS_FEATURE,
|
|
7384
|
+
featureValue: "true"
|
|
7385
|
+
});
|
|
7386
|
+
}
|
|
7387
|
+
/**
|
|
7388
|
+
* Associate one or more app groups with an App ID. `appGroupIds` are the
|
|
7389
|
+
* internal ids (Spaceship's `app_group_id`), NOT the `group.com.…`
|
|
7390
|
+
* identifiers. Apple wants both `appIdId` and `displayId` set to the
|
|
7391
|
+
* App ID's internal id, per Spaceship's associate_groups_with_app.
|
|
7392
|
+
*/
|
|
7393
|
+
async assignAppGroups(params) {
|
|
7394
|
+
await this.post("account/ios/identifiers/assignApplicationGroupToAppId.action", {
|
|
7395
|
+
teamId: this.teamId,
|
|
7396
|
+
appIdId: params.appIdId,
|
|
7397
|
+
displayId: params.appIdId,
|
|
7398
|
+
applicationGroups: params.appGroupIds
|
|
7399
|
+
});
|
|
7400
|
+
}
|
|
7401
|
+
// ── Apple Pay merchants (OMCs) ─────────────────────────────────────────
|
|
7402
|
+
//
|
|
7403
|
+
// Merchant IDs are team-level identifiers (merchant.com.…) that an App ID's
|
|
7404
|
+
// Apple Pay capability must be LINKED to — enabling the capability alone
|
|
7405
|
+
// produces profiles that fail with "doesn't support the … Merchant ID".
|
|
7406
|
+
// Like App Groups, merchants are only manageable through the legacy
|
|
7407
|
+
// session-cookie portal API. Endpoint shapes ported 1:1 from Spaceship's
|
|
7408
|
+
// portal_client.rb (merchants / create_merchant! /
|
|
7409
|
+
// associate_merchants_with_app).
|
|
7410
|
+
/** Apple's internal feature code for the Apple Pay capability. */
|
|
7411
|
+
static APPLE_PAY_FEATURE = "OM633U5T5G";
|
|
7412
|
+
/**
|
|
7413
|
+
* List all Apple Pay merchant IDs in the team. Each record maps
|
|
7414
|
+
* (per Spaceship's merchant.rb attr_mapping): `omcId` = internal id,
|
|
7415
|
+
* `identifier` = the human `merchant.com.…` string, `name` = display name.
|
|
7416
|
+
*/
|
|
7417
|
+
async listMerchants() {
|
|
7418
|
+
const data = await this.post("account/ios/identifiers/listOMCs.action", {
|
|
7419
|
+
teamId: this.teamId,
|
|
7420
|
+
pageNumber: "1",
|
|
7421
|
+
pageSize: "500",
|
|
7422
|
+
sort: "name=asc"
|
|
7423
|
+
});
|
|
7424
|
+
return data.identifierList ?? [];
|
|
7425
|
+
}
|
|
7426
|
+
/**
|
|
7427
|
+
* Register (or reuse) a merchant ID, returning its INTERNAL id (`omcId`) —
|
|
7428
|
+
* Apple's opaque handle that association requires, NOT the human
|
|
7429
|
+
* `merchant.com.…` identifier. Idempotent: lists first and reuses an
|
|
7430
|
+
* existing merchant with the same identifier. Endpoint + params ported 1:1
|
|
7431
|
+
* from Spaceship's create_merchant! (addOMC.action).
|
|
7432
|
+
*/
|
|
7433
|
+
async createMerchant(params) {
|
|
7434
|
+
const existing = await this.listMerchants();
|
|
7435
|
+
const hit = existing.find((m) => m.identifier === params.identifier);
|
|
7436
|
+
if (hit?.omcId) return String(hit.omcId);
|
|
7437
|
+
const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "Merchant";
|
|
7438
|
+
const data = await this.post("account/ios/identifiers/addOMC.action", {
|
|
7439
|
+
name: safeName,
|
|
7440
|
+
identifier: params.identifier,
|
|
7441
|
+
teamId: this.teamId
|
|
7442
|
+
});
|
|
7443
|
+
const rec = data.omcId ?? data;
|
|
7444
|
+
const id = typeof rec === "string" ? rec : rec?.omcId ?? rec?.merchantId;
|
|
7445
|
+
if (!id) {
|
|
7446
|
+
throw new Error("Apple returned no internal merchant id (omcId) for the registered merchant.");
|
|
7447
|
+
}
|
|
7448
|
+
return String(id);
|
|
7449
|
+
}
|
|
7450
|
+
/**
|
|
7451
|
+
* Enable the Apple Pay capability on an App ID. Apple Pay is a standard
|
|
7452
|
+
* AppService (service_id OM633U5T5G) whose service_uri is the default
|
|
7453
|
+
* `updateService.action` — confirmed in Spaceship's app_service.rb.
|
|
7454
|
+
*/
|
|
7455
|
+
async enableApplePay(appIdId) {
|
|
7456
|
+
await this.post("account/ios/identifiers/updateService.action", {
|
|
7457
|
+
teamId: this.teamId,
|
|
7458
|
+
displayId: appIdId,
|
|
7459
|
+
featureType: _PortalClient.APPLE_PAY_FEATURE,
|
|
7460
|
+
featureValue: "true"
|
|
7461
|
+
});
|
|
7462
|
+
}
|
|
7463
|
+
/**
|
|
7464
|
+
* Associate merchant IDs with an App ID's Apple Pay capability. `omcIds`
|
|
7465
|
+
* are the internal ids (each merchant record's `omcId`), NOT the
|
|
7466
|
+
* `merchant.com.…` identifiers. Endpoint + params ported 1:1 from
|
|
7467
|
+
* Spaceship's associate_merchants_with_app (assignOMCToAppId.action — note
|
|
7468
|
+
* the singular "OMC", and NO displayId param).
|
|
7469
|
+
*/
|
|
7470
|
+
async assignMerchants(params) {
|
|
7471
|
+
await this.post("account/ios/identifiers/assignOMCToAppId.action", {
|
|
7472
|
+
teamId: this.teamId,
|
|
7473
|
+
appIdId: params.appIdId,
|
|
7474
|
+
omcIds: params.omcIds
|
|
7475
|
+
});
|
|
7476
|
+
}
|
|
7477
|
+
/**
|
|
7478
|
+
* Create an Apple Pay **Payment Processing Certificate** on a merchant ID
|
|
7479
|
+
* from a CSR — the certificate a PSP (e.g. Stripe) hands you as a CSR, which
|
|
7480
|
+
* must be uploaded to the Merchant ID in Apple Developer, then the resulting
|
|
7481
|
+
* `.cer` downloaded and given back to the PSP so it can decrypt payment
|
|
7482
|
+
* tokens. This is the one manual Apple-portal step in wiring up Apple Pay.
|
|
7483
|
+
*
|
|
7484
|
+
* Ported 1:1 from Spaceship's create_certificate! (submitCertificateRequest
|
|
7485
|
+
* .action): the merchant's INTERNAL id (`omcId`) is passed as BOTH `appIdId`
|
|
7486
|
+
* and `specialIdentifierDisplayId` — the latter is how the portal scopes a
|
|
7487
|
+
* cert to a merchant/special identifier rather than an App ID. The cert type
|
|
7488
|
+
* is APPLE_PAY (`4APLUP237T`). Returns the created certRequest (its
|
|
7489
|
+
* `certificateId` feeds `downloadApplePayCertificate`).
|
|
7490
|
+
*
|
|
7491
|
+
* @param omcId the merchant's internal id (from createMerchant/listMerchants)
|
|
7492
|
+
* @param csrContent the PEM CSR string (Stripe's "Apple Pay CSR")
|
|
7493
|
+
*/
|
|
7494
|
+
async createApplePayCertificate(params) {
|
|
7495
|
+
const data = await this.post("account/ios/certificate/submitCertificateRequest.action", {
|
|
7496
|
+
teamId: this.teamId,
|
|
7497
|
+
type: _PortalClient.CERT_TYPES.APPLE_PAY,
|
|
7498
|
+
csrContent: params.csrContent,
|
|
7499
|
+
appIdId: params.omcId,
|
|
7500
|
+
specialIdentifierDisplayId: params.omcId
|
|
7501
|
+
});
|
|
7502
|
+
return data.certRequest;
|
|
7503
|
+
}
|
|
7504
|
+
/**
|
|
7505
|
+
* Download an Apple Pay Payment Processing Certificate's raw bytes by its
|
|
7506
|
+
* certificate id (from `createApplePayCertificate`). Thin wrapper over
|
|
7507
|
+
* `downloadCertificate` pinned to the APPLE_PAY type — the DER `.cer` you
|
|
7508
|
+
* upload back into Stripe.
|
|
7509
|
+
*/
|
|
7510
|
+
async downloadApplePayCertificate(certificateId) {
|
|
7511
|
+
return this.downloadCertificate(certificateId, _PortalClient.CERT_TYPES.APPLE_PAY);
|
|
7512
|
+
}
|
|
6705
7513
|
// ── Certificates ───────────────────────────────────────────────────────
|
|
6706
7514
|
async listCertificates(types) {
|
|
6707
7515
|
const iosTypes = types ?? [
|
|
@@ -7241,6 +8049,9 @@ var init_client = __esm({
|
|
|
7241
8049
|
init_in_app_purchases();
|
|
7242
8050
|
init_game_center_detail();
|
|
7243
8051
|
init_xcode_metrics();
|
|
8052
|
+
init_app_asset_libraries();
|
|
8053
|
+
init_app_asset_library_assets();
|
|
8054
|
+
init_app_asset_library_placements();
|
|
7244
8055
|
init_auth();
|
|
7245
8056
|
init_request();
|
|
7246
8057
|
init_apple_session();
|
|
@@ -7282,6 +8093,10 @@ var init_client = __esm({
|
|
|
7282
8093
|
inAppPurchases;
|
|
7283
8094
|
gameCenterDetail;
|
|
7284
8095
|
xcodeMetrics;
|
|
8096
|
+
appAssetLibraries;
|
|
8097
|
+
appAssetLibraryImages;
|
|
8098
|
+
appAssetLibraryVideos;
|
|
8099
|
+
appAssetLibraryPlacements;
|
|
7285
8100
|
constructor(config) {
|
|
7286
8101
|
const tokenManager = new TokenManager({
|
|
7287
8102
|
keyId: config.keyId,
|
|
@@ -7325,6 +8140,10 @@ var init_client = __esm({
|
|
|
7325
8140
|
this.inAppPurchases = new InAppPurchasesResource(this.http);
|
|
7326
8141
|
this.gameCenterDetail = new GameCenterDetailResource(this.http);
|
|
7327
8142
|
this.xcodeMetrics = new XcodeMetricsResource(this.http);
|
|
8143
|
+
this.appAssetLibraries = new AppAssetLibrariesResource(this.http);
|
|
8144
|
+
this.appAssetLibraryImages = new AppAssetLibraryImagesResource(this.http);
|
|
8145
|
+
this.appAssetLibraryVideos = new AppAssetLibraryVideosResource(this.http);
|
|
8146
|
+
this.appAssetLibraryPlacements = new AppAssetLibraryPlacementsResource(this.http);
|
|
7328
8147
|
}
|
|
7329
8148
|
async request(path, options) {
|
|
7330
8149
|
return this.http.request(path, options);
|