@hot-updater/apple-helper 1.1.4 → 1.2.0-rc.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/dist/index.d.cts CHANGED
@@ -1,4 +1,63 @@
1
- import { ApplePlatform, NativeBuildIosScheme, NativeBuildOptions } from "@hot-updater/plugin-core";
1
+ import { CredentialRequest, CredentialResolver, Credentials } from "@hot-updater/cli-tools";
2
+ import { NativeBuildIosScheme } from "@hot-updater/plugin-core";
3
+ //#region src/ascApiKey.d.ts
4
+ /** Where team API keys are listed and revoked in App Store Connect. */
5
+ declare const ASC_INTEGRATIONS_URL = "https://appstoreconnect.apple.com/access/integrations/api";
6
+ /** A team API key's role, as App Store Connect names it. */
7
+ type AscApiKeyRole = "ADMIN" | "APP_MANAGER" | "DEVELOPER";
8
+ /** The App Store Connect team an Apple Account signed in to. */
9
+ interface AscTeam {
10
+ readonly appleId?: string;
11
+ /** The team ID to pass as `teamId` to issue the key in this team. */
12
+ readonly teamId?: string;
13
+ /** The Apple Developer team ID, as Xcode's DEVELOPMENT_TEAM names it. */
14
+ readonly developerTeamId?: string;
15
+ }
16
+ interface IssueAscApiKeyRequest {
17
+ /** The key's name in App Store Connect. */
18
+ readonly name: string;
19
+ readonly role: AscApiKeyRole;
20
+ /** The Apple Account to sign in with; the sign-in asks for it when unset. */
21
+ readonly appleId?: string;
22
+ /** The team to issue the key in; else the one the sign-in lands in. */
23
+ readonly teamId?: string;
24
+ /**
25
+ * Sees the team the sign-in landed in, when `teamId` is unset, and
26
+ * returns another team's ID to issue the key there instead.
27
+ */
28
+ readonly chooseTeam?: (team: AscTeam) => Promise<string | undefined>;
29
+ }
30
+ /**
31
+ * An App Store Connect team API key, just issued, with its one-time private
32
+ * key and the signed-in session it was issued in. `dispose` ends that
33
+ * session and deletes everything the sign-in kept; `revoke` works until then.
34
+ */
35
+ interface IssuedAscApiKey {
36
+ /** The `.p8` private key, a PEM. */
37
+ readonly p8: string;
38
+ readonly keyId: string;
39
+ readonly issuerId: string;
40
+ /** The key's name in App Store Connect. */
41
+ readonly name: string;
42
+ readonly team: AscTeam;
43
+ /** Revokes the key with the same session; whether App Store Connect did. */
44
+ revoke(): Promise<boolean>;
45
+ dispose(): Promise<void>;
46
+ }
47
+ /**
48
+ * Signs in to App Store Connect with an Apple Account and issues a team API
49
+ * key. The developer types the password and the two-factor code into the
50
+ * sign-in's prompts; the caller never sees them. Nothing of the sign-in
51
+ * outlives `dispose`.
52
+ */
53
+ type IssueAscApiKey = (request: IssueAscApiKeyRequest) => Promise<IssuedAscApiKey>;
54
+ /**
55
+ * Hot Updater's sign-in, the one place that picks how it is done: asc, the
56
+ * open-source App Store Connect CLI. Another implementation of
57
+ * `IssueAscApiKey` replaces it here without touching its callers.
58
+ */
59
+ declare const issueAscApiKey: IssueAscApiKey;
60
+ //#endregion
2
61
  //#region src/utils/enrichNativeBuildIosScheme.d.ts
