@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.
Files changed (126) hide show
  1. package/README.md +58 -1
  2. package/dist/actions/index.cjs +891 -72
  3. package/dist/actions/index.cjs.map +1 -1
  4. package/dist/actions/index.mjs +892 -73
  5. package/dist/actions/index.mjs.map +1 -1
  6. package/dist/apple-session-jUzBrql5.d.cts +122 -0
  7. package/dist/apple-session-jUzBrql5.d.ts +122 -0
  8. package/dist/cert/index.cjs +17868 -24
  9. package/dist/cert/index.cjs.map +1 -1
  10. package/dist/cert/index.d.cts +5 -4
  11. package/dist/cert/index.d.ts +5 -4
  12. package/dist/cert/index.mjs +17853 -7
  13. package/dist/cert/index.mjs.map +1 -1
  14. package/dist/{cert-e5GAgAPb.d.ts → cert-RvXmdMke.d.ts} +2 -2
  15. package/dist/{cert-uLLdb5a7.d.cts → cert-eOoxGMN1.d.cts} +2 -2
  16. package/dist/cli/index.cjs +1364 -78
  17. package/dist/cli/index.cjs.map +1 -1
  18. package/dist/cli/index.d.cts +3 -2
  19. package/dist/cli/index.d.ts +3 -2
  20. package/dist/cli/index.mjs +1365 -79
  21. package/dist/cli/index.mjs.map +1 -1
  22. package/dist/client/index.cjs +480 -31
  23. package/dist/client/index.cjs.map +1 -1
  24. package/dist/client/index.d.cts +3 -2
  25. package/dist/client/index.d.ts +3 -2
  26. package/dist/client/index.mjs +480 -31
  27. package/dist/client/index.mjs.map +1 -1
  28. package/dist/deliver/index.cjs +334 -13
  29. package/dist/deliver/index.cjs.map +1 -1
  30. package/dist/deliver/index.d.cts +4 -3
  31. package/dist/deliver/index.d.ts +4 -3
  32. package/dist/deliver/index.mjs +330 -15
  33. package/dist/deliver/index.mjs.map +1 -1
  34. package/dist/{enums-B609j2rb.d.cts → enums-yXNVh3UI.d.cts} +8 -1
  35. package/dist/{enums-B609j2rb.d.ts → enums-yXNVh3UI.d.ts} +8 -1
  36. package/dist/gradle/index.cjs +222 -0
  37. package/dist/gradle/index.cjs.map +1 -0
  38. package/dist/gradle/index.d.cts +1 -0
  39. package/dist/gradle/index.d.ts +1 -0
  40. package/dist/gradle/index.mjs +217 -0
  41. package/dist/gradle/index.mjs.map +1 -0
  42. package/dist/gym/index.d.cts +2 -2
  43. package/dist/gym/index.d.ts +2 -2
  44. package/dist/index-8djTPW8O.d.ts +104 -0
  45. package/dist/{index-DrUpRXRG.d.cts → index-B6-MJpO4.d.cts} +1 -1
  46. package/dist/{index-Clyf4-lu.d.cts → index-BE7bwNff.d.cts} +1 -1
  47. package/dist/index-BGhV0gS-.d.ts +65 -0
  48. package/dist/index-BaNcf7jt.d.cts +201 -0
  49. package/dist/{index-Di4Ic0EE.d.ts → index-BvyZ4uiw.d.ts} +3 -3
  50. package/dist/{index-CEldrbyz.d.ts → index-C01Rkrir.d.ts} +2 -2
  51. package/dist/{index-jeI44Q5G.d.ts → index-C9mHBJED.d.ts} +1 -1
  52. package/dist/{index-BgHagzj1.d.cts → index-CAEiXVWd.d.ts} +9 -1
  53. package/dist/index-CBf0-NUQ.d.ts +201 -0
  54. package/dist/index-CXJlvmB4.d.cts +63 -0
  55. package/dist/index-CXJlvmB4.d.ts +63 -0
  56. package/dist/{index-BNP7Bpu5.d.ts → index-D1GHEM3c.d.ts} +32 -3
  57. package/dist/index-DFstwgGK.d.cts +65 -0
  58. package/dist/{index-DrCOh0Vh.d.cts → index-DGkgRnZp.d.cts} +2 -2
  59. package/dist/{index-CtydPNxA.d.cts → index-DLJso7Vs.d.cts} +32 -3
  60. package/dist/{index-BN0iWhuw.d.cts → index-DWOLZNlz.d.cts} +2 -2
  61. package/dist/{index-BjkOUQcQ.d.cts → index-DfkIz7i0.d.cts} +18 -4
  62. package/dist/{index-EKU_G1G5.d.ts → index-DjC_gmnL.d.ts} +18 -4
  63. package/dist/index-DnHQ2uT2.d.cts +104 -0
  64. package/dist/{index-CdfgEZwh.d.cts → index-DrmS802s.d.cts} +356 -130
  65. package/dist/index-Dv-Mek07.d.cts +84 -0
  66. package/dist/index-Dv-Mek07.d.ts +84 -0
  67. package/dist/{index-DHxi-QPC.d.cts → index-icIH5e5V.d.cts} +3 -3
  68. package/dist/{index-mddpvrRX.d.ts → index-laE3_wm9.d.cts} +9 -1
  69. package/dist/{index-BwwpAlk2.d.ts → index-lsuS2R1T.d.ts} +356 -130
  70. package/dist/{index-DImN9PWc.d.ts → index-mEEB7An6.d.ts} +1 -1
  71. package/dist/{index-C2YLoFVl.d.ts → index-rTZOdMRE.d.ts} +2 -2
  72. package/dist/index.cjs +20496 -217
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.cts +278 -22
  75. package/dist/index.d.ts +278 -22
  76. package/dist/index.mjs +20489 -220
  77. package/dist/index.mjs.map +1 -1
  78. package/dist/lane/index.cjs +891 -72
  79. package/dist/lane/index.cjs.map +1 -1
  80. package/dist/lane/index.mjs +892 -73
  81. package/dist/lane/index.mjs.map +1 -1
  82. package/dist/match/index.d.cts +6 -5
  83. package/dist/match/index.d.ts +6 -5
  84. package/dist/pem/index.d.cts +4 -3
  85. package/dist/pem/index.d.ts +4 -3
  86. package/dist/pilot/index.cjs +8 -4
  87. package/dist/pilot/index.cjs.map +1 -1
  88. package/dist/pilot/index.d.cts +4 -3
  89. package/dist/pilot/index.d.ts +4 -3
  90. package/dist/pilot/index.mjs +10 -6
  91. package/dist/pilot/index.mjs.map +1 -1
  92. package/dist/playstore/index.cjs +207 -0
  93. package/dist/playstore/index.cjs.map +1 -0
  94. package/dist/playstore/index.d.cts +1 -0
  95. package/dist/playstore/index.d.ts +1 -0
  96. package/dist/playstore/index.mjs +203 -0
  97. package/dist/playstore/index.mjs.map +1 -0
  98. package/dist/precheck/index.d.cts +4 -3
  99. package/dist/precheck/index.d.ts +4 -3
  100. package/dist/privacy/index.cjs +207 -0
  101. package/dist/privacy/index.cjs.map +1 -0
  102. package/dist/privacy/index.d.cts +2 -0
  103. package/dist/privacy/index.d.ts +2 -0
  104. package/dist/privacy/index.mjs +195 -0
  105. package/dist/privacy/index.mjs.map +1 -0
  106. package/dist/produce/index.cjs +38 -18
  107. package/dist/produce/index.cjs.map +1 -1
  108. package/dist/produce/index.d.cts +4 -3
  109. package/dist/produce/index.d.ts +4 -3
  110. package/dist/produce/index.mjs +38 -19
  111. package/dist/produce/index.mjs.map +1 -1
  112. package/dist/review/index.cjs +141 -0
  113. package/dist/review/index.cjs.map +1 -0
  114. package/dist/review/index.d.cts +2 -0
  115. package/dist/review/index.d.ts +2 -0
  116. package/dist/review/index.mjs +137 -0
  117. package/dist/review/index.mjs.map +1 -0
  118. package/dist/sigh/index.d.cts +4 -3
  119. package/dist/sigh/index.d.ts +4 -3
  120. package/dist/xcode/index.cjs +1 -1
  121. package/dist/xcode/index.cjs.map +1 -1
  122. package/dist/xcode/index.mjs +1 -1
  123. package/dist/xcode/index.mjs.map +1 -1
  124. package/package.json +27 -1
  125. package/dist/index-BbY_H_f6.d.ts +0 -99
  126. package/dist/index-XsMsVc3O.d.cts +0 -99
