@sitepong/fastnode 0.2.2 → 0.3.1

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 +64 -1
  2. package/dist/actions/index.cjs +928 -54
  3. package/dist/actions/index.cjs.map +1 -1
  4. package/dist/actions/index.mjs +930 -56
  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 +782 -736
  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 +782 -737
  13. package/dist/cert/index.mjs.map +1 -1
  14. package/dist/{cert-Ddl7FpFT.d.ts → cert-ClacRx9y.d.ts} +2 -2
  15. package/dist/{cert-FehieKeb.d.cts → cert-DffLKURp.d.cts} +2 -2
  16. package/dist/cli/index.cjs +1408 -60
  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 +1410 -62
  21. package/dist/cli/index.mjs.map +1 -1
  22. package/dist/client/index.cjs +519 -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 +514 -32
  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-Bk1Gd-qE.d.cts → index-925_Ov7X.d.cts} +2 -2
  46. package/dist/{index-DrUpRXRG.d.cts → index-B6-MJpO4.d.cts} +1 -1
  47. package/dist/index-BGhV0gS-.d.ts +65 -0
  48. package/dist/{index-OelCoOth.d.cts → index-BbVQzSV1.d.cts} +18 -4
  49. package/dist/index-BhGaVlK1.d.cts +201 -0
  50. package/dist/{index-CtPyeMLH.d.ts → index-C-EEYXSE.d.ts} +18 -4
  51. package/dist/{index-CNHf7I4v.d.cts → index-C18cijCd.d.ts} +32 -3
  52. package/dist/{index-DFni8oZt.d.ts → index-CBM5XZKC.d.ts} +2 -2
  53. package/dist/index-CXJlvmB4.d.cts +63 -0
  54. package/dist/index-CXJlvmB4.d.ts +63 -0
  55. package/dist/{index-BKUapQP2.d.cts → index-CemR3UI9.d.ts} +356 -124
  56. package/dist/{index-DI1ZHr-3.d.ts → index-CmcFPn_G.d.cts} +9 -1
  57. package/dist/{index-CCtYhPN9.d.ts → index-CpGV1f0L.d.cts} +356 -124
  58. package/dist/{index-B8FOfxOw.d.ts → index-D9YQQ3FM.d.ts} +2 -2
  59. package/dist/index-DFstwgGK.d.cts +65 -0
  60. package/dist/{index-YcKz4aRS.d.ts → index-Dkf5sH1E.d.ts} +1 -1
  61. package/dist/index-DnHQ2uT2.d.cts +104 -0
  62. package/dist/index-Dv-Mek07.d.cts +84 -0
  63. package/dist/index-Dv-Mek07.d.ts +84 -0
  64. package/dist/{index-BICa8odo.d.cts → index-N6eV5yym.d.ts} +9 -1
  65. package/dist/{index-oVyLLIEK.d.cts → index-O3EMrd5s.d.cts} +3 -3
  66. package/dist/{index-DZoPGCbE.d.ts → index-Ol0RtbQA.d.ts} +3 -3
  67. package/dist/{index-CplRWSUc.d.ts → index-_24DRp3a.d.cts} +32 -3
  68. package/dist/{index-BZWmOTWE.d.cts → index-isT08RuY.d.cts} +2 -2
  69. package/dist/{index-DImN9PWc.d.ts → index-mEEB7An6.d.ts} +1 -1
  70. package/dist/{index-D13sn8DO.d.cts → index-on573dnO.d.cts} +1 -1
  71. package/dist/index-s2PigtDM.d.ts +201 -0
  72. package/dist/index.cjs +3425 -955
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.cts +237 -14
  75. package/dist/index.d.ts +237 -14
  76. package/dist/index.mjs +3417 -958
  77. package/dist/index.mjs.map +1 -1
  78. package/dist/lane/index.cjs +928 -54
  79. package/dist/lane/index.cjs.map +1 -1
  80. package/dist/lane/index.mjs +930 -56
  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 +32 -0
  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 +32 -1
  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-B9QeTpQK.d.cts +0 -99
  126. package/dist/index-xykpq6Lz.d.ts +0 -99
@@ -3,6 +3,9 @@
3
3
  var jose = require('jose');
4
4
  var crypto = require('crypto');
5
5
  var readline = require('readline');
6
+ var fs = require('fs');
7
+ var os = require('os');
8
+ var path = require('path');
6
9
 
7
10
  // src/client/auth.ts
8
11
  var MAX_TOKEN_DURATION = 1200;