3
62
  /**
4
63
  * Validated scheme filled nullish values with default values.
@@ -12,35 +71,146 @@ declare const enrichNativeBuildIosScheme: ({ scheme, hotUpdaterSchemeName }: {
12
71
  }) => Promise<EnrichedNativeBuildIosScheme>;
13
72
  //#endregion
14
73
  //#region src/buildIos.d.ts
15
- declare const buildIos: ({ schemeConfig }: {
74
+ /**
75
+ * Builds a simulator `.app` for a `simulator` scheme, and otherwise archives
76
+ * and exports an `.ipa`, or returns the `.xcarchive` of a scheme it cannot
77
+ * export: one with neither `exportOptionsPlist` nor `signing.ascApiKey`.
78
+ */
79
+ declare const buildIos: ({ schemeConfig, minBundleId, credentials }: {
16
80
  schemeConfig: EnrichedNativeBuildIosScheme;
81
+ /** The MIN_BUNDLE_ID the build compiles in. */
82
+ minBundleId?: string;
83
+ /** Resolves the scheme's `signing` key, when a device build signs with one. */
84
+ credentials?: Pick<CredentialResolver, "resolve">;
17
85
  }) => Promise<{
18
86
  buildDirectory: string;
19
87
  buildArtifactPath: string;
88
+ /** What the built app reports, from its Info.plist. */
89
+ builtApp?: {
90
+ appVersion: string | null;
91
+ channel: string | null;
92
+ /** The app's `CFBundleVersion`. */
93
+ buildNumber: string | null;
94
+ };
20
95
  }>;
21
96
  //#endregion
97
+ //#region src/uploadToTestFlight.d.ts
98
+ type AppStoreConnectApiKey = Credentials["asc-api-key"];
99
+ /**
100
+ * App Store Connect refused the upload: it already has a build with the
101
+ * `.ipa`'s version and build number, from this or another upload, and takes
102
+ * each build number once per version.
103
+ */
104
+ declare class TestFlightBuildExistsError extends Error {
105
+ readonly shortVersion: string;
106
+ readonly bundleVersion: string;
107
+ readonly name = "TestFlightBuildExistsError";
108
+ constructor(shortVersion: string, bundleVersion: string, options?: ErrorOptions);
109
+ }
110
+ /**
111
+ * The App Store Connect API key a scheme's `.ipa` uploads with: the
112
+ * `signing.ascApiKey` it signs with, or else the `asc-api-key` item. It reads
113
+ * the config, and the scheme's files that exist before the native projects
114
+ * are written, and throws, saying why, for a scheme whose build App Store
115
+ * Connect would not take: a simulator `.app`, a Debug configuration, which
116
+ * loads its JavaScript from Metro, code signing turned off, an
117
+ * `.xcarchive` it cannot export, or export options of another method. An
118
+ * `exportOptionsPlist` the adapter's prebuild writes later is checked after
119
+ * it (`assertTestFlightExportOptions`).
120
+ */
121
+ declare const resolveTestFlightUploadKey: ({ scheme, iosScheme }: {
122
+ /** The scheme's name. */
123
+ scheme: string;
124
+ iosScheme: NativeBuildIosScheme;
125
+ }) => Promise<CredentialRequest<"asc-api-key">>;
126
+ /**
127
+ * Throws when the scheme's `exportOptionsPlist` exports with another method
128
+ * than App Store Connect's, whose `.ipa` App Store Connect refuses. It reads
129
+ * the file, which an adapter's prebuild may write, so it runs after it.
130
+ */
131
+ declare const assertTestFlightExportOptions: (schemeConfig: EnrichedNativeBuildIosScheme) => Promise<void>;
132
+ interface UploadToTestFlightResult {
133
+ /** The App Store Connect app the build went to. */
134
+ appId: string;
135
+ buildUploadId: string;
136
+ /**
137
+ * `COMPLETE` once App Store Connect accepted the build, or `PROCESSING`
138
+ * when it was still processing it after `processingTimeoutMs`.
139
+ */
140
+ state: "COMPLETE" | "PROCESSING";
141
+ /** The app's TestFlight page, where the build appears once processed. */
142
+ testFlightUrl: string;
143
+ /** The build's version, its `CFBundleShortVersionString`. */
144
+ shortVersion: string;
145
+ /** The build's number, its `CFBundleVersion`, which TestFlight lists it by. */
146
+ bundleVersion: string;
147
+ }
148
+ /**
149
+ * Whether App Store Connect already has the `.ipa`'s build: a build of its
150
+ * version with its build number, which TestFlight lists or is processing,
151
+ * as when a reused native build was uploaded before. A build upload that
152
+ * never finished is no build, so uploading again fails as it would without
153
+ * this check.
154
+ */
155
+ declare const findTestFlightBuild: ({ ipaPath, apiKey, fetch }: {
156
+ ipaPath: string;
157
+ apiKey: AppStoreConnectApiKey;
158
+ fetch?: typeof globalThis.fetch;
159
+ }) => Promise<{
160
+ found: boolean;
161
+ testFlightUrl: string;
162
+ shortVersion: string;
163
+ bundleVersion: string;
164
+ }>;
165
+ /**
166
+ * Uploads an `.ipa` to App Store Connect, which lists it in TestFlight, with
167
+ * the App Store Connect API's build uploads: it finds the app by the
168
+ * `.ipa`'s bundle ID, reserves the build's version and build number, sends
169
+ * the file in the parts App Store Connect asks for, commits it, and waits
170
+ * for App Store Connect to accept or reject the build.
171
+ */
172
+ declare const uploadToTestFlight: ({ ipaPath, apiKey, fetch, pollIntervalMs, processingTimeoutMs }: {
173
+ ipaPath: string;
174
+ apiKey: AppStoreConnectApiKey;
175
+ fetch?: typeof globalThis.fetch;
176
+ /** How often to check whether App Store Connect accepted the build. */
177
+ pollIntervalMs?: number;
178
+ /** How long to wait for App Store Connect to accept the build. */
179
+ processingTimeoutMs?: number;
180
+ }) => Promise<UploadToTestFlightResult>;
181
+ //#endregion
22
182
  //#region src/types.d.ts