@@ -1,2 +1,3 @@
1
- export { a7 as ApiKeyRole, a as AppStoreConnectClient, d as AppStoreConnectConfig, b as AppStoreConnectError, A as AppleSession, _ as AppleSessionConfig, a2 as BeginLoginResult, $ as ContractMessage, a6 as IrisApiKey, a5 as IrisAuthError, a4 as IrisClient, i as JsonApiError, a9 as JsonApiErrorResponse, h as JsonApiListResponse, a8 as JsonApiRelationship, J as JsonApiResource, g as JsonApiResponse, L as ListParams, a0 as PendingLogin, a3 as PortalClient, Z as ProgramLicenseAgreementError, c as RateLimitError, R as RequestClient, f as RequestOptions, e as TokenConfig, T as TokenManager, a1 as TwoFactorChallenge, V as beginLogin, Y as checkPortalResponse, S as loginWithAppleId, X as requestSmsCode, W as submitTwoFactorCode } from '../index-CdfgEZwh.cjs';
2
- import '../enums-B609j2rb.cjs';
1
+ export { ac as ApiKeyRole, A as AppStoreConnectClient, c as AppStoreConnectConfig, a as AppStoreConnectError, ab as IrisApiKey, aa as IrisAuthError, a9 as IrisClient, h as JsonApiError, ae as JsonApiErrorResponse, g as JsonApiListResponse, ad as JsonApiRelationship, J as JsonApiResource, f as JsonApiResponse, L as ListParams, a8 as PortalClient, b as RateLimitError, R as RequestClient, e as RequestOptions, d as TokenConfig, T as TokenManager } from '../index-DrmS802s.cjs';
2
+ export { A as AppleSession, a as AppleSessionConfig, B as BeginLoginResult, C as ContractMessage, d as PendingLogin, P as ProgramLicenseAgreementError, T as TwoFactorChallenge, b as beginLogin, c as checkPortalResponse, l as loginWithAppleId, r as requestSmsCode, s as submitTwoFactorCode } from '../apple-session-jUzBrql5.cjs';
3
+ import '../enums-yXNVh3UI.cjs';
@@ -1,2 +1,3 @@
1
- export { a7 as ApiKeyRole, a as AppStoreConnectClient, d as AppStoreConnectConfig, b as AppStoreConnectError, A as AppleSession, _ as AppleSessionConfig, a2 as BeginLoginResult, $ as ContractMessage, a6 as IrisApiKey, a5 as IrisAuthError, a4 as IrisClient, i as JsonApiError, a9 as JsonApiErrorResponse, h as JsonApiListResponse, a8 as JsonApiRelationship, J as JsonApiResource, g as JsonApiResponse, L as ListParams, a0 as PendingLogin, a3 as PortalClient, Z as ProgramLicenseAgreementError, c as RateLimitError, R as RequestClient, f as RequestOptions, e as TokenConfig, T as TokenManager, a1 as TwoFactorChallenge, V as beginLogin, Y as checkPortalResponse, S as loginWithAppleId, X as requestSmsCode, W as submitTwoFactorCode } from '../index-BwwpAlk2.js';
2
- import '../enums-B609j2rb.js';
1
+ export { ac as ApiKeyRole, A as AppStoreConnectClient, c as AppStoreConnectConfig, a as AppStoreConnectError, ab as IrisApiKey, aa as IrisAuthError, a9 as IrisClient, h as JsonApiError, ae as JsonApiErrorResponse, g as JsonApiListResponse, ad as JsonApiRelationship, J as JsonApiResource, f as JsonApiResponse, L as ListParams, a8 as PortalClient, b as RateLimitError, R as RequestClient, e as RequestOptions, d as TokenConfig, T as TokenManager } from '../index-lsuS2R1T.js';
2
+ export { A as AppleSession, a as AppleSessionConfig, B as BeginLoginResult, C as ContractMessage, d as PendingLogin, P as ProgramLicenseAgreementError, T as TwoFactorChallenge, b as beginLogin, c as checkPortalResponse, l as loginWithAppleId, r as requestSmsCode, s as submitTwoFactorCode } from '../apple-session-jUzBrql5.js';
3
+ import '../enums-yXNVh3UI.js';
@@ -164,6 +164,20 @@ function applePbkdf2(password, salt, iterations, keyLength, protocol) {
164
164
 
165
165
  // src/client/apple-session.ts
166
166
  var AUTH_BASE = "https://idmsa.apple.com/appleauth/auth";
167
+ async function appleJson(res, what) {
168
+ const text = await res.text();
169
+ try {
170
+ return JSON.parse(text);
171
+ } catch {
172
+ const contentType = (res.headers.get("content-type") ?? "").split(";")[0].trim();
173
+ const title = /<title>([^<]{1,120})<\/title>/i.exec(text)?.[1]?.trim();
174
+ const looksHtml = /^\s*<(!doctype|html)/i.test(text) || contentType === "text/html";
175
+ const page = looksHtml ? "an HTML page" : contentType ? `a ${contentType} response` : "a non-JSON response";
176
+ throw new Error(
177
+ `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.`
178
+ );
179
+ }
180
+ }
167
181
  var OLYMPUS_SESSION = "https://appstoreconnect.apple.com/olympus/v1/session";
168
182
  var WIDGET_KEY_URL = "https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com";
169
183
  var ProgramLicenseAgreementError = class extends Error {
@@ -250,7 +264,7 @@ async function beginLogin(config) {
250
264
  }
251
265
  async function fetchTwoFactorChallenge(pending) {
252
266
  const r = await fetch(`${AUTH_BASE}`, { headers: buildAuthHeadersFromPending(pending) });
253
- const body = await r.json();
267
+ const body = await appleJson(r, "two-factor challenge");
254
268
  const trustedPhoneNumbers = (body.trustedPhoneNumbers ?? []).map((p) => ({
255
269
  id: p.id,
256
270
  numberWithDialCode: p.numberWithDialCode,
@@ -271,7 +285,8 @@ async function requestSmsCode(input) {
271
285
  body: JSON.stringify({ phoneNumber: { id: input.phoneId }, mode: input.mode ?? "sms" })
272
286
  });
273
287
  }
274
- async function submitTwoFactorCode(input) {
288
+ async function submitTwoFactorCode(rawInput) {
289
+ const input = { ...rawInput, code: rawInput.code.replace(/[\s\u00a0]+/g, "") };
275
290
  const log = input.onLog ?? (() => {
276
291
  });
277
292
  let path;
@@ -383,9 +398,12 @@ async function doSirp(user, password, serviceKey, cookieStr, hashcash) {
383
398
  body: JSON.stringify({ a: aBase64, accountName: user, protocols: ["s2k", "s2k_fo"] }),
384
399
  redirect: "manual"
385
400
  });
386
- const initBody = await initRes.json();
401
+ const initBody = await appleJson(initRes, "sign-in init");
387
402
  if (initBody.serviceErrors) throw new Error(`Apple auth error: ${JSON.stringify(initBody.serviceErrors)}`);
388
403
  const { iteration: iterations, salt: saltB64, b: bB64, c, protocol } = initBody;
404
+ if (!iterations || !saltB64 || !bB64 || !c || !protocol) {
405
+ throw new Error(`Apple's sign-in init answered without a challenge (HTTP ${initRes.status}): ${JSON.stringify(initBody).slice(0, 300)}`);
406
+ }
389
407
  const salt = Buffer.from(saltB64, "base64");
390
408
  const b = Buffer.from(bB64, "base64");
391
409
  const encryptedPassword = applePbkdf2(password, salt, iterations, 32, protocol);
@@ -428,7 +446,7 @@ async function fetchOlympusSession(session) {
428
446
  const cookieStr = Object.entries(session.cookies).map(([k, v]) => `${k}=${v}`).join("; ");
429
447
  const res = await fetch(OLYMPUS_SESSION, { headers: { Cookie: cookieStr, Accept: "application/json" } });
430
448
  if (res.ok) {
431
- const body = await res.json();
449
+ const body = await appleJson(res, "session lookup");
432
450
  if (body?.user?.emailAddress) session.userEmail = body.user.emailAddress;
433
451
  if (body?.provider?.providerId) session.teamId = String(body.provider.providerId);
434
452
  }
@@ -440,7 +458,7 @@ async function fetchContractMessages(session) {
440
458
  headers: { Cookie: cookieStr, Accept: "application/json" }
441
459
  });
442
460
  if (!res.ok) return [];
443
- const body = await res.json();
461
+ const body = await appleJson(res, "agreements lookup");
444
462
  return (body ?? []).map((m) => ({
445
463
  id: m.id ?? "",
446
464
  group: m.group ?? "",
@@ -451,10 +469,23 @@ async function fetchContractMessages(session) {
451
469
  return [];
452
470
  }
453
471
  }
454
- async function fetchServiceKey() {
455
- const res = await fetch(WIDGET_KEY_URL);
456
- const body = await res.json();
457
- if (!body?.authServiceKey) throw new Error("Could not fetch Apple service key");
472
+ var LOGOUT_URL = "https://appstoreconnect.apple.com/logout";
473
+ function serviceKeyFromLocation(location) {
474
+ if (!location) return null;
475
+ try {
476
+ const key = new URL(location).searchParams.get("widgetKey")?.trim();
477
+ return key && /^[0-9a-f]{16,}$/i.test(key) ? key : null;
478
+ } catch {
479
+ return null;
480
+ }
481
+ }
482
+ async function fetchServiceKey(fetchImpl = fetch) {
483
+ const head = await fetchImpl(LOGOUT_URL, { method: "HEAD", redirect: "manual" });
484
+ const fromRedirect = serviceKeyFromLocation(head.headers.get("location"));
485
+ if (fromRedirect) return fromRedirect;
486
+ const res = await fetchImpl(WIDGET_KEY_URL);
487
+ const body = await appleJson(res, "service key request");
488
+ 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.");
458
489
  return body.authServiceKey;
459
490
  }
460
491
  function buildAuthHeaders(session) {
@@ -980,6 +1011,43 @@ var AppsResource = class {
980
1011
  });
981
1012
  return results[0] ?? null;
982
1013
  }
1014
+ /**
1015
+ * Create an App Store Connect **app record** (the thing that shows up
1016
+ * under "My Apps" and that TestFlight / App Store uploads require).
1017
+ *
1018
+ * This is distinct from registering an App ID in the Developer Portal —
1019
+ * you need BOTH. The Developer Portal App ID unlocks provisioning
1020
+ * profiles and entitlements; the App Store Connect app record unlocks
1021
+ * uploads. `upload_to_testflight` fails with "Couldn't find app
1022
+ * 'com.foo.bar' on the account…" when this record is missing.
1023
+ *
1024
+ * `bundleIdId` is the REST API primary key of the bundle id resource
1025
+ * (not the identifier string) — fetch it via `bundleIds.find(identifier)`
1026
+ * or from the response of `bundleIds.create()`.
1027
+ *
1028
+ * `sku` is an arbitrary unique string Apple uses to identify the app in
1029
+ * their internal records. If you don't have a meaningful one, pass the
1030
+ * bundle identifier itself or append a timestamp for uniqueness.
1031
+ */
1032
+ async create(params) {
1033
+ const response = await this.http.post("/v1/apps", {
1034
+ data: {
1035
+ type: "apps",
1036
+ attributes: {
1037
+ bundleId: params.bundleIdentifier,
1038
+ name: params.appName,
1039
+ sku: params.sku,
1040
+ primaryLocale: params.primaryLocale ?? "en-US"
1041
+ },
1042
+ relationships: {
1043
+ bundleId: {
1044
+ data: { type: "bundleIds", id: params.bundleIdId }
1045
+ }
1046
+ }
1047
+ }
1048
+ });
1049
+ return response.data;
1050
+ }
983
1051
  };
984
1052
 
985
1053
  // src/resources/builds.ts
@@ -2015,6 +2083,19 @@ var PreReleaseVersionsResource = class {
2015
2083
  };
2016
2084
 
2017
2085
  // src/resources/review-submission-items.ts
2086
+ var ITEM_RELATIONSHIPS = {
2087
+ appStoreVersionId: ["appStoreVersion", "appStoreVersions"],
2088
+ appCustomProductPageVersionId: ["appCustomProductPageVersion", "appCustomProductPageVersions"],
2089
+ appStoreVersionExperimentId: ["appStoreVersionExperiment", "appStoreVersionExperiments"],
2090
+ appStoreVersionExperimentV2Id: ["appStoreVersionExperimentV2", "appStoreVersionExperiments"],
2091
+ appEventId: ["appEvent", "appEvents"],
2092
+ appAssetLibraryImageId: ["appAssetLibraryImage", "appAssetLibraryImages"],
2093
+ appAssetLibraryVideoId: ["appAssetLibraryVideo", "appAssetLibraryVideos"],
2094
+ backgroundAssetVersionId: ["backgroundAssetVersion", "backgroundAssetVersions"],
2095
+ inAppPurchaseVersionId: ["inAppPurchaseVersion", "inAppPurchaseVersions"],
2096
+ subscriptionVersionId: ["subscriptionVersion", "subscriptionVersions"],
2097
+ subscriptionGroupVersionId: ["subscriptionGroupVersion", "subscriptionGroupVersions"]
2098
+ };
2018
2099
  var ReviewSubmissionItemsResource = class {
2019
2100
  constructor(http) {
2020
2101
  this.http = http;
@@ -2036,32 +2117,20 @@ var ReviewSubmissionItemsResource = class {
2036
2117
  );
2037
2118
  return response.data;
2038
2119
  }
2120
+ /**
2121
+ * Add one item to a review submission. Besides versions, asset library
2122
+ * images and videos can be submitted on their own (ASC API 4.5.1) so
2123
+ * creative assets are approved ahead of a campaign.
2124
+ */
2039
2125
  async create(params) {
2040
2126
  const relationships = {
2041
2127
  reviewSubmission: {
2042
2128
  data: { type: "reviewSubmissions", id: params.reviewSubmissionId }
2043
2129
  }
2044
2130
  };
2045
- if (params.appStoreVersionId) {
2046
- relationships.appStoreVersion = {
2047
- data: { type: "appStoreVersions", id: params.appStoreVersionId }
2048
- };
2049
- }
2050
- if (params.appCustomProductPageVersionId) {
2051
- relationships.appCustomProductPageVersion = {
2052
- data: {
2053
- type: "appCustomProductPageVersions",
2054
- id: params.appCustomProductPageVersionId
2055
- }
2056
- };
2057
- }
2058
- if (params.appStoreVersionExperimentId) {
2059
- relationships.appStoreVersionExperiment = {
2060
- data: {
2061
- type: "appStoreVersionExperiments",
2062
- id: params.appStoreVersionExperimentId
2063
- }
2064
- };
2131
+ for (const [param, [relationship, type]] of Object.entries(ITEM_RELATIONSHIPS)) {
2132
+ const id = params[param];
2133
+ if (id) relationships[relationship] = { data: { type, id } };
2065
2134
  }
2066
2135
  const response = await this.http.post(
2067
2136
  "/v1/reviewSubmissionItems",
@@ -2265,6 +2334,189 @@ var XcodeMetricsResource = class {
2265
2334
  }
2266
2335
  };
2267
2336
 
2337
+ // src/resources/app-asset-libraries.ts
2338
+ var AppAssetLibrariesResource = class {
2339
+ constructor(http) {
2340
+ this.http = http;
2341
+ }
2342
+ http;
2343
+ async getForApp(appId) {
2344
+ const response = await this.http.get(
2345
+ `/v1/apps/${appId}/assetLibrary`
2346
+ );
2347
+ return response.data;
2348
+ }
2349
+ async listImages(libraryId, params = {}) {
2350
+ return this.http.all(`/v1/appAssetLibraries/${libraryId}/images`, params);
2351
+ }
2352
+ async listVideos(libraryId, params = {}) {
2353
+ return this.http.all(`/v1/appAssetLibraries/${libraryId}/videos`, params);
2354
+ }
2355
+ /**
2356
+ * Specs, placement groups and limits. Apple adds placement groups (new
2357
+ * devices) without an API version bump, so read them from here rather than
2358
+ * hard-coding display types.
2359
+ */
2360
+ async refData(params = {}) {
2361
+ const filter = {};
2362
+ if (params.features?.length) filter.features = params.features;
2363
+ if (params.placementTypes?.length) filter.placementTypes = params.placementTypes;
2364
+ if (params.placementProfileGroups?.length) filter.placementProfileGroups = params.placementProfileGroups;
2365
+ const data = await this.http.all("/v1/appAssetLibraryRefData", { filter });
2366
+ return mergeRefData(data.map((d) => d.attributes));
2367
+ }
2368
+ };
2369
+ function mergeRefData(parts) {
2370
+ const merged = {
2371
+ features: [],
2372
+ placementProfileGroups: [],
2373
+ imageSpecs: [],
2374
+ videoSpecs: [],
2375
+ placementTypes: [],
2376
+ displayClasses: []
2377
+ };
2378
+ for (const part of parts) {
2379
+ for (const key of Object.keys(merged)) {
2380
+ merged[key].push(...part[key] ?? []);
2381
+ }
2382
+ }
2383
+ return merged;
2384
+ }
2385
+
2386
+ // src/resources/app-asset-library-assets.ts
2387
+ var AssetLibraryMediaResource = class {
2388
+ constructor(http) {
2389
+ this.http = http;
2390
+ }
2391
+ http;
2392
+ async get(id) {
2393
+ const response = await this.http.get(`/v1/${this.type}/${id}`);
2394
+ return response.data;
2395
+ }
2396
+ async reserve(params, extra = {}) {
2397
+ const { libraryId, ...attributes } = params;
2398
+ const response = await this.http.post(`/v1/${this.type}`, {
2399
+ data: {
2400
+ type: this.type,
2401
+ attributes: { ...attributes, ...extra },
2402
+ relationships: {
2403
+ assetLibrary: { data: { type: "appAssetLibraries", id: libraryId } }
2404
+ }
2405
+ }
2406
+ });
2407
+ return response.data;
2408
+ }
2409
+ async patch(id, attributes) {
2410
+ const response = await this.http.patch(`/v1/${this.type}/${id}`, {
2411
+ data: { type: this.type, id, attributes }
2412
+ });
2413
+ return response.data;
2414
+ }
2415
+ /** Fails with 409 STATE_ERROR.ASSET_HAS_PLACEMENTS while the asset is still placed. */
2416
+ async delete(id) {
2417
+ await this.http.delete(`/v1/${this.type}/${id}`);
2418
+ }
2419
+ async listPlacements(id, params = {}) {
2420
+ return this.http.all(`/v1/${this.type}/${id}/placements`, params);
2421
+ }
2422
+ };
2423
+ var AppAssetLibraryImagesResource = class extends AssetLibraryMediaResource {
2424
+ type = "appAssetLibraryImages";
2425
+ async create(params) {
2426
+ return this.reserve(params);
2427
+ }
2428
+ async update(id, attributes) {
2429
+ return this.patch(id, { ...attributes });
2430
+ }
2431
+ };
2432
+ var AppAssetLibraryVideosResource = class extends AssetLibraryMediaResource {
2433
+ type = "appAssetLibraryVideos";
2434
+ async create(params) {
2435
+ const { previewFrameTimeCode, ...rest } = params;
2436
+ return this.reserve(rest, previewFrameTimeCode ? { previewFrameTimeCode } : {});
2437
+ }
2438
+ async update(id, attributes) {
2439
+ return this.patch(id, { ...attributes });
2440
+ }
2441
+ };
2442
+
2443
+ // src/resources/app-asset-library-placements.ts
2444
+ var TARGET_TYPES = {
2445
+ appStoreVersionLocalization: "appStoreVersionLocalizations",
2446
+ appCustomProductPageLocalization: "appCustomProductPageLocalizations",
2447
+ appEventLocalization: "appEventLocalizations",
2448
+ appStoreVersionExperimentTreatmentLocalization: "appStoreVersionExperimentTreatmentLocalizations"
2449
+ };
2450
+ function targetRelationship(target) {
2451
+ return { [target.kind]: { data: { type: TARGET_TYPES[target.kind], id: target.id } } };
2452
+ }
2453
+ var AppAssetLibraryPlacementsResource = class {
2454
+ constructor(http) {
2455
+ this.http = http;
2456
+ }
2457
+ http;
2458
+ /**
2459
+ * Place an image or video on one localization. Placements cannot be edited —
2460
+ * delete and recreate. The parent version/page must be editable.
2461
+ */
2462
+ async create(params) {
2463
+ if (!params.imageId === !params.videoId) {
2464
+ throw new Error("A placement needs exactly one of imageId or videoId");
2465
+ }
2466
+ const media = params.imageId ? { image: { data: { type: "appAssetLibraryImages", id: params.imageId } } } : { video: { data: { type: "appAssetLibraryVideos", id: params.videoId } } };
2467
+ const response = await this.http.post(
2468
+ "/v1/appAssetLibraryPlacements",
2469
+ {
2470
+ data: {
2471
+ type: "appAssetLibraryPlacements",
2472
+ attributes: { placementType: params.placementType, placementGroup: params.placementGroup },
2473
+ relationships: { ...media, ...targetRelationship(params.target) }
2474
+ }
2475
+ }
2476
+ );
2477
+ return response.data;
2478
+ }
2479
+ async get(id, params = {}) {
2480
+ const response = await this.http.get(
2481
+ `/v1/appAssetLibraryPlacements/${id}`,
2482
+ params.include ? { include: params.include } : void 0
2483
+ );
2484
+ return response.data;
2485
+ }
2486
+ async delete(id) {
2487
+ await this.http.delete(`/v1/appAssetLibraryPlacements/${id}`);
2488
+ }
2489
+ /** Placements on one localization, in display order by default. */
2490
+ async listForTarget(target, params = {}) {
2491
+ const filter = {};
2492
+ if (params.placementType) filter.placementType = params.placementType;
2493
+ if (params.placementGroup) filter.placementGroup = params.placementGroup;
2494
+ const listParams = { filter, include: params.include, sort: "placementGroupPosition" };
2495
+ return this.http.all(
2496
+ `/v1/${TARGET_TYPES[target.kind]}/${target.id}/placements`,
2497
+ listParams
2498
+ );
2499
+ }
2500
+ /** Set the display order of a placement group. In-app event localizations cannot be ordered. */
2501
+ async order(params) {
2502
+ if (params.target.kind === "appEventLocalization") {
2503
+ throw new Error("In-app event placements cannot be ordered");
2504
+ }
2505
+ await this.http.post("/v1/appAssetLibraryPlacementOrderingRequests", {
2506
+ data: {
2507
+ type: "appAssetLibraryPlacementOrderingRequests",
2508
+ attributes: { placementGroup: params.placementGroup },
2509
+ relationships: {
2510
+ orderedPlacements: {
2511
+ data: params.placementIds.map((id) => ({ type: "appAssetLibraryPlacements", id }))
2512
+ },
2513
+ ...targetRelationship(params.target)
2514
+ }
2515
+ }
2516
+ });
2517
+ }
2518
+ };
2519
+
2268
2520
  // src/client/portal.ts
2269
2521
  var PORTAL_BASE = "https://developer.apple.com/services-account/QH65B2";
2270
2522
  function pickUniqueKeyName(base, taken) {
@@ -2600,7 +2852,10 @@ var PortalClient = class _PortalClient {
2600
2852
  }
2601
2853
  // ── Teams ──────────────────────────────────────────────────────────────
2602
2854
  async listTeams() {
2603
- const data = await this.post("account/listTeams.action", {});
2855
+ const data = await this.post("account/listTeams.action", {
2856
+ pageSize: "500",
2857
+ pageNumber: "1"
2858
+ });
2604
2859
  return data.teams ?? [];
2605
2860
  }
2606
2861
  // ── App IDs / Bundle IDs ───────────────────────────────────────────────
@@ -2614,11 +2869,12 @@ var PortalClient = class _PortalClient {
2614
2869
  return data.appIds ?? [];
2615
2870
  }
2616
2871
  async createAppId(params) {
2872
+ const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "App";
2617
2873
  const reqParams = {
2618
2874
  teamId: this.teamId,
2619
2875
  type: "explicit",
2620
2876
  identifier: params.identifier,
2621
- name: params.name
2877
+ name: safeName
2622
2878
  };
2623
2879
  if (params.enablePush) reqParams.push = "on";
2624
2880
  if (params.enableIAP !== false) reqParams.inAppPurchase = "on";
@@ -2653,6 +2909,191 @@ var PortalClient = class _PortalClient {
2653
2909
  featureValue: params.enabled ? "on" : "off"
2654
2910
  });
2655
2911
  }
2912
+ // ── Application Groups ──────────────────────────────────────────────────
2913
+ //
2914
+ // App Groups (e.g. `group.com.playspina.com`) are the only clean way to
2915
+ // share data — an auth token, in our case — between a host app and an
2916
+ // embedded extension (Messages / Notification Service / etc.). App Store
2917
+ // Connect API keys (.p8) genuinely cannot manage them; Apple only exposes
2918
+ // group create/associate through this legacy session-cookie portal API,
2919
+ // which is why they live on PortalClient (Apple ID session) and not on the
2920
+ // ASC REST client. Endpoint shapes ported 1:1 from Spaceship's
2921
+ // portal_client.rb (listApplicationGroups / addApplicationGroup /
2922
+ // assignApplicationGroupToAppId).
2923
+ /** Apple's internal feature code for the App Groups capability. */
2924
+ static APP_GROUPS_FEATURE = "APG3427HIY";
2925
+ /** List all app groups in the team. */
2926
+ async listAppGroups() {
2927
+ const data = await this.post("account/ios/identifiers/listApplicationGroups.action", {
2928
+ teamId: this.teamId,
2929
+ pageNumber: "1",
2930
+ pageSize: "500",
2931
+ sort: "name=asc"
2932
+ });
2933
+ return data.applicationGroupList ?? [];
2934
+ }
2935
+ /**
2936
+ * Create an app group, returning the raw group record. Idempotent —
2937
+ * Apple rejects a duplicate group identifier ("Multiple App Groups may
2938
+ * not have the same identifier"), so we list first and reuse an existing
2939
+ * group when the identifier already exists in the team.
2940
+ *
2941
+ * Note the two ids on the returned record: `identifier` is the
2942
+ * `group.com.…` string you type, while `applicationGroup` is Apple's
2943
+ * opaque internal handle that association actually requires — do NOT pass
2944
+ * the human identifier to assignAppGroups.
2945
+ */
2946
+ async createAppGroup(params) {
2947
+ const existing = await this.listAppGroups();
2948
+ const hit = existing.find((g) => g.identifier === params.identifier);
2949
+ if (hit) return hit;
2950
+ const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "App Group";
2951
+ const data = await this.post("account/ios/identifiers/addApplicationGroup.action", {
2952
+ teamId: this.teamId,
2953
+ identifier: params.identifier,
2954
+ name: safeName
2955
+ });
2956
+ return data.applicationGroup;
2957
+ }
2958
+ /**
2959
+ * Enable the App Groups capability on an App ID. Required before an
2960
+ * App ID can have a group associated with it — mirrors Spaceship
2961
+ * enabling `AppService::AppGroup` before `associate_groups`.
2962
+ */
2963
+ async enableAppGroups(appIdId) {
2964
+ await this.post("account/ios/identifiers/updateService.action", {
2965
+ teamId: this.teamId,
2966
+ displayId: appIdId,
2967
+ featureType: _PortalClient.APP_GROUPS_FEATURE,
2968
+ featureValue: "true"
2969
+ });
2970
+ }
2971
+ /**
2972
+ * Associate one or more app groups with an App ID. `appGroupIds` are the
2973
+ * internal ids (Spaceship's `app_group_id`), NOT the `group.com.…`
2974
+ * identifiers. Apple wants both `appIdId` and `displayId` set to the
2975
+ * App ID's internal id, per Spaceship's associate_groups_with_app.
2976
+ */
2977
+ async assignAppGroups(params) {
2978
+ await this.post("account/ios/identifiers/assignApplicationGroupToAppId.action", {
2979
+ teamId: this.teamId,
2980
+ appIdId: params.appIdId,
2981
+ displayId: params.appIdId,
2982
+ applicationGroups: params.appGroupIds
2983
+ });
2984
+ }
2985
+ // ── Apple Pay merchants (OMCs) ─────────────────────────────────────────
2986
+ //
2987
+ // Merchant IDs are team-level identifiers (merchant.com.…) that an App ID's
2988
+ // Apple Pay capability must be LINKED to — enabling the capability alone
2989
+ // produces profiles that fail with "doesn't support the … Merchant ID".
2990
+ // Like App Groups, merchants are only manageable through the legacy
2991
+ // session-cookie portal API. Endpoint shapes ported 1:1 from Spaceship's
2992
+ // portal_client.rb (merchants / create_merchant! /
2993
+ // associate_merchants_with_app).
2994
+ /** Apple's internal feature code for the Apple Pay capability. */
2995
+ static APPLE_PAY_FEATURE = "OM633U5T5G";
2996
+ /**
2997
+ * List all Apple Pay merchant IDs in the team. Each record maps
2998
+ * (per Spaceship's merchant.rb attr_mapping): `omcId` = internal id,
2999
+ * `identifier` = the human `merchant.com.…` string, `name` = display name.
3000
+ */
3001
+ async listMerchants() {
3002
+ const data = await this.post("account/ios/identifiers/listOMCs.action", {
3003
+ teamId: this.teamId,
3004
+ pageNumber: "1",
3005
+ pageSize: "500",
3006
+ sort: "name=asc"
3007
+ });
3008
+ return data.identifierList ?? [];
3009
+ }
3010
+ /**
3011
+ * Register (or reuse) a merchant ID, returning its INTERNAL id (`omcId`) —
3012
+ * Apple's opaque handle that association requires, NOT the human
3013
+ * `merchant.com.…` identifier. Idempotent: lists first and reuses an
3014
+ * existing merchant with the same identifier. Endpoint + params ported 1:1
3015
+ * from Spaceship's create_merchant! (addOMC.action).
3016
+ */
3017
+ async createMerchant(params) {
3018
+ const existing = await this.listMerchants();
3019
+ const hit = existing.find((m) => m.identifier === params.identifier);
3020
+ if (hit?.omcId) return String(hit.omcId);
3021
+ const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "Merchant";
3022
+ const data = await this.post("account/ios/identifiers/addOMC.action", {
3023
+ name: safeName,
3024
+ identifier: params.identifier,
3025
+ teamId: this.teamId
3026
+ });
3027
+ const rec = data.omcId ?? data;
3028
+ const id = typeof rec === "string" ? rec : rec?.omcId ?? rec?.merchantId;
3029
+ if (!id) {
3030
+ throw new Error("Apple returned no internal merchant id (omcId) for the registered merchant.");
3031
+ }
3032
+ return String(id);
3033
+ }
3034
+ /**
3035
+ * Enable the Apple Pay capability on an App ID. Apple Pay is a standard
3036
+ * AppService (service_id OM633U5T5G) whose service_uri is the default
3037
+ * `updateService.action` — confirmed in Spaceship's app_service.rb.
3038
+ */
3039
+ async enableApplePay(appIdId) {
3040
+ await this.post("account/ios/identifiers/updateService.action", {
3041
+ teamId: this.teamId,
3042
+ displayId: appIdId,
3043
+ featureType: _PortalClient.APPLE_PAY_FEATURE,
3044
+ featureValue: "true"
3045
+ });
3046
+ }
3047
+ /**
3048
+ * Associate merchant IDs with an App ID's Apple Pay capability. `omcIds`
3049
+ * are the internal ids (each merchant record's `omcId`), NOT the
3050
+ * `merchant.com.…` identifiers. Endpoint + params ported 1:1 from
3051
+ * Spaceship's associate_merchants_with_app (assignOMCToAppId.action — note
3052
+ * the singular "OMC", and NO displayId param).
3053
+ */
3054
+ async assignMerchants(params) {
3055
+ await this.post("account/ios/identifiers/assignOMCToAppId.action", {
3056
+ teamId: this.teamId,
3057
+ appIdId: params.appIdId,
3058
+ omcIds: params.omcIds
3059
+ });
3060
+ }
3061
+ /**
3062
+ * Create an Apple Pay **Payment Processing Certificate** on a merchant ID
3063
+ * from a CSR — the certificate a PSP (e.g. Stripe) hands you as a CSR, which
3064
+ * must be uploaded to the Merchant ID in Apple Developer, then the resulting
3065
+ * `.cer` downloaded and given back to the PSP so it can decrypt payment
3066
+ * tokens. This is the one manual Apple-portal step in wiring up Apple Pay.
3067
+ *
3068
+ * Ported 1:1 from Spaceship's create_certificate! (submitCertificateRequest
3069
+ * .action): the merchant's INTERNAL id (`omcId`) is passed as BOTH `appIdId`
3070
+ * and `specialIdentifierDisplayId` — the latter is how the portal scopes a
3071
+ * cert to a merchant/special identifier rather than an App ID. The cert type
3072
+ * is APPLE_PAY (`4APLUP237T`). Returns the created certRequest (its
3073
+ * `certificateId` feeds `downloadApplePayCertificate`).
3074
+ *
3075
+ * @param omcId the merchant's internal id (from createMerchant/listMerchants)
3076
+ * @param csrContent the PEM CSR string (Stripe's "Apple Pay CSR")
3077
+ */
3078
+ async createApplePayCertificate(params) {
3079
+ const data = await this.post("account/ios/certificate/submitCertificateRequest.action", {
3080
+ teamId: this.teamId,
3081
+ type: _PortalClient.CERT_TYPES.APPLE_PAY,
3082
+ csrContent: params.csrContent,
3083
+ appIdId: params.omcId,
3084
+ specialIdentifierDisplayId: params.omcId
3085
+ });
3086
+ return data.certRequest;
3087
+ }
3088
+ /**
3089
+ * Download an Apple Pay Payment Processing Certificate's raw bytes by its
3090
+ * certificate id (from `createApplePayCertificate`). Thin wrapper over
3091
+ * `downloadCertificate` pinned to the APPLE_PAY type — the DER `.cer` you
3092
+ * upload back into Stripe.
3093
+ */
3094
+ async downloadApplePayCertificate(certificateId) {
3095
+ return this.downloadCertificate(certificateId, _PortalClient.CERT_TYPES.APPLE_PAY);
3096
+ }
2656
3097
  // ── Certificates ───────────────────────────────────────────────────────
2657
3098
  async listCertificates(types) {
2658
3099
  const iosTypes = types ?? [
@@ -3165,6 +3606,10 @@ var AppStoreConnectClient = class {
3165
3606
  inAppPurchases;
3166
3607
  gameCenterDetail;
3167
3608
  xcodeMetrics;
3609
+ appAssetLibraries;
3610
+ appAssetLibraryImages;
3611
+ appAssetLibraryVideos;
3612
+ appAssetLibraryPlacements;
3168
3613
  constructor(config) {
3169
3614
  const tokenManager = new TokenManager({
3170
3615
  keyId: config.keyId,
@@ -3208,6 +3653,10 @@ var AppStoreConnectClient = class {
3208
3653
  this.inAppPurchases = new InAppPurchasesResource(this.http);
3209
3654
  this.gameCenterDetail = new GameCenterDetailResource(this.http);
3210
3655
  this.xcodeMetrics = new XcodeMetricsResource(this.http);
3656
+ this.appAssetLibraries = new AppAssetLibrariesResource(this.http);
3657
+ this.appAssetLibraryImages = new AppAssetLibraryImagesResource(this.http);
3658
+ this.appAssetLibraryVideos = new AppAssetLibraryVideosResource(this.http);
3659
+ this.appAssetLibraryPlacements = new AppAssetLibraryPlacementsResource(this.http);
3211
3660
  }
3212
3661
  async request(path, options) {
3213
3662
  return this.http.request(path, options);