@@ -166,6 +169,20 @@ function applePbkdf2(password, salt, iterations, keyLength, protocol) {
166
169
 
167
170
  // src/client/apple-session.ts
168
171
  var AUTH_BASE = "https://idmsa.apple.com/appleauth/auth";
172
+ async function appleJson(res, what) {
173
+ const text = await res.text();
174
+ try {
175
+ return JSON.parse(text);
176
+ } catch {
177
+ const contentType = (res.headers.get("content-type") ?? "").split(";")[0].trim();
178
+ const title = /<title>([^<]{1,120})<\/title>/i.exec(text)?.[1]?.trim();
179
+ const looksHtml = /^\s*<(!doctype|html)/i.test(text) || contentType === "text/html";
180
+ const page = looksHtml ? "an HTML page" : contentType ? `a ${contentType} response` : "a non-JSON response";
181
+ throw new Error(
182
+ `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.`
183
+ );
184
+ }
185
+ }
169
186
  var OLYMPUS_SESSION = "https://appstoreconnect.apple.com/olympus/v1/session";
170
187
  var WIDGET_KEY_URL = "https://appstoreconnect.apple.com/olympus/v1/app/config?hostname=itunesconnect.apple.com";
171
188
  var ProgramLicenseAgreementError = class extends Error {
@@ -252,7 +269,7 @@ async function beginLogin(config) {
252
269
  }
253
270
  async function fetchTwoFactorChallenge(pending) {
254
271
  const r = await fetch(`${AUTH_BASE}`, { headers: buildAuthHeadersFromPending(pending) });
255
- const body = await r.json();
272
+ const body = await appleJson(r, "two-factor challenge");
256
273
  const trustedPhoneNumbers = (body.trustedPhoneNumbers ?? []).map((p) => ({
257
274
  id: p.id,
258
275
  numberWithDialCode: p.numberWithDialCode,
@@ -273,7 +290,8 @@ async function requestSmsCode(input) {
273
290
  body: JSON.stringify({ phoneNumber: { id: input.phoneId }, mode: input.mode ?? "sms" })
274
291
  });
275
292
  }
276
- async function submitTwoFactorCode(input) {
293
+ async function submitTwoFactorCode(rawInput) {
294
+ const input = { ...rawInput, code: rawInput.code.replace(/[\s\u00a0]+/g, "") };
277
295
  const log = input.onLog ?? (() => {
278
296
  });
279
297
  let path;
@@ -385,9 +403,12 @@ async function doSirp(user, password, serviceKey, cookieStr, hashcash) {
385
403
  body: JSON.stringify({ a: aBase64, accountName: user, protocols: ["s2k", "s2k_fo"] }),
386
404
  redirect: "manual"
387
405
  });
388
- const initBody = await initRes.json();
406
+ const initBody = await appleJson(initRes, "sign-in init");
389
407
  if (initBody.serviceErrors) throw new Error(`Apple auth error: ${JSON.stringify(initBody.serviceErrors)}`);
390
408
  const { iteration: iterations, salt: saltB64, b: bB64, c, protocol } = initBody;
409
+ if (!iterations || !saltB64 || !bB64 || !c || !protocol) {
410
+ throw new Error(`Apple's sign-in init answered without a challenge (HTTP ${initRes.status}): ${JSON.stringify(initBody).slice(0, 300)}`);
411
+ }
391
412
  const salt = Buffer.from(saltB64, "base64");
392
413
  const b = Buffer.from(bB64, "base64");
393
414
  const encryptedPassword = applePbkdf2(password, salt, iterations, 32, protocol);
@@ -430,7 +451,7 @@ async function fetchOlympusSession(session) {
430
451
  const cookieStr = Object.entries(session.cookies).map(([k, v]) => `${k}=${v}`).join("; ");
431
452
  const res = await fetch(OLYMPUS_SESSION, { headers: { Cookie: cookieStr, Accept: "application/json" } });
432
453
  if (res.ok) {
433
- const body = await res.json();
454
+ const body = await appleJson(res, "session lookup");
434
455
  if (body?.user?.emailAddress) session.userEmail = body.user.emailAddress;
435
456
  if (body?.provider?.providerId) session.teamId = String(body.provider.providerId);
436
457
  }
@@ -442,7 +463,7 @@ async function fetchContractMessages(session) {
442
463
  headers: { Cookie: cookieStr, Accept: "application/json" }
443
464
  });
444
465
  if (!res.ok) return [];
445
- const body = await res.json();
466
+ const body = await appleJson(res, "agreements lookup");
446
467
  return (body ?? []).map((m) => ({
447
468
  id: m.id ?? "",
448
469
  group: m.group ?? "",
@@ -453,10 +474,23 @@ async function fetchContractMessages(session) {
453
474
  return [];
454
475
  }
455
476
  }
456
- async function fetchServiceKey() {
457
- const res = await fetch(WIDGET_KEY_URL);
458
- const body = await res.json();
459
- if (!body?.authServiceKey) throw new Error("Could not fetch Apple service key");
477
+ var LOGOUT_URL = "https://appstoreconnect.apple.com/logout";
478
+ function serviceKeyFromLocation(location) {
479
+ if (!location) return null;
480
+ try {
481
+ const key = new URL(location).searchParams.get("widgetKey")?.trim();
482
+ return key && /^[0-9a-f]{16,}$/i.test(key) ? key : null;
483
+ } catch {
484
+ return null;
485
+ }
486
+ }
487
+ async function fetchServiceKey(fetchImpl = fetch) {
488
+ const head = await fetchImpl(LOGOUT_URL, { method: "HEAD", redirect: "manual" });
489
+ const fromRedirect = serviceKeyFromLocation(head.headers.get("location"));
490
+ if (fromRedirect) return fromRedirect;
491
+ const res = await fetchImpl(WIDGET_KEY_URL);
492
+ const body = await appleJson(res, "service key request");
493
+ 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.");
460
494
  return body.authServiceKey;
461
495
  }
462
496
  function buildAuthHeaders(session) {
@@ -2054,6 +2088,19 @@ var PreReleaseVersionsResource = class {
2054
2088
  };
2055
2089
 
2056
2090
  // src/resources/review-submission-items.ts
2091
+ var ITEM_RELATIONSHIPS = {
2092
+ appStoreVersionId: ["appStoreVersion", "appStoreVersions"],
2093
+ appCustomProductPageVersionId: ["appCustomProductPageVersion", "appCustomProductPageVersions"],
2094
+ appStoreVersionExperimentId: ["appStoreVersionExperiment", "appStoreVersionExperiments"],
2095
+ appStoreVersionExperimentV2Id: ["appStoreVersionExperimentV2", "appStoreVersionExperiments"],
2096
+ appEventId: ["appEvent", "appEvents"],
2097
+ appAssetLibraryImageId: ["appAssetLibraryImage", "appAssetLibraryImages"],
2098
+ appAssetLibraryVideoId: ["appAssetLibraryVideo", "appAssetLibraryVideos"],
2099
+ backgroundAssetVersionId: ["backgroundAssetVersion", "backgroundAssetVersions"],
2100
+ inAppPurchaseVersionId: ["inAppPurchaseVersion", "inAppPurchaseVersions"],
2101
+ subscriptionVersionId: ["subscriptionVersion", "subscriptionVersions"],
2102
+ subscriptionGroupVersionId: ["subscriptionGroupVersion", "subscriptionGroupVersions"]
2103
+ };
2057
2104
  var ReviewSubmissionItemsResource = class {
2058
2105
  constructor(http) {
2059
2106
  this.http = http;
@@ -2075,32 +2122,20 @@ var ReviewSubmissionItemsResource = class {
2075
2122
  );
2076
2123
  return response.data;
2077
2124
  }
2125
+ /**
2126
+ * Add one item to a review submission. Besides versions, asset library
2127
+ * images and videos can be submitted on their own (ASC API 4.5.1) so
2128
+ * creative assets are approved ahead of a campaign.
2129
+ */
2078
2130
  async create(params) {
2079
2131
  const relationships = {
2080
2132
  reviewSubmission: {
2081
2133
  data: { type: "reviewSubmissions", id: params.reviewSubmissionId }
2082
2134
  }
2083
2135
  };
2084
- if (params.appStoreVersionId) {
2085
- relationships.appStoreVersion = {
2086
- data: { type: "appStoreVersions", id: params.appStoreVersionId }
2087
- };
2088
- }
2089
- if (params.appCustomProductPageVersionId) {
2090
- relationships.appCustomProductPageVersion = {
2091
- data: {
2092
- type: "appCustomProductPageVersions",
2093
- id: params.appCustomProductPageVersionId
2094
- }
2095
- };
2096
- }
2097
- if (params.appStoreVersionExperimentId) {
2098
- relationships.appStoreVersionExperiment = {
2099
- data: {
2100
- type: "appStoreVersionExperiments",
2101
- id: params.appStoreVersionExperimentId
2102
- }
2103
- };
2136
+ for (const [param, [relationship, type]] of Object.entries(ITEM_RELATIONSHIPS)) {
2137
+ const id = params[param];
2138
+ if (id) relationships[relationship] = { data: { type, id } };
2104
2139
  }
2105
2140
  const response = await this.http.post(
2106
2141
  "/v1/reviewSubmissionItems",
@@ -2304,6 +2339,256 @@ var XcodeMetricsResource = class {
2304
2339
  }
2305
2340
  };
2306
2341
 
2342
+ // src/resources/app-asset-libraries.ts
2343
+ var AppAssetLibrariesResource = class {
2344
+ constructor(http) {
2345
+ this.http = http;
2346
+ }
2347
+ http;
2348
+ async getForApp(appId) {
2349
+ const response = await this.http.get(
2350
+ `/v1/apps/${appId}/assetLibrary`
2351
+ );
2352
+ return response.data;
2353
+ }
2354
+ async listImages(libraryId, params = {}) {
2355
+ return this.http.all(`/v1/appAssetLibraries/${libraryId}/images`, params);
2356
+ }
2357
+ async listVideos(libraryId, params = {}) {
2358
+ return this.http.all(`/v1/appAssetLibraries/${libraryId}/videos`, params);
2359
+ }
2360
+ /**
2361
+ * Specs, placement groups and limits. Apple adds placement groups (new
2362
+ * devices) without an API version bump, so read them from here rather than
2363
+ * hard-coding display types.
2364
+ */
2365
+ async refData(params = {}) {
2366
+ const filter = {};
2367
+ if (params.features?.length) filter.features = params.features;
2368
+ if (params.placementTypes?.length) filter.placementTypes = params.placementTypes;
2369
+ if (params.placementProfileGroups?.length) filter.placementProfileGroups = params.placementProfileGroups;
2370
+ const data = await this.http.all("/v1/appAssetLibraryRefData", { filter });
2371
+ return mergeRefData(data.map((d) => d.attributes));
2372
+ }
2373
+ };
2374
+ function mergeRefData(parts) {
2375
+ const merged = {
2376
+ features: [],
2377
+ placementProfileGroups: [],
2378
+ imageSpecs: [],
2379
+ videoSpecs: [],
2380
+ placementTypes: [],
2381
+ displayClasses: []
2382
+ };
2383
+ for (const part of parts) {
2384
+ for (const key of Object.keys(merged)) {
2385
+ merged[key].push(...part[key] ?? []);
2386
+ }
2387
+ }
2388
+ return merged;
2389
+ }
2390
+
2391
+ // src/resources/app-asset-library-assets.ts
2392
+ var AssetLibraryMediaResource = class {
2393
+ constructor(http) {
2394
+ this.http = http;
2395
+ }
2396
+ http;
2397
+ async get(id) {
2398
+ const response = await this.http.get(`/v1/${this.type}/${id}`);
2399
+ return response.data;
2400
+ }
2401
+ async reserve(params, extra = {}) {
2402
+ const { libraryId, ...attributes } = params;
2403
+ const response = await this.http.post(`/v1/${this.type}`, {
2404
+ data: {
2405
+ type: this.type,
2406
+ attributes: { ...attributes, ...extra },
2407
+ relationships: {
2408
+ assetLibrary: { data: { type: "appAssetLibraries", id: libraryId } }
2409
+ }
2410
+ }
2411
+ });
2412
+ return response.data;
2413
+ }
2414
+ async patch(id, attributes) {
2415
+ const response = await this.http.patch(`/v1/${this.type}/${id}`, {
2416
+ data: { type: this.type, id, attributes }
2417
+ });
2418
+ return response.data;
2419
+ }
2420
+ /** Fails with 409 STATE_ERROR.ASSET_HAS_PLACEMENTS while the asset is still placed. */
2421
+ async delete(id) {
2422
+ await this.http.delete(`/v1/${this.type}/${id}`);
2423
+ }
2424
+ async listPlacements(id, params = {}) {
2425
+ return this.http.all(`/v1/${this.type}/${id}/placements`, params);
2426
+ }
2427
+ };
2428
+ var AppAssetLibraryImagesResource = class extends AssetLibraryMediaResource {
2429
+ type = "appAssetLibraryImages";
2430
+ async create(params) {
2431
+ return this.reserve(params);
2432
+ }
2433
+ async update(id, attributes) {
2434
+ return this.patch(id, { ...attributes });
2435
+ }
2436
+ };
2437
+ var AppAssetLibraryVideosResource = class extends AssetLibraryMediaResource {
2438
+ type = "appAssetLibraryVideos";
2439
+ async create(params) {
2440
+ const { previewFrameTimeCode, ...rest } = params;
2441
+ return this.reserve(rest, previewFrameTimeCode ? { previewFrameTimeCode } : {});
2442
+ }
2443
+ async update(id, attributes) {
2444
+ return this.patch(id, { ...attributes });
2445
+ }
2446
+ };
2447
+
2448
+ // src/resources/app-asset-library-placements.ts
2449
+ var TARGET_TYPES = {
2450
+ appStoreVersionLocalization: "appStoreVersionLocalizations",
2451
+ appCustomProductPageLocalization: "appCustomProductPageLocalizations",
2452
+ appEventLocalization: "appEventLocalizations",
2453
+ appStoreVersionExperimentTreatmentLocalization: "appStoreVersionExperimentTreatmentLocalizations"
2454
+ };
2455
+ function targetRelationship(target) {
2456
+ return { [target.kind]: { data: { type: TARGET_TYPES[target.kind], id: target.id } } };
2457
+ }
2458
+ var AppAssetLibraryPlacementsResource = class {
2459
+ constructor(http) {
2460
+ this.http = http;
2461
+ }
2462
+ http;
2463
+ /**
2464
+ * Place an image or video on one localization. Placements cannot be edited —
2465
+ * delete and recreate. The parent version/page must be editable.
2466
+ */
2467
+ async create(params) {
2468
+ if (!params.imageId === !params.videoId) {
2469
+ throw new Error("A placement needs exactly one of imageId or videoId");
2470
+ }
2471
+ const media = params.imageId ? { image: { data: { type: "appAssetLibraryImages", id: params.imageId } } } : { video: { data: { type: "appAssetLibraryVideos", id: params.videoId } } };
2472
+ const response = await this.http.post(
2473
+ "/v1/appAssetLibraryPlacements",
2474
+ {
2475
+ data: {
2476
+ type: "appAssetLibraryPlacements",
2477
+ attributes: { placementType: params.placementType, placementGroup: params.placementGroup },
2478
+ relationships: { ...media, ...targetRelationship(params.target) }
2479
+ }
2480
+ }
2481
+ );
2482
+ return response.data;
2483
+ }
2484
+ async get(id, params = {}) {
2485
+ const response = await this.http.get(
2486
+ `/v1/appAssetLibraryPlacements/${id}`,
2487
+ params.include ? { include: params.include } : void 0
2488
+ );
2489
+ return response.data;
2490
+ }
2491
+ async delete(id) {
2492
+ await this.http.delete(`/v1/appAssetLibraryPlacements/${id}`);
2493
+ }
2494
+ /** Placements on one localization, in display order by default. */
2495
+ async listForTarget(target, params = {}) {
2496
+ const filter = {};
2497
+ if (params.placementType) filter.placementType = params.placementType;
2498
+ if (params.placementGroup) filter.placementGroup = params.placementGroup;
2499
+ const listParams = { filter, include: params.include, sort: "placementGroupPosition" };
2500
+ return this.http.all(
2501
+ `/v1/${TARGET_TYPES[target.kind]}/${target.id}/placements`,
2502
+ listParams
2503
+ );
2504
+ }
2505
+ /** Set the display order of a placement group. In-app event localizations cannot be ordered. */
2506
+ async order(params) {
2507
+ if (params.target.kind === "appEventLocalization") {
2508
+ throw new Error("In-app event placements cannot be ordered");
2509
+ }
2510
+ await this.http.post("/v1/appAssetLibraryPlacementOrderingRequests", {
2511
+ data: {
2512
+ type: "appAssetLibraryPlacementOrderingRequests",
2513
+ attributes: { placementGroup: params.placementGroup },
2514
+ relationships: {
2515
+ orderedPlacements: {
2516
+ data: params.placementIds.map((id) => ({ type: "appAssetLibraryPlacements", id }))
2517
+ },
2518
+ ...targetRelationship(params.target)
2519
+ }
2520
+ }
2521
+ });
2522
+ }
2523
+ };
2524
+ var OLYMPUS_SESSION2 = "https://appstoreconnect.apple.com/olympus/v1/session";
2525
+ function sessionDir() {
2526
+ return process.env.FASTNODE_SESSION_DIR || path.join(os.homedir(), ".fastnode", "sessions");
2527
+ }
2528
+ function fileFor(email) {
2529
+ const key = crypto.createHash("sha256").update(email.trim().toLowerCase()).digest("hex").slice(0, 16);
2530
+ return path.join(sessionDir(), `${key}.json`);
2531
+ }
2532
+ function ensureDir() {
2533
+ const dir = sessionDir();
2534
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 448 });
2535
+ fs.chmodSync(dir, 448);
2536
+ }
2537
+ function saveAppleSession(session) {
2538
+ ensureDir();
2539
+ const stored = { email: session.userEmail, savedAt: (/* @__PURE__ */ new Date()).toISOString(), session };
2540
+ const path$1 = fileFor(session.userEmail);
2541
+ fs.writeFileSync(path$1, JSON.stringify(stored), { mode: 384 });
2542
+ fs.chmodSync(path$1, 384);
2543
+ fs.writeFileSync(path.join(sessionDir(), "last-apple-id"), session.userEmail, { mode: 384 });
2544
+ }
2545
+ function clearAppleSession(email) {
2546
+ const path = fileFor(email);
2547
+ if (fs.existsSync(path)) fs.unlinkSync(path);
2548
+ }
2549
+ function lastAppleId() {
2550
+ try {
2551
+ return fs.readFileSync(path.join(sessionDir(), "last-apple-id"), "utf8").trim() || null;
2552
+ } catch {
2553
+ return null;
2554
+ }
2555
+ }
2556
+ async function loadAppleSession(email, fetchImpl = fetch) {
2557
+ const path = fileFor(email);
2558
+ if (!fs.existsSync(path)) return null;
2559
+ let stored;
2560
+ try {
2561
+ stored = JSON.parse(fs.readFileSync(path, "utf8"));
2562
+ } catch {
2563
+ return null;
2564
+ }
2565
+ const cookie = Object.entries(stored.session.cookies).map(([k, v]) => `${k}=${v}`).join("; ");
2566
+ try {
2567
+ const res = await fetchImpl(OLYMPUS_SESSION2, { headers: { Cookie: cookie, Accept: "application/json" } });
2568
+ if (!res.ok) {
2569
+ clearAppleSession(email);
2570
+ return null;
2571
+ }
2572
+ } catch {
2573
+ return null;
2574
+ }
2575
+ return stored.session;
2576
+ }
2577
+ async function signInWithAppleId(options) {
2578
+ const email = options.email || lastAppleId() || await options.askEmail();
2579
+ if (!options.fresh) {
2580
+ const saved = await loadAppleSession(email);
2581
+ if (saved) {
2582
+ options.onLog?.(`Using saved Apple ID session for ${email}`);
2583
+ return saved;
2584
+ }
2585
+ }
2586
+ const password = await options.askPassword(email);
2587
+ const session = await loginWithAppleId({ email, password, onLog: options.onLog });
2588
+ saveAppleSession(session);
2589
+ return session;
2590
+ }
2591
+
2307
2592
  // src/client/portal.ts
2308
2593
  var PORTAL_BASE = "https://developer.apple.com/services-account/QH65B2";
2309
2594
  function pickUniqueKeyName(base, taken) {
@@ -2639,7 +2924,10 @@ var PortalClient = class _PortalClient {
2639
2924
  }
2640
2925
  // ── Teams ──────────────────────────────────────────────────────────────
2641
2926
  async listTeams() {
2642
- const data = await this.post("account/listTeams.action", {});
2927
+ const data = await this.post("account/listTeams.action", {
2928
+ pageSize: "500",
2929
+ pageNumber: "1"
2930
+ });
2643
2931
  return data.teams ?? [];
2644
2932
  }
2645
2933
  // ── App IDs / Bundle IDs ───────────────────────────────────────────────
@@ -2653,11 +2941,12 @@ var PortalClient = class _PortalClient {
2653
2941
  return data.appIds ?? [];
2654
2942
  }
2655
2943
  async createAppId(params) {
2944
+ const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "App";
2656
2945
  const reqParams = {
2657
2946
  teamId: this.teamId,
2658
2947
  type: "explicit",
2659
2948
  identifier: params.identifier,
2660
- name: params.name
2949
+ name: safeName
2661
2950
  };
2662
2951
  if (params.enablePush) reqParams.push = "on";
2663
2952
  if (params.enableIAP !== false) reqParams.inAppPurchase = "on";
@@ -2692,6 +2981,191 @@ var PortalClient = class _PortalClient {
2692
2981
  featureValue: params.enabled ? "on" : "off"
2693
2982
  });
2694
2983
  }
2984
+ // ── Application Groups ──────────────────────────────────────────────────
2985
+ //
2986
+ // App Groups (e.g. `group.com.playspina.com`) are the only clean way to
2987
+ // share data — an auth token, in our case — between a host app and an
2988
+ // embedded extension (Messages / Notification Service / etc.). App Store
2989
+ // Connect API keys (.p8) genuinely cannot manage them; Apple only exposes
2990
+ // group create/associate through this legacy session-cookie portal API,
2991
+ // which is why they live on PortalClient (Apple ID session) and not on the
2992
+ // ASC REST client. Endpoint shapes ported 1:1 from Spaceship's
2993
+ // portal_client.rb (listApplicationGroups / addApplicationGroup /
2994
+ // assignApplicationGroupToAppId).
2995
+ /** Apple's internal feature code for the App Groups capability. */
2996
+ static APP_GROUPS_FEATURE = "APG3427HIY";
2997
+ /** List all app groups in the team. */
2998
+ async listAppGroups() {
2999
+ const data = await this.post("account/ios/identifiers/listApplicationGroups.action", {
3000
+ teamId: this.teamId,
3001
+ pageNumber: "1",
3002
+ pageSize: "500",
3003
+ sort: "name=asc"
3004
+ });
3005
+ return data.applicationGroupList ?? [];
3006
+ }
3007
+ /**
3008
+ * Create an app group, returning the raw group record. Idempotent —
3009
+ * Apple rejects a duplicate group identifier ("Multiple App Groups may
3010
+ * not have the same identifier"), so we list first and reuse an existing
3011
+ * group when the identifier already exists in the team.
3012
+ *
3013
+ * Note the two ids on the returned record: `identifier` is the
3014
+ * `group.com.…` string you type, while `applicationGroup` is Apple's
3015
+ * opaque internal handle that association actually requires — do NOT pass
3016
+ * the human identifier to assignAppGroups.
3017
+ */
3018
+ async createAppGroup(params) {
3019
+ const existing = await this.listAppGroups();
3020
+ const hit = existing.find((g) => g.identifier === params.identifier);
3021
+ if (hit) return hit;
3022
+ const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "App Group";
3023
+ const data = await this.post("account/ios/identifiers/addApplicationGroup.action", {
3024
+ teamId: this.teamId,
3025
+ identifier: params.identifier,
3026
+ name: safeName
3027
+ });
3028
+ return data.applicationGroup;
3029
+ }
3030
+ /**
3031
+ * Enable the App Groups capability on an App ID. Required before an
3032
+ * App ID can have a group associated with it — mirrors Spaceship
3033
+ * enabling `AppService::AppGroup` before `associate_groups`.
3034
+ */
3035
+ async enableAppGroups(appIdId) {
3036
+ await this.post("account/ios/identifiers/updateService.action", {
3037
+ teamId: this.teamId,
3038
+ displayId: appIdId,
3039
+ featureType: _PortalClient.APP_GROUPS_FEATURE,
3040
+ featureValue: "true"
3041
+ });
3042
+ }
3043
+ /**
3044
+ * Associate one or more app groups with an App ID. `appGroupIds` are the
3045
+ * internal ids (Spaceship's `app_group_id`), NOT the `group.com.…`
3046
+ * identifiers. Apple wants both `appIdId` and `displayId` set to the
3047
+ * App ID's internal id, per Spaceship's associate_groups_with_app.
3048
+ */
3049
+ async assignAppGroups(params) {
3050
+ await this.post("account/ios/identifiers/assignApplicationGroupToAppId.action", {
3051
+ teamId: this.teamId,
3052
+ appIdId: params.appIdId,
3053
+ displayId: params.appIdId,
3054
+ applicationGroups: params.appGroupIds
3055
+ });
3056
+ }
3057
+ // ── Apple Pay merchants (OMCs) ─────────────────────────────────────────
3058
+ //
3059
+ // Merchant IDs are team-level identifiers (merchant.com.…) that an App ID's
3060
+ // Apple Pay capability must be LINKED to — enabling the capability alone
3061
+ // produces profiles that fail with "doesn't support the … Merchant ID".
3062
+ // Like App Groups, merchants are only manageable through the legacy
3063
+ // session-cookie portal API. Endpoint shapes ported 1:1 from Spaceship's
3064
+ // portal_client.rb (merchants / create_merchant! /
3065
+ // associate_merchants_with_app).
3066
+ /** Apple's internal feature code for the Apple Pay capability. */
3067
+ static APPLE_PAY_FEATURE = "OM633U5T5G";
3068
+ /**
3069
+ * List all Apple Pay merchant IDs in the team. Each record maps
3070
+ * (per Spaceship's merchant.rb attr_mapping): `omcId` = internal id,
3071
+ * `identifier` = the human `merchant.com.…` string, `name` = display name.
3072
+ */
3073
+ async listMerchants() {
3074
+ const data = await this.post("account/ios/identifiers/listOMCs.action", {
3075
+ teamId: this.teamId,
3076
+ pageNumber: "1",
3077
+ pageSize: "500",
3078
+ sort: "name=asc"
3079
+ });
3080
+ return data.identifierList ?? [];
3081
+ }
3082
+ /**
3083
+ * Register (or reuse) a merchant ID, returning its INTERNAL id (`omcId`) —
3084
+ * Apple's opaque handle that association requires, NOT the human
3085
+ * `merchant.com.…` identifier. Idempotent: lists first and reuses an
3086
+ * existing merchant with the same identifier. Endpoint + params ported 1:1
3087
+ * from Spaceship's create_merchant! (addOMC.action).
3088
+ */
3089
+ async createMerchant(params) {
3090
+ const existing = await this.listMerchants();
3091
+ const hit = existing.find((m) => m.identifier === params.identifier);
3092
+ if (hit?.omcId) return String(hit.omcId);
3093
+ const safeName = params.name.replace(/[^0-9A-Za-z\s]/g, " ").replace(/\s+/g, " ").trim() || "Merchant";
3094
+ const data = await this.post("account/ios/identifiers/addOMC.action", {
3095
+ name: safeName,
3096
+ identifier: params.identifier,
3097
+ teamId: this.teamId
3098
+ });
3099
+ const rec = data.omcId ?? data;
3100
+ const id = typeof rec === "string" ? rec : rec?.omcId ?? rec?.merchantId;
3101
+ if (!id) {
3102
+ throw new Error("Apple returned no internal merchant id (omcId) for the registered merchant.");
3103
+ }
3104
+ return String(id);
3105
+ }
3106
+ /**
3107
+ * Enable the Apple Pay capability on an App ID. Apple Pay is a standard
3108
+ * AppService (service_id OM633U5T5G) whose service_uri is the default
3109
+ * `updateService.action` — confirmed in Spaceship's app_service.rb.
3110
+ */
3111
+ async enableApplePay(appIdId) {
3112
+ await this.post("account/ios/identifiers/updateService.action", {
3113
+ teamId: this.teamId,
3114
+ displayId: appIdId,
3115
+ featureType: _PortalClient.APPLE_PAY_FEATURE,
3116
+ featureValue: "true"
3117
+ });
3118
+ }
3119
+ /**
3120
+ * Associate merchant IDs with an App ID's Apple Pay capability. `omcIds`
3121
+ * are the internal ids (each merchant record's `omcId`), NOT the
3122
+ * `merchant.com.…` identifiers. Endpoint + params ported 1:1 from
3123
+ * Spaceship's associate_merchants_with_app (assignOMCToAppId.action — note
3124
+ * the singular "OMC", and NO displayId param).
3125
+ */
3126
+ async assignMerchants(params) {
3127
+ await this.post("account/ios/identifiers/assignOMCToAppId.action", {
3128
+ teamId: this.teamId,
3129
+ appIdId: params.appIdId,
3130
+ omcIds: params.omcIds
3131
+ });
3132
+ }
3133
+ /**
3134
+ * Create an Apple Pay **Payment Processing Certificate** on a merchant ID
3135
+ * from a CSR — the certificate a PSP (e.g. Stripe) hands you as a CSR, which
3136
+ * must be uploaded to the Merchant ID in Apple Developer, then the resulting
3137
+ * `.cer` downloaded and given back to the PSP so it can decrypt payment
3138
+ * tokens. This is the one manual Apple-portal step in wiring up Apple Pay.
3139
+ *
3140
+ * Ported 1:1 from Spaceship's create_certificate! (submitCertificateRequest
3141
+ * .action): the merchant's INTERNAL id (`omcId`) is passed as BOTH `appIdId`
3142
+ * and `specialIdentifierDisplayId` — the latter is how the portal scopes a
3143
+ * cert to a merchant/special identifier rather than an App ID. The cert type
3144
+ * is APPLE_PAY (`4APLUP237T`). Returns the created certRequest (its
3145
+ * `certificateId` feeds `downloadApplePayCertificate`).
3146
+ *
3147
+ * @param omcId the merchant's internal id (from createMerchant/listMerchants)
3148
+ * @param csrContent the PEM CSR string (Stripe's "Apple Pay CSR")
3149
+ */
3150
+ async createApplePayCertificate(params) {
3151
+ const data = await this.post("account/ios/certificate/submitCertificateRequest.action", {
3152
+ teamId: this.teamId,
3153
+ type: _PortalClient.CERT_TYPES.APPLE_PAY,
3154
+ csrContent: params.csrContent,
3155
+ appIdId: params.omcId,
3156
+ specialIdentifierDisplayId: params.omcId
3157
+ });
3158
+ return data.certRequest;
3159
+ }
3160
+ /**
3161
+ * Download an Apple Pay Payment Processing Certificate's raw bytes by its
3162
+ * certificate id (from `createApplePayCertificate`). Thin wrapper over
3163
+ * `downloadCertificate` pinned to the APPLE_PAY type — the DER `.cer` you
3164
+ * upload back into Stripe.
3165
+ */
3166
+ async downloadApplePayCertificate(certificateId) {
3167
+ return this.downloadCertificate(certificateId, _PortalClient.CERT_TYPES.APPLE_PAY);
3168
+ }
2695
3169
  // ── Certificates ───────────────────────────────────────────────────────
2696
3170
  async listCertificates(types) {
2697
3171
  const iosTypes = types ?? [
@@ -3204,6 +3678,10 @@ var AppStoreConnectClient = class {
3204
3678
  inAppPurchases;
3205
3679
  gameCenterDetail;
3206
3680
  xcodeMetrics;
3681
+ appAssetLibraries;
3682
+ appAssetLibraryImages;
3683
+ appAssetLibraryVideos;
3684
+ appAssetLibraryPlacements;
3207
3685
  constructor(config) {
3208
3686
  const tokenManager = new TokenManager({
3209
3687
  keyId: config.keyId,
@@ -3247,6 +3725,10 @@ var AppStoreConnectClient = class {
3247
3725
  this.inAppPurchases = new InAppPurchasesResource(this.http);
3248
3726
  this.gameCenterDetail = new GameCenterDetailResource(this.http);
3249
3727
  this.xcodeMetrics = new XcodeMetricsResource(this.http);
3728
+ this.appAssetLibraries = new AppAssetLibrariesResource(this.http);
3729
+ this.appAssetLibraryImages = new AppAssetLibraryImagesResource(this.http);
3730
+ this.appAssetLibraryVideos = new AppAssetLibraryVideosResource(this.http);
3731
+ this.appAssetLibraryPlacements = new AppAssetLibraryPlacementsResource(this.http);
3250
3732
  }
3251
3733
  async request(path, options) {
3252
3734
  return this.http.request(path, options);
@@ -3264,8 +3746,14 @@ exports.RequestClient = RequestClient;
3264
3746
  exports.TokenManager = TokenManager;
3265
3747
  exports.beginLogin = beginLogin;
3266
3748
  exports.checkPortalResponse = checkPortalResponse;
3749
+ exports.clearAppleSession = clearAppleSession;
3750
+ exports.lastAppleId = lastAppleId;
3751
+ exports.loadAppleSession = loadAppleSession;
3267
3752
  exports.loginWithAppleId = loginWithAppleId;
3268
3753
  exports.requestSmsCode = requestSmsCode;
3754
+ exports.saveAppleSession = saveAppleSession;
3755
+ exports.sessionDir = sessionDir;
3756
+ exports.signInWithAppleId = signInWithAppleId;
3269
3757
  exports.submitTwoFactorCode = submitTwoFactorCode;
3270
3758
  //# sourceMappingURL=index.cjs.map
3271
3759
  //# sourceMappingURL=index.cjs.map