23
- type DeviceState = "Booted" | "Shutdown";
183
+ /** Whether a build is for a physical device or a simulator. */
24
184
  type AppleDeviceType = "device" | "simulator";
25
- type AppleDevice = {
26
- name: string;
27
- udid: string;
28
- version: string;
29
- platform: ApplePlatform;
30
- state: DeviceState;
31
- type: AppleDeviceType;
32
- };
33
- interface IosNativeRunOptions extends NativeBuildOptions {
34
- device?: string | boolean;
35
- }
36
185
  //#endregion
37
- //#region src/runIos.d.ts
38
- declare const runIos: ({ schemeConfig, runOption }: {
39
- schemeConfig: EnrichedNativeBuildIosScheme;
40
- runOption: IosNativeRunOptions;
41
- }) => Promise<{
186
+ //#region src/utils/cocoapods.d.ts
187
+ declare const installPodsIfNeeded: (sourceDir: string) => Promise<void>;
188
+ //#endregion
189
+ //#region src/utils/extractBuiltInBundle.d.ts
190
+ /**
191
+ * Copies the JavaScript bundle React Native's Xcode build phase wrote into a
192
+ * `.app` or an `.ipa` (`Payload/<App>.app/main.jsbundle`) to `outputPath`:
193
+ * the built-in bundle, byte for byte, which no rebuild reproduces.
194
+ */
195
+ declare const extractBuiltInBundle: ({ artifactPath, outputPath }: {
196
+ artifactPath: string;
197
+ outputPath: string;
198
+ }) => Promise<void>;
199
+ /**
200
+ * Unpacks a `.app` that `archiveAppBundle` stored as one zip file into
201
+ * `outputPath`, which then holds the `.app` directory.
202
+ */
203
+ declare const extractAppBundle: ({ archivePath, outputPath }: {
204
+ archivePath: string;
205
+ outputPath: string;
206
+ }) => Promise<void>;
207
+ /**
208
+ * A `.app` directory as one zip file, the way Finder and Xcode archive it,
209
+ * so a simulator build uploads and downloads as a single artifact.
210
+ */
211
+ declare const archiveAppBundle: ({ appPath, outputPath }: {
42
212
  appPath: string;
43
- infoPlistPath: string;
44
- }>;
213
+ outputPath: string;
214
+ }) => Promise<void>;
45
215
  //#endregion
46
- export { type AppleDevice, type AppleDeviceType, type DeviceState, type EnrichedNativeBuildIosScheme, type IosNativeRunOptions, buildIos, enrichNativeBuildIosScheme, runIos };
216
+ export { ASC_INTEGRATIONS_URL, type AppleDeviceType, type AscApiKeyRole, type AscTeam, type EnrichedNativeBuildIosScheme, type IssueAscApiKey, type IssueAscApiKeyRequest, type IssuedAscApiKey, TestFlightBuildExistsError, type UploadToTestFlightResult, archiveAppBundle, assertTestFlightExportOptions, buildIos, enrichNativeBuildIosScheme, extractAppBundle, extractBuiltInBundle, findTestFlightBuild, installPodsIfNeeded, issueAscApiKey, resolveTestFlightUploadKey, uploadToTestFlight };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,64 @@
1
- import { ApplePlatform, NativeBuildIosScheme, NativeBuildOptions } from "@hot-updater/plugin-core";
1
+ import { CredentialRequest, CredentialResolver, Credentials } from "@hot-updater/cli-tools";
2
+ import crypto from "crypto";
3
+ import { NativeBuildIosScheme } from "@hot-updater/plugin-core";
4
+ //#region src/ascApiKey.d.ts
5
+ /** Where team API keys are listed and revoked in App Store Connect. */
6
+ declare const ASC_INTEGRATIONS_URL = "https://appstoreconnect.apple.com/access/integrations/api";
7
+ /** A team API key's role, as App Store Connect names it. */
8
+ type AscApiKeyRole = "ADMIN" | "APP_MANAGER" | "DEVELOPER";
9
+ /** The App Store Connect team an Apple Account signed in to. */
10
+ interface AscTeam {
11
+ readonly appleId?: string;
12
+ /** The team ID to pass as `teamId` to issue the key in this team. */
13
+ readonly teamId?: string;
14
+ /** The Apple Developer team ID, as Xcode's DEVELOPMENT_TEAM names it. */
15
+ readonly developerTeamId?: string;
16
+ }
17
+ interface IssueAscApiKeyRequest {
18
+ /** The key's name in App Store Connect. */
19
+ readonly name: string;
20
+ readonly role: AscApiKeyRole;
21
+ /** The Apple Account to sign in with; the sign-in asks for it when unset. */
22
+ readonly appleId?: string;
23
+ /** The team to issue the key in; else the one the sign-in lands in. */
24
+ readonly teamId?: string;
25
+ /**
26
+ * Sees the team the sign-in landed in, when `teamId` is unset, and
27
+ * returns another team's ID to issue the key there instead.
28
+ */
29
+ readonly chooseTeam?: (team: AscTeam) => Promise<string | undefined>;
30
+ }
31
+ /**
32
+ * An App Store Connect team API key, just issued, with its one-time private
33
+ * key and the signed-in session it was issued in. `dispose` ends that
34
+ * session and deletes everything the sign-in kept; `revoke` works until then.
35
+ */
36
+ interface IssuedAscApiKey {
37
+ /** The `.p8` private key, a PEM. */
38
+ readonly p8: string;
39
+ readonly keyId: string;
40
+ readonly issuerId: string;
41
+ /** The key's name in App Store Connect. */
42
+ readonly name: string;
43
+ readonly team: AscTeam;
44
+ /** Revokes the key with the same session; whether App Store Connect did. */
45
+ revoke(): Promise<boolean>;
46
+ dispose(): Promise<void>;
47
+ }
48
+ /**
49
+ * Signs in to App Store Connect with an Apple Account and issues a team API
50
+ * key. The developer types the password and the two-factor code into the
51
+ * sign-in's prompts; the caller never sees them. Nothing of the sign-in
52
+ * outlives `dispose`.
53
+ */
54
+ type IssueAscApiKey = (request: IssueAscApiKeyRequest) => Promise<IssuedAscApiKey>;
55
+ /**
56
+ * Hot Updater's sign-in, the one place that picks how it is done: asc, the
57
+ * open-source App Store Connect CLI. Another implementation of
58
+ * `IssueAscApiKey` replaces it here without touching its callers.
59
+ */
60
+ declare const issueAscApiKey: IssueAscApiKey;
61
+ //#endregion
2
62
  //#region src/utils/enrichNativeBuildIosScheme.d.ts
3
63
  /**
4
64
  * Validated scheme filled nullish values with default values.
@@ -12,35 +72,146 @@ declare const enrichNativeBuildIosScheme: ({ scheme, hotUpdaterSchemeName }: {
12
72
  }) => Promise<EnrichedNativeBuildIosScheme>;
13
73
  //#endregion
14
74
  //#region src/buildIos.d.ts
15
- declare const buildIos: ({ schemeConfig }: {
75
+ /**
76
+ * Builds a simulator `.app` for a `simulator` scheme, and otherwise archives
77
+ * and exports an `.ipa`, or returns the `.xcarchive` of a scheme it cannot
78
+ * export: one with neither `exportOptionsPlist` nor `signing.ascApiKey`.
79
+ */
80
+ declare const buildIos: ({ schemeConfig, minBundleId, credentials }: {
16
81
  schemeConfig: EnrichedNativeBuildIosScheme;
82
+ /** The MIN_BUNDLE_ID the build compiles in. */
83
+ minBundleId?: string;
84
+ /** Resolves the scheme's `signing` key, when a device build signs with one. */
85
+ credentials?: Pick<CredentialResolver, "resolve">;
17
86
  }) => Promise<{
18
87
  buildDirectory: string;
19
88
  buildArtifactPath: string;
89
+ /** What the built app reports, from its Info.plist. */
90
+ builtApp?: {
91
+ appVersion: string | null;
92
+ channel: string | null;
93
+ /** The app's `CFBundleVersion`. */
94
+ buildNumber: string | null;
95
+ };
20
96
  }>;
21
97
  //#endregion
98
+ //#region src/uploadToTestFlight.d.ts
99
+ type AppStoreConnectApiKey = Credentials["asc-api-key"];
100
+ /**
101
+ * App Store Connect refused the upload: it already has a build with the
102
+ * `.ipa`'s version and build number, from this or another upload, and takes
103
+ * each build number once per version.
104
+ */
105
+ declare class TestFlightBuildExistsError extends Error {
106
+ readonly shortVersion: string;
107
+ readonly bundleVersion: string;
108
+ readonly name = "TestFlightBuildExistsError";
109
+ constructor(shortVersion: string, bundleVersion: string, options?: ErrorOptions);
110
+ }
111
+ /**
112
+ * The App Store Connect API key a scheme's `.ipa` uploads with: the
113
+ * `signing.ascApiKey` it signs with, or else the `asc-api-key` item. It reads
114
+ * the config, and the scheme's files that exist before the native projects
115
+ * are written, and throws, saying why, for a scheme whose build App Store
116
+ * Connect would not take: a simulator `.app`, a Debug configuration, which
117
+ * loads its JavaScript from Metro, code signing turned off, an
118
+ * `.xcarchive` it cannot export, or export options of another method. An
119
+ * `exportOptionsPlist` the adapter's prebuild writes later is checked after
120
+ * it (`assertTestFlightExportOptions`).
121
+ */
122
+ declare const resolveTestFlightUploadKey: ({ scheme, iosScheme }: {
123
+ /** The scheme's name. */
124
+ scheme: string;
125
+ iosScheme: NativeBuildIosScheme;
126
+ }) => Promise<CredentialRequest<"asc-api-key">>;
127
+ /**
128
+ * Throws when the scheme's `exportOptionsPlist` exports with another method
129
+ * than App Store Connect's, whose `.ipa` App Store Connect refuses. It reads
130
+ * the file, which an adapter's prebuild may write, so it runs after it.
131
+ */
132
+ declare const assertTestFlightExportOptions: (schemeConfig: EnrichedNativeBuildIosScheme) => Promise<void>;
133
+ interface UploadToTestFlightResult {
134
+ /** The App Store Connect app the build went to. */
135
+ appId: string;
136
+ buildUploadId: string;
137
+ /**
138
+ * `COMPLETE` once App Store Connect accepted the build, or `PROCESSING`
139
+ * when it was still processing it after `processingTimeoutMs`.
140
+ */
141
+ state: "COMPLETE" | "PROCESSING";
142
+ /** The app's TestFlight page, where the build appears once processed. */
143
+ testFlightUrl: string;
144
+ /** The build's version, its `CFBundleShortVersionString`. */
145
+ shortVersion: string;
146
+ /** The build's number, its `CFBundleVersion`, which TestFlight lists it by. */
147
+ bundleVersion: string;
148
+ }
149
+ /**
150
+ * Whether App Store Connect already has the `.ipa`'s build: a build of its
151
+ * version with its build number, which TestFlight lists or is processing,
152
+ * as when a reused native build was uploaded before. A build upload that
153
+ * never finished is no build, so uploading again fails as it would without
154
+ * this check.
155
+ */
156
+ declare const findTestFlightBuild: ({ ipaPath, apiKey, fetch }: {
157
+ ipaPath: string;
158
+ apiKey: AppStoreConnectApiKey;
159
+ fetch?: typeof globalThis.fetch;
160
+ }) => Promise<{
161
+ found: boolean;
162
+ testFlightUrl: string;
163
+ shortVersion: string;
164
+ bundleVersion: string;
165
+ }>;
166
+ /**
167
+ * Uploads an `.ipa` to App Store Connect, which lists it in TestFlight, with
168
+ * the App Store Connect API's build uploads: it finds the app by the
169
+ * `.ipa`'s bundle ID, reserves the build's version and build number, sends
170
+ * the file in the parts App Store Connect asks for, commits it, and waits
171
+ * for App Store Connect to accept or reject the build.
172
+ */
173
+ declare const uploadToTestFlight: ({ ipaPath, apiKey, fetch, pollIntervalMs, processingTimeoutMs }: {
174
+ ipaPath: string;
175
+ apiKey: AppStoreConnectApiKey;
176
+ fetch?: typeof globalThis.fetch;
177
+ /** How often to check whether App Store Connect accepted the build. */
178
+ pollIntervalMs?: number;
179
+ /** How long to wait for App Store Connect to accept the build. */
180
+ processingTimeoutMs?: number;
181
+ }) => Promise<UploadToTestFlightResult>;
182
+ //#endregion
22
183
  //#region src/types.d.ts
23
- type DeviceState = "Booted" | "Shutdown";
184
+ /** Whether a build is for a physical device or a simulator. */
24
185
  type AppleDeviceType = "device" | "simulator";
25
- type AppleDevice = {
26
- name: string;
27
- udid: string;
28
- version: string;
29
- platform: ApplePlatform;
30
- state: DeviceState;
31
- type: AppleDeviceType;
32
- };
33
- interface IosNativeRunOptions extends NativeBuildOptions {
34
- device?: string | boolean;
35
- }
36
186
  //#endregion
37
- //#region src/runIos.d.ts
38
- declare const runIos: ({ schemeConfig, runOption }: {
39
- schemeConfig: EnrichedNativeBuildIosScheme;
40
- runOption: IosNativeRunOptions;
41
- }) => Promise<{
187
+ //#region src/utils/cocoapods.d.ts
188
+ declare const installPodsIfNeeded: (sourceDir: string) => Promise<void>;
189
+ //#endregion
190
+ //#region src/utils/extractBuiltInBundle.d.ts
191
+ /**
192
+ * Copies the JavaScript bundle React Native's Xcode build phase wrote into a
193
+ * `.app` or an `.ipa` (`Payload/<App>.app/main.jsbundle`) to `outputPath`:
194
+ * the built-in bundle, byte for byte, which no rebuild reproduces.
195
+ */
196
+ declare const extractBuiltInBundle: ({ artifactPath, outputPath }: {
197
+ artifactPath: string;
198
+ outputPath: string;
199
+ }) => Promise<void>;
200
+ /**
201
+ * Unpacks a `.app` that `archiveAppBundle` stored as one zip file into
202
+ * `outputPath`, which then holds the `.app` directory.
203
+ */
204
+ declare const extractAppBundle: ({ archivePath, outputPath }: {
205
+ archivePath: string;
206
+ outputPath: string;
207
+ }) => Promise<void>;
208
+ /**
209
+ * A `.app` directory as one zip file, the way Finder and Xcode archive it,
210
+ * so a simulator build uploads and downloads as a single artifact.
211
+ */
212
+ declare const archiveAppBundle: ({ appPath, outputPath }: {
42
213
  appPath: string;
43
- infoPlistPath: string;
44
- }>;
214
+ outputPath: string;
215
+ }) => Promise<void>;
45
216
  //#endregion
46
- export { type AppleDevice, type AppleDeviceType, type DeviceState, type EnrichedNativeBuildIosScheme, type IosNativeRunOptions, buildIos, enrichNativeBuildIosScheme, runIos };
217
+ export { ASC_INTEGRATIONS_URL, type AppleDeviceType, type AscApiKeyRole, type AscTeam, type EnrichedNativeBuildIosScheme, type IssueAscApiKey, type IssueAscApiKeyRequest, type IssuedAscApiKey, TestFlightBuildExistsError, type UploadToTestFlightResult, archiveAppBundle, assertTestFlightExportOptions, buildIos, enrichNativeBuildIosScheme, extractAppBundle, extractBuiltInBundle, findTestFlightBuild, installPodsIfNeeded, issueAscApiKey, resolveTestFlightUploadKey, uploadToTestFlight };