@limrun/ui 0.14.0-rc.5 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -26
- package/dist/components/remote-control.d.ts +36 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1189 -1089
- package/package.json +10 -51
- package/src/components/remote-control.tsx +267 -0
- package/src/index.ts +1 -1
- package/vite.config.ts +0 -9
- package/dist/app-store-relay/index.cjs +0 -9
- package/dist/app-store-relay/index.d.ts +0 -204
- package/dist/app-store-relay/index.js +0 -677
- package/dist/app-store-relay/react.cjs +0 -1
- package/dist/app-store-relay/react.d.ts +0 -15
- package/dist/app-store-relay/react.js +0 -69
- package/dist/apple-credentials/index.cjs +0 -1
- package/dist/apple-credentials/index.d.ts +0 -110
- package/dist/apple-credentials/index.js +0 -291
- package/dist/browser-storage-BnO-Ma-e.mjs +0 -219
- package/dist/browser-storage-DLxIqZk5.js +0 -1
- package/dist/client-C9z_sgOf.js +0 -1
- package/dist/client-CFDB4mX5.mjs +0 -191
- package/dist/core/device-install/apple/client.d.ts +0 -18
- package/dist/core/device-install/apple/crypto.d.ts +0 -20
- package/dist/core/device-install/apple/gsa-srp.d.ts +0 -26
- package/dist/core/device-install/apple/index.d.ts +0 -5
- package/dist/core/device-install/apple/provisioning.d.ts +0 -237
- package/dist/core/device-install/apple/relay.d.ts +0 -53
- package/dist/core/device-install/index.d.ts +0 -4
- package/dist/core/device-install/operations/index.d.ts +0 -6
- package/dist/core/device-install/operations/limbuild-client.d.ts +0 -43
- package/dist/core/device-install/operations/operations.d.ts +0 -47
- package/dist/core/device-install/operations/relay-client.d.ts +0 -26
- package/dist/core/device-install/operations/relay-protocol.d.ts +0 -27
- package/dist/core/device-install/operations/usbmux.d.ts +0 -32
- package/dist/core/device-install/operations/webusb.d.ts +0 -21
- package/dist/core/device-install/storage/browser-storage.d.ts +0 -56
- package/dist/core/device-install/storage/index.d.ts +0 -2
- package/dist/core/device-install/storage/secret-store.d.ts +0 -124
- package/dist/core/device-install/types.d.ts +0 -57
- package/dist/core/errors.d.ts +0 -1
- package/dist/core/play-publish/google.d.ts +0 -13
- package/dist/core/play-publish/publish.d.ts +0 -34
- package/dist/device-build/index.cjs +0 -1
- package/dist/device-build/index.d.ts +0 -4
- package/dist/device-build/index.js +0 -85
- package/dist/device-build/react.cjs +0 -1
- package/dist/device-build/react.d.ts +0 -21
- package/dist/device-build/react.js +0 -68
- package/dist/device-build/signing.d.ts +0 -20
- package/dist/device-install/demo/demo.d.ts +0 -0
- package/dist/device-install/index.cjs +0 -1
- package/dist/device-install/index.d.ts +0 -18
- package/dist/device-install/index.js +0 -594
- package/dist/device-install/react.cjs +0 -1
- package/dist/device-install/react.d.ts +0 -24
- package/dist/device-install/react.js +0 -105
- package/dist/errors-BSkqS7U-.js +0 -1
- package/dist/errors-gy8yQJos.mjs +0 -6
- package/dist/gsa-srp-Bme3JLEi.mjs +0 -266
- package/dist/gsa-srp-CCIfUz7Y.js +0 -1
- package/dist/index-ChhtuQ7e.js +0 -22
- package/dist/index-YDu8K9a4.mjs +0 -11279
- package/dist/limbuild-client-BagK_FWE.mjs +0 -83
- package/dist/limbuild-client-CVDwq54z.js +0 -1
- package/dist/play-publish/index.cjs +0 -1
- package/dist/play-publish/index.d.ts +0 -2
- package/dist/play-publish/index.js +0 -109
- package/dist/play-publish/react.cjs +0 -1
- package/dist/play-publish/react.d.ts +0 -31
- package/dist/play-publish/react.js +0 -91
- package/dist/provisioning-CDPvxd1h.mjs +0 -270
- package/dist/provisioning-CbC4h--7.js +0 -1
- package/src/app-store-relay/index.test.ts +0 -60
- package/src/app-store-relay/index.ts +0 -846
- package/src/app-store-relay/react.ts +0 -131
- package/src/apple-credentials/index.test.ts +0 -180
- package/src/apple-credentials/index.ts +0 -388
- package/src/core/device-install/apple/client.ts +0 -150
- package/src/core/device-install/apple/crypto.ts +0 -205
- package/src/core/device-install/apple/gsa-srp.ts +0 -123
- package/src/core/device-install/apple/index.ts +0 -5
- package/src/core/device-install/apple/provisioning.test.ts +0 -114
- package/src/core/device-install/apple/provisioning.ts +0 -419
- package/src/core/device-install/apple/relay.ts +0 -202
- package/src/core/device-install/index.ts +0 -4
- package/src/core/device-install/operations/index.ts +0 -6
- package/src/core/device-install/operations/limbuild-client.ts +0 -144
- package/src/core/device-install/operations/operations.ts +0 -239
- package/src/core/device-install/operations/relay-client.ts +0 -286
- package/src/core/device-install/operations/relay-protocol.ts +0 -71
- package/src/core/device-install/operations/usbmux.ts +0 -270
- package/src/core/device-install/operations/webusb-dom.d.ts +0 -54
- package/src/core/device-install/operations/webusb.ts +0 -110
- package/src/core/device-install/storage/browser-storage.test.ts +0 -65
- package/src/core/device-install/storage/browser-storage.ts +0 -325
- package/src/core/device-install/storage/index.ts +0 -2
- package/src/core/device-install/storage/secret-store.ts +0 -332
- package/src/core/device-install/types.ts +0 -69
- package/src/core/errors.ts +0 -3
- package/src/core/play-publish/google.ts +0 -113
- package/src/core/play-publish/publish.test.ts +0 -68
- package/src/core/play-publish/publish.ts +0 -89
- package/src/device-build/index.ts +0 -42
- package/src/device-build/react.ts +0 -130
- package/src/device-build/signing.ts +0 -94
- package/src/device-install/README.md +0 -423
- package/src/device-install/demo/demo.css +0 -493
- package/src/device-install/demo/demo.tsx +0 -1061
- package/src/device-install/demo/index.html +0 -12
- package/src/device-install/index.ts +0 -50
- package/src/device-install/react.ts +0 -195
- package/src/play-publish/README.md +0 -66
- package/src/play-publish/index.ts +0 -12
- package/src/play-publish/react.ts +0 -208
|
@@ -1,846 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createAdHocProfileRequest,
|
|
3
|
-
createAppStoreProfileRequest,
|
|
4
|
-
createBundleIDRequest,
|
|
5
|
-
createDevelopmentProfileRequest,
|
|
6
|
-
downloadCertificateRequest,
|
|
7
|
-
downloadDistributionCertificateRequest,
|
|
8
|
-
downloadProfileRequest,
|
|
9
|
-
findAdHocProfilesRequest,
|
|
10
|
-
findAppStoreProfilesRequest,
|
|
11
|
-
findBundleIDRequest,
|
|
12
|
-
findDevelopmentCertificatesRequest,
|
|
13
|
-
findDevelopmentProfilesRequest,
|
|
14
|
-
findDeviceRequest,
|
|
15
|
-
findDistributionCertificatesRequest,
|
|
16
|
-
listTeamsRequest,
|
|
17
|
-
proxyAppStoreConnectRequest,
|
|
18
|
-
proxyProvisioningRequest,
|
|
19
|
-
registerDeviceRequest,
|
|
20
|
-
submitDevelopmentCSRRequest,
|
|
21
|
-
submitDistributionCSRRequest,
|
|
22
|
-
type AppleDeveloperPortalAppID,
|
|
23
|
-
type AppleDeveloperPortalDevice,
|
|
24
|
-
type AppleDeveloperPortalResponse,
|
|
25
|
-
type AppleDeveloperPortalTeam,
|
|
26
|
-
type AppleProvisioningRequest,
|
|
27
|
-
type AppleRelayResponse,
|
|
28
|
-
type AppleRelayWebSocketClient,
|
|
29
|
-
} from '../core/device-install/apple';
|
|
30
|
-
|
|
31
|
-
export * from '../core/device-install/apple/client';
|
|
32
|
-
export * from '../core/device-install/apple/crypto';
|
|
33
|
-
export * from '../core/device-install/apple/gsa-srp';
|
|
34
|
-
export type {
|
|
35
|
-
AppleDeveloperPortalAppID,
|
|
36
|
-
AppleDeveloperPortalDevice,
|
|
37
|
-
AppleDeveloperPortalResponse,
|
|
38
|
-
AppleDeveloperPortalTeam,
|
|
39
|
-
AppleProvisioningRequest,
|
|
40
|
-
AppleRelayResponse,
|
|
41
|
-
AppStoreConnectRequest,
|
|
42
|
-
} from '../core/device-install/apple';
|
|
43
|
-
export {
|
|
44
|
-
AppleRelayWebSocketClient,
|
|
45
|
-
fetchAppleAccountSession,
|
|
46
|
-
openAppleRelayWebSocket,
|
|
47
|
-
proxyAppStoreConnectRequest,
|
|
48
|
-
proxyPhoneTwoFactorCode,
|
|
49
|
-
proxySrpComplete,
|
|
50
|
-
proxySrpInit,
|
|
51
|
-
proxyTwoFactorCode,
|
|
52
|
-
triggerPhoneTwoFactor,
|
|
53
|
-
triggerTrustedDeviceTwoFactor,
|
|
54
|
-
} from '../core/device-install/apple';
|
|
55
|
-
|
|
56
|
-
export type AppleRelayClientOptions = {
|
|
57
|
-
relay: AppleRelayWebSocketClient;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export type AppleTeamScopedOptions = AppleRelayClientOptions & {
|
|
61
|
-
teamId?: string;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export type AppleCertificateKind = 'development' | 'distribution';
|
|
65
|
-
|
|
66
|
-
export type AppleProfileKind = 'development' | 'adhoc' | 'appstore';
|
|
67
|
-
|
|
68
|
-
export type RequestAppleProvisioningOptions<T = unknown> = AppleRelayClientOptions & {
|
|
69
|
-
request: AppleProvisioningRequest;
|
|
70
|
-
label?: string;
|
|
71
|
-
validatePortalResponse?: boolean;
|
|
72
|
-
mapBody?: (body: AppleDeveloperPortalResponse | undefined, response: AppleRelayResponse<T>) => T;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export type MappedAppleProvisioningOptions<T> = RequestAppleProvisioningOptions<T> & {
|
|
76
|
-
mapBody: (body: AppleDeveloperPortalResponse | undefined, response: AppleRelayResponse<T>) => T;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export type ListAppleCertificatesOptions = AppleTeamScopedOptions & {
|
|
80
|
-
certificateKind?: AppleCertificateKind;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export type CreateAppleCertificateOptions = AppleTeamScopedOptions & {
|
|
84
|
-
certificateKind?: AppleCertificateKind;
|
|
85
|
-
csrPEM: string;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export type DownloadAppleCertificateOptions = AppleTeamScopedOptions & {
|
|
89
|
-
certificateKind?: AppleCertificateKind;
|
|
90
|
-
certificateId: string;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export type DeleteAppleCertificateOptions = DownloadAppleCertificateOptions;
|
|
94
|
-
|
|
95
|
-
export type ListAppleBundleIDsOptions = AppleTeamScopedOptions & {
|
|
96
|
-
search?: string;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export type CreateAppleBundleIDOptions = AppleTeamScopedOptions & {
|
|
100
|
-
bundleId: string;
|
|
101
|
-
name?: string;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export type UpdateAppleBundleIDOptions = AppleTeamScopedOptions & {
|
|
105
|
-
appIdId: string;
|
|
106
|
-
bundleId?: string;
|
|
107
|
-
name?: string;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export type DeleteAppleBundleIDOptions = AppleTeamScopedOptions & {
|
|
111
|
-
appIdId: string;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export type ListAppleDevicesOptions = AppleTeamScopedOptions & {
|
|
115
|
-
deviceUDID?: string;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export type RegisterAppleDeviceOptions = AppleTeamScopedOptions & {
|
|
119
|
-
deviceUDID: string;
|
|
120
|
-
name?: string;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export type UpdateAppleDeviceOptions = AppleTeamScopedOptions & {
|
|
124
|
-
deviceId: string;
|
|
125
|
-
name?: string;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export type DeleteAppleDeviceOptions = AppleTeamScopedOptions & {
|
|
129
|
-
deviceId: string;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export type ListAppleProfilesOptions = AppleTeamScopedOptions & {
|
|
133
|
-
profileKind?: AppleProfileKind;
|
|
134
|
-
bundleId?: string;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
export type CreateAppleProfileOptions = AppleTeamScopedOptions & {
|
|
138
|
-
profileKind?: AppleProfileKind;
|
|
139
|
-
bundleId: string;
|
|
140
|
-
appIdId: string;
|
|
141
|
-
certificateIds: string[];
|
|
142
|
-
/** Devices the profile binds. Ignored for App Store profiles, which bind none. */
|
|
143
|
-
deviceIds?: string[];
|
|
144
|
-
/** Profile name. Required for App Store profiles; there is no default. */
|
|
145
|
-
name?: string;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
export type DownloadAppleProfileOptions = AppleTeamScopedOptions & {
|
|
149
|
-
profileId: string;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
export type DeleteAppleProfileOptions = DownloadAppleProfileOptions;
|
|
153
|
-
|
|
154
|
-
export function requestAppleProvisioning<T>(options: MappedAppleProvisioningOptions<T>): Promise<T>;
|
|
155
|
-
export function requestAppleProvisioning<T = AppleDeveloperPortalResponse>(
|
|
156
|
-
options: RequestAppleProvisioningOptions<T>,
|
|
157
|
-
): Promise<AppleRelayResponse<T>>;
|
|
158
|
-
export async function requestAppleProvisioning<T = AppleDeveloperPortalResponse>({
|
|
159
|
-
relay,
|
|
160
|
-
request,
|
|
161
|
-
label = 'Apple Developer Portal request',
|
|
162
|
-
validatePortalResponse = true,
|
|
163
|
-
mapBody,
|
|
164
|
-
}: RequestAppleProvisioningOptions<T>) {
|
|
165
|
-
const response = await proxyProvisioningRequest<T>(relay, request);
|
|
166
|
-
if (validatePortalResponse) {
|
|
167
|
-
assertAppleDeveloperPortalResponseOK(response.body as AppleDeveloperPortalResponse | undefined, label);
|
|
168
|
-
}
|
|
169
|
-
return mapBody ? mapBody(response.body as AppleDeveloperPortalResponse | undefined, response) : response;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export async function listAppleTeams(options: AppleRelayClientOptions) {
|
|
173
|
-
return requestAppleProvisioning<AppleDeveloperPortalTeam[]>({
|
|
174
|
-
...options,
|
|
175
|
-
request: listTeamsRequest(),
|
|
176
|
-
label: 'Apple Developer team list',
|
|
177
|
-
mapBody: (body) =>
|
|
178
|
-
uniqueAppleTeams([
|
|
179
|
-
...(body?.teams ?? []),
|
|
180
|
-
...(body?.availableProviders ?? []),
|
|
181
|
-
...(body?.provider ? [body.provider] : []),
|
|
182
|
-
]),
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export async function listAppleCertificates({
|
|
187
|
-
certificateKind = 'development',
|
|
188
|
-
teamId = '',
|
|
189
|
-
...options
|
|
190
|
-
}: ListAppleCertificatesOptions) {
|
|
191
|
-
return requestAppleProvisioning<Array<Record<string, unknown>>>({
|
|
192
|
-
...options,
|
|
193
|
-
request:
|
|
194
|
-
certificateKind === 'distribution' ?
|
|
195
|
-
findDistributionCertificatesRequest(teamId)
|
|
196
|
-
: findDevelopmentCertificatesRequest(teamId),
|
|
197
|
-
label: 'Apple Developer certificate list',
|
|
198
|
-
mapBody: (body) => body?.certRequests ?? [],
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export async function createAppleCertificate({
|
|
203
|
-
certificateKind = 'development',
|
|
204
|
-
teamId = '',
|
|
205
|
-
csrPEM,
|
|
206
|
-
...options
|
|
207
|
-
}: CreateAppleCertificateOptions) {
|
|
208
|
-
return requestAppleProvisioning<Record<string, unknown> | undefined>({
|
|
209
|
-
...options,
|
|
210
|
-
request:
|
|
211
|
-
certificateKind === 'distribution' ?
|
|
212
|
-
submitDistributionCSRRequest({ csrPEM, teamID: teamId })
|
|
213
|
-
: submitDevelopmentCSRRequest({ csrPEM, teamID: teamId }),
|
|
214
|
-
label:
|
|
215
|
-
certificateKind === 'distribution' ?
|
|
216
|
-
'Apple Distribution certificate creation'
|
|
217
|
-
: 'Apple Development certificate creation',
|
|
218
|
-
mapBody: (body) => body?.certRequest,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export async function downloadAppleCertificate({
|
|
223
|
-
certificateKind = 'development',
|
|
224
|
-
certificateId,
|
|
225
|
-
teamId = '',
|
|
226
|
-
...options
|
|
227
|
-
}: DownloadAppleCertificateOptions) {
|
|
228
|
-
return requestAppleProvisioning<AppleRelayResponse>({
|
|
229
|
-
...options,
|
|
230
|
-
request:
|
|
231
|
-
certificateKind === 'distribution' ?
|
|
232
|
-
downloadDistributionCertificateRequest(certificateId, teamId)
|
|
233
|
-
: downloadCertificateRequest(certificateId, teamId),
|
|
234
|
-
label: 'Apple certificate download',
|
|
235
|
-
validatePortalResponse: false,
|
|
236
|
-
mapBody: (_body, response) => response,
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export async function deleteAppleCertificate({
|
|
241
|
-
certificateKind = 'development',
|
|
242
|
-
certificateId,
|
|
243
|
-
teamId = '',
|
|
244
|
-
...options
|
|
245
|
-
}: DeleteAppleCertificateOptions) {
|
|
246
|
-
return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
|
|
247
|
-
...options,
|
|
248
|
-
request: {
|
|
249
|
-
method: 'POST',
|
|
250
|
-
path: '/account/ios/certificate/revokeCertificate.action',
|
|
251
|
-
payload: {
|
|
252
|
-
teamId,
|
|
253
|
-
certificateId,
|
|
254
|
-
type: certificateKind === 'distribution' ? 'WXV89964HE' : '83Q87W3TGH',
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
label: 'Apple certificate deletion',
|
|
258
|
-
mapBody: (body) => body,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export async function listAppleBundleIDs({
|
|
263
|
-
teamId = '',
|
|
264
|
-
search = '',
|
|
265
|
-
...options
|
|
266
|
-
}: ListAppleBundleIDsOptions) {
|
|
267
|
-
const query = search.trim().toLowerCase();
|
|
268
|
-
return requestAppleProvisioning<AppleDeveloperPortalAppID[]>({
|
|
269
|
-
...options,
|
|
270
|
-
request: findBundleIDRequest({ bundleID: search, teamID: teamId }),
|
|
271
|
-
label: 'Apple bundle ID list',
|
|
272
|
-
// Apple's listAppIds.action returns the full paginated list and does not
|
|
273
|
-
// honor a server-side search filter, so apply the `search` filter here.
|
|
274
|
-
mapBody: (body) => {
|
|
275
|
-
const appIds = body?.appIds ?? [];
|
|
276
|
-
if (!query) return appIds;
|
|
277
|
-
return appIds.filter((appId) =>
|
|
278
|
-
[appId.identifier, appId.bundleId, appId.name].some(
|
|
279
|
-
(value) => typeof value === 'string' && value.toLowerCase().includes(query),
|
|
280
|
-
),
|
|
281
|
-
);
|
|
282
|
-
},
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export async function createAppleBundleID({
|
|
287
|
-
teamId = '',
|
|
288
|
-
bundleId,
|
|
289
|
-
name,
|
|
290
|
-
...options
|
|
291
|
-
}: CreateAppleBundleIDOptions) {
|
|
292
|
-
return requestAppleProvisioning<Record<string, unknown> | undefined>({
|
|
293
|
-
...options,
|
|
294
|
-
request: createBundleIDRequest({ bundleID: bundleId, teamID: teamId, name }),
|
|
295
|
-
label: 'Apple bundle ID creation',
|
|
296
|
-
mapBody: (body) => body?.appId,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export async function updateAppleBundleID({
|
|
301
|
-
teamId = '',
|
|
302
|
-
appIdId,
|
|
303
|
-
bundleId,
|
|
304
|
-
name,
|
|
305
|
-
...options
|
|
306
|
-
}: UpdateAppleBundleIDOptions) {
|
|
307
|
-
return requestAppleProvisioning<Record<string, unknown> | undefined>({
|
|
308
|
-
...options,
|
|
309
|
-
request: {
|
|
310
|
-
method: 'POST',
|
|
311
|
-
path: '/account/ios/identifiers/updateAppId.action',
|
|
312
|
-
payload: {
|
|
313
|
-
teamId,
|
|
314
|
-
appIdId,
|
|
315
|
-
...(bundleId ? { identifier: bundleId } : {}),
|
|
316
|
-
...(name ? { name } : {}),
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
label: 'Apple bundle ID update',
|
|
320
|
-
mapBody: (body) => body?.appId,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export async function deleteAppleBundleID({ teamId = '', appIdId, ...options }: DeleteAppleBundleIDOptions) {
|
|
325
|
-
return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
|
|
326
|
-
...options,
|
|
327
|
-
request: {
|
|
328
|
-
method: 'POST',
|
|
329
|
-
path: '/account/ios/identifiers/deleteAppId.action',
|
|
330
|
-
payload: { teamId, appIdId },
|
|
331
|
-
},
|
|
332
|
-
label: 'Apple bundle ID deletion',
|
|
333
|
-
mapBody: (body) => body,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export async function listAppleDevices({
|
|
338
|
-
teamId = '',
|
|
339
|
-
deviceUDID = '',
|
|
340
|
-
...options
|
|
341
|
-
}: ListAppleDevicesOptions) {
|
|
342
|
-
const wantedUDID = normalizeAppleUDID(deviceUDID);
|
|
343
|
-
return requestAppleProvisioning<AppleDeveloperPortalDevice[]>({
|
|
344
|
-
...options,
|
|
345
|
-
request: findDeviceRequest({ deviceUDID, teamID: teamId }),
|
|
346
|
-
label: 'Apple device list',
|
|
347
|
-
// Apple's listDevices.action returns the full paginated list and does not
|
|
348
|
-
// honor a server-side UDID filter, so apply the `deviceUDID` filter here.
|
|
349
|
-
mapBody: (body) => {
|
|
350
|
-
const devices = body?.devices ?? [];
|
|
351
|
-
if (!wantedUDID) return devices;
|
|
352
|
-
return devices.filter((device) => normalizeAppleUDID(device.deviceNumber) === wantedUDID);
|
|
353
|
-
},
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function normalizeAppleUDID(udid?: string) {
|
|
358
|
-
return (udid ?? '')
|
|
359
|
-
.replace(/-/g, '')
|
|
360
|
-
.replace(/[^a-fA-F0-9]/g, '')
|
|
361
|
-
.toUpperCase();
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
export async function registerAppleDevice({
|
|
365
|
-
teamId = '',
|
|
366
|
-
deviceUDID,
|
|
367
|
-
name,
|
|
368
|
-
...options
|
|
369
|
-
}: RegisterAppleDeviceOptions) {
|
|
370
|
-
return requestAppleProvisioning<Record<string, unknown> | undefined>({
|
|
371
|
-
...options,
|
|
372
|
-
request: registerDeviceRequest({ deviceUDID, teamID: teamId, name }),
|
|
373
|
-
label: 'Apple device registration',
|
|
374
|
-
mapBody: (body) => body?.device,
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export async function updateAppleDevice({
|
|
379
|
-
teamId = '',
|
|
380
|
-
deviceId,
|
|
381
|
-
name,
|
|
382
|
-
...options
|
|
383
|
-
}: UpdateAppleDeviceOptions) {
|
|
384
|
-
return requestAppleProvisioning<Record<string, unknown> | undefined>({
|
|
385
|
-
...options,
|
|
386
|
-
request: {
|
|
387
|
-
method: 'POST',
|
|
388
|
-
path: '/account/ios/device/updateDevice.action',
|
|
389
|
-
payload: { teamId, deviceId, ...(name ? { name } : {}) },
|
|
390
|
-
},
|
|
391
|
-
label: 'Apple device update',
|
|
392
|
-
mapBody: (body) => body?.device,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
export async function deleteAppleDevice({ teamId = '', deviceId, ...options }: DeleteAppleDeviceOptions) {
|
|
397
|
-
return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
|
|
398
|
-
...options,
|
|
399
|
-
request: {
|
|
400
|
-
method: 'POST',
|
|
401
|
-
path: '/account/ios/device/deleteDevice.action',
|
|
402
|
-
payload: { teamId, deviceId },
|
|
403
|
-
},
|
|
404
|
-
label: 'Apple device deletion',
|
|
405
|
-
mapBody: (body) => body,
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export async function listAppleProfiles({
|
|
410
|
-
profileKind = 'development',
|
|
411
|
-
teamId = '',
|
|
412
|
-
bundleId = '',
|
|
413
|
-
...options
|
|
414
|
-
}: ListAppleProfilesOptions) {
|
|
415
|
-
return requestAppleProvisioning<Array<Record<string, unknown>>>({
|
|
416
|
-
...options,
|
|
417
|
-
request:
|
|
418
|
-
profileKind === 'adhoc' ? findAdHocProfilesRequest({ bundleID: bundleId, teamID: teamId })
|
|
419
|
-
: profileKind === 'appstore' ? findAppStoreProfilesRequest({ bundleID: bundleId, teamID: teamId })
|
|
420
|
-
: findDevelopmentProfilesRequest({ bundleID: bundleId, teamID: teamId }),
|
|
421
|
-
label: 'Apple provisioning profile list',
|
|
422
|
-
mapBody: (body) => body?.provisioningProfiles ?? [],
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
export async function createAppleProfile({
|
|
427
|
-
profileKind = 'development',
|
|
428
|
-
teamId = '',
|
|
429
|
-
bundleId,
|
|
430
|
-
appIdId,
|
|
431
|
-
certificateIds,
|
|
432
|
-
deviceIds = [],
|
|
433
|
-
name,
|
|
434
|
-
...options
|
|
435
|
-
}: CreateAppleProfileOptions) {
|
|
436
|
-
const [certificateId] = certificateIds;
|
|
437
|
-
if (!certificateId) {
|
|
438
|
-
throw new Error('At least one certificate ID is required to create an Apple provisioning profile.');
|
|
439
|
-
}
|
|
440
|
-
let request: AppleProvisioningRequest;
|
|
441
|
-
if (profileKind === 'appstore') {
|
|
442
|
-
if (!name) {
|
|
443
|
-
throw new Error('An explicit name is required to create an App Store provisioning profile.');
|
|
444
|
-
}
|
|
445
|
-
request = createAppStoreProfileRequest({
|
|
446
|
-
teamID: teamId,
|
|
447
|
-
appIDID: appIdId,
|
|
448
|
-
certificateID: certificateId,
|
|
449
|
-
name,
|
|
450
|
-
});
|
|
451
|
-
} else if (profileKind === 'adhoc') {
|
|
452
|
-
request = createAdHocProfileRequest({
|
|
453
|
-
bundleID: bundleId,
|
|
454
|
-
teamID: teamId,
|
|
455
|
-
appIDID: appIdId,
|
|
456
|
-
certificateID: certificateId,
|
|
457
|
-
deviceIDs: deviceIds,
|
|
458
|
-
name,
|
|
459
|
-
});
|
|
460
|
-
} else {
|
|
461
|
-
request = createDevelopmentProfileRequest({
|
|
462
|
-
bundleID: bundleId,
|
|
463
|
-
teamID: teamId,
|
|
464
|
-
appIDID: appIdId,
|
|
465
|
-
certificateID: certificateId,
|
|
466
|
-
deviceIDs: deviceIds,
|
|
467
|
-
name,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
return requestAppleProvisioning<Record<string, unknown> | undefined>({
|
|
471
|
-
...options,
|
|
472
|
-
request,
|
|
473
|
-
label: 'Apple provisioning profile creation',
|
|
474
|
-
mapBody: (body) => body?.provisioningProfile,
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
export async function downloadAppleProfile({
|
|
479
|
-
teamId = '',
|
|
480
|
-
profileId,
|
|
481
|
-
...options
|
|
482
|
-
}: DownloadAppleProfileOptions) {
|
|
483
|
-
return requestAppleProvisioning<AppleRelayResponse>({
|
|
484
|
-
...options,
|
|
485
|
-
request: downloadProfileRequest(profileId, teamId),
|
|
486
|
-
label: 'Apple provisioning profile download',
|
|
487
|
-
validatePortalResponse: false,
|
|
488
|
-
mapBody: (_body, response) => response,
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
export async function deleteAppleProfile({ teamId = '', profileId, ...options }: DeleteAppleProfileOptions) {
|
|
493
|
-
return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
|
|
494
|
-
...options,
|
|
495
|
-
request: {
|
|
496
|
-
method: 'POST',
|
|
497
|
-
path: '/account/ios/profile/deleteProvisioningProfile.action',
|
|
498
|
-
payload: { teamId, provisioningProfileId: profileId },
|
|
499
|
-
},
|
|
500
|
-
label: 'Apple provisioning profile deletion',
|
|
501
|
-
mapBody: (body) => body,
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* A JSON:API resource returned by the session-authenticated App Store
|
|
507
|
-
* Connect API. Kept loose on purpose: the endpoint is not a public contract
|
|
508
|
-
* and Apple adds attributes freely.
|
|
509
|
-
*/
|
|
510
|
-
export type AppStoreConnectResource = {
|
|
511
|
-
type?: string;
|
|
512
|
-
id?: string;
|
|
513
|
-
attributes?: Record<string, unknown>;
|
|
514
|
-
relationships?: Record<string, unknown>;
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
type AppStoreConnectEnvelope = {
|
|
518
|
-
data?: AppStoreConnectResource | AppStoreConnectResource[];
|
|
519
|
-
included?: AppStoreConnectResource[];
|
|
520
|
-
errors?: Array<{ status?: string; code?: string; title?: string; detail?: string }>;
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
export type SwitchAppStoreConnectProviderOptions = AppleRelayClientOptions & {
|
|
524
|
-
/** The numeric provider ID of the team, from the team list's providerId. */
|
|
525
|
-
providerId: string | number;
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Points the App Store Connect session at the given team (provider). Must
|
|
530
|
-
* run before any other App Store Connect call when the account belongs to
|
|
531
|
-
* multiple teams, because the session carries an active provider.
|
|
532
|
-
*/
|
|
533
|
-
export async function switchAppStoreConnectProvider({
|
|
534
|
-
relay,
|
|
535
|
-
providerId,
|
|
536
|
-
}: SwitchAppStoreConnectProviderOptions) {
|
|
537
|
-
const numericProviderId = Number(providerId);
|
|
538
|
-
const response = await proxyAppStoreConnectRequest<Record<string, unknown>>(relay, {
|
|
539
|
-
method: 'POST',
|
|
540
|
-
path: '/olympus/v1/session',
|
|
541
|
-
payload: {
|
|
542
|
-
provider: { providerId: Number.isNaN(numericProviderId) ? providerId : numericProviderId },
|
|
543
|
-
},
|
|
544
|
-
});
|
|
545
|
-
if (response.status < 200 || response.status >= 300) {
|
|
546
|
-
throw new Error(
|
|
547
|
-
`App Store Connect provider switch failed: HTTP ${response.status} ${response.rawBody ?? ''}`.trim(),
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
return response;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Roles Apple accepts on App Store Connect API keys. APP_MANAGER is enough
|
|
555
|
-
* to upload builds and manage TestFlight, which is why it is the default
|
|
556
|
-
* for keys minted by ensureAppStoreConnectApiKeySecret.
|
|
557
|
-
*/
|
|
558
|
-
export type AppStoreConnectApiKeyRole =
|
|
559
|
-
| 'ADMIN'
|
|
560
|
-
| 'APP_MANAGER'
|
|
561
|
-
| 'CUSTOMER_SUPPORT'
|
|
562
|
-
| 'DEVELOPER'
|
|
563
|
-
| 'FINANCE'
|
|
564
|
-
| 'MARKETING'
|
|
565
|
-
| 'SALES';
|
|
566
|
-
|
|
567
|
-
export type ListAppStoreConnectApiKeysOptions = AppleRelayClientOptions;
|
|
568
|
-
|
|
569
|
-
/** Lists the team's App Store Connect API keys. Requires an Admin session. */
|
|
570
|
-
export async function listAppStoreConnectApiKeys({ relay }: ListAppStoreConnectApiKeysOptions) {
|
|
571
|
-
const response = await proxyAppStoreConnectRequest<AppStoreConnectEnvelope>(relay, {
|
|
572
|
-
path: '/iris/v1/apiKeys',
|
|
573
|
-
});
|
|
574
|
-
assertAppStoreConnectResponseOK(response, 'App Store Connect API key list');
|
|
575
|
-
return resourceArray(response.body?.data);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
export type CreateAppStoreConnectApiKeyOptions = AppleRelayClientOptions & {
|
|
579
|
-
/** Display name of the key. Required; implementors brand keys themselves. */
|
|
580
|
-
nickname: string;
|
|
581
|
-
roles?: AppStoreConnectApiKeyRole[];
|
|
582
|
-
allAppsVisible?: boolean;
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* Creates a team App Store Connect API key through the logged-in session,
|
|
587
|
-
* the same way the App Store Connect website does. The session user must
|
|
588
|
-
* be a team Admin. The private key is NOT part of the response; download
|
|
589
|
-
* it once with downloadAppStoreConnectApiKeyPrivateKey.
|
|
590
|
-
*/
|
|
591
|
-
export async function createAppStoreConnectApiKey({
|
|
592
|
-
relay,
|
|
593
|
-
nickname,
|
|
594
|
-
roles = ['APP_MANAGER'],
|
|
595
|
-
allAppsVisible = true,
|
|
596
|
-
}: CreateAppStoreConnectApiKeyOptions) {
|
|
597
|
-
if (!nickname) {
|
|
598
|
-
throw new Error('A nickname is required to create an App Store Connect API key.');
|
|
599
|
-
}
|
|
600
|
-
const response = await proxyAppStoreConnectRequest<AppStoreConnectEnvelope>(relay, {
|
|
601
|
-
method: 'POST',
|
|
602
|
-
path: '/iris/v1/apiKeys',
|
|
603
|
-
payload: {
|
|
604
|
-
data: {
|
|
605
|
-
type: 'apiKeys',
|
|
606
|
-
attributes: {
|
|
607
|
-
nickname,
|
|
608
|
-
allAppsVisible,
|
|
609
|
-
keyType: 'PUBLIC_API',
|
|
610
|
-
roles,
|
|
611
|
-
},
|
|
612
|
-
},
|
|
613
|
-
},
|
|
614
|
-
});
|
|
615
|
-
assertAppStoreConnectResponseOK(response, 'App Store Connect API key creation');
|
|
616
|
-
const key = singleResource(response.body?.data);
|
|
617
|
-
if (!key?.id) {
|
|
618
|
-
throw new Error('App Store Connect API key creation did not return a key ID.');
|
|
619
|
-
}
|
|
620
|
-
return key;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
export type DownloadAppStoreConnectApiKeyOptions = AppleRelayClientOptions & {
|
|
624
|
-
keyId: string;
|
|
625
|
-
};
|
|
626
|
-
|
|
627
|
-
export type DownloadedAppStoreConnectApiKey = {
|
|
628
|
-
/** PEM contents of the .p8 private key. */
|
|
629
|
-
privateKeyPem: string;
|
|
630
|
-
/** Issuer ID for JWTs signed with this key, when Apple returned it. */
|
|
631
|
-
issuerId?: string;
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Downloads the private half of an App Store Connect API key. Apple serves
|
|
636
|
-
* it exactly once per key; afterwards the sparse fieldset comes back empty
|
|
637
|
-
* and the key must be revoked and re-minted.
|
|
638
|
-
*/
|
|
639
|
-
export async function downloadAppStoreConnectApiKeyPrivateKey({
|
|
640
|
-
relay,
|
|
641
|
-
keyId,
|
|
642
|
-
}: DownloadAppStoreConnectApiKeyOptions): Promise<DownloadedAppStoreConnectApiKey> {
|
|
643
|
-
const response = await proxyAppStoreConnectRequest<AppStoreConnectEnvelope>(relay, {
|
|
644
|
-
path: `/iris/v1/apiKeys/${encodeURIComponent(keyId)}`,
|
|
645
|
-
query: { 'fields[apiKeys]': 'privateKey', include: 'provider' },
|
|
646
|
-
});
|
|
647
|
-
assertAppStoreConnectResponseOK(response, 'App Store Connect API key download');
|
|
648
|
-
const key = singleResource(response.body?.data);
|
|
649
|
-
const privateKeyPem = stringField(key?.attributes, 'privateKey');
|
|
650
|
-
if (!privateKeyPem) {
|
|
651
|
-
throw new Error(
|
|
652
|
-
`App Store Connect API key ${keyId} returned no private key. Apple serves it only once; ` +
|
|
653
|
-
'revoke the key and create a new one.',
|
|
654
|
-
);
|
|
655
|
-
}
|
|
656
|
-
const provider = (response.body?.included ?? []).find((item) => item.type === 'providers');
|
|
657
|
-
const issuerId =
|
|
658
|
-
stringField(provider?.attributes, 'publicProviderId') ??
|
|
659
|
-
stringField(key?.attributes, 'issuerId') ??
|
|
660
|
-
provider?.id;
|
|
661
|
-
return { privateKeyPem, issuerId };
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
export type FindAppStoreConnectAppOptions = AppleRelayClientOptions & {
|
|
665
|
-
bundleId: string;
|
|
666
|
-
};
|
|
667
|
-
|
|
668
|
-
/** Looks up the App Store Connect app record for a bundle ID, if any. */
|
|
669
|
-
export async function findAppStoreConnectApp({ relay, bundleId }: FindAppStoreConnectAppOptions) {
|
|
670
|
-
const response = await proxyAppStoreConnectRequest<AppStoreConnectEnvelope>(relay, {
|
|
671
|
-
path: '/iris/v1/apps',
|
|
672
|
-
query: { 'filter[bundleId]': bundleId },
|
|
673
|
-
});
|
|
674
|
-
assertAppStoreConnectResponseOK(response, 'App Store Connect app lookup');
|
|
675
|
-
return resourceArray(response.body?.data).find(
|
|
676
|
-
(app) => stringField(app.attributes, 'bundleId') === bundleId,
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
export type CreateAppStoreConnectAppOptions = AppleRelayClientOptions & {
|
|
681
|
-
bundleId: string;
|
|
682
|
-
/** App name shown on the App Store. Required; there is no default. */
|
|
683
|
-
name: string;
|
|
684
|
-
/** Unique internal identifier. Defaults to the bundle ID. */
|
|
685
|
-
sku?: string;
|
|
686
|
-
/** Primary App Store locale. Defaults to en-US. */
|
|
687
|
-
primaryLocale?: string;
|
|
688
|
-
/** Version string of the first App Store version. Defaults to 1.0. */
|
|
689
|
-
versionString?: string;
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
/**
|
|
693
|
-
* Creates the App Store Connect app record for a bundle ID. Only the
|
|
694
|
-
* session-authenticated API can create app records (the key-authenticated
|
|
695
|
-
* public API cannot), which is why this goes through the relay. The body
|
|
696
|
-
* mirrors what the App Store Connect website and fastlane's produce send.
|
|
697
|
-
*/
|
|
698
|
-
export async function createAppStoreConnectApp({
|
|
699
|
-
relay,
|
|
700
|
-
bundleId,
|
|
701
|
-
name,
|
|
702
|
-
sku,
|
|
703
|
-
primaryLocale = 'en-US',
|
|
704
|
-
versionString = '1.0',
|
|
705
|
-
}: CreateAppStoreConnectAppOptions) {
|
|
706
|
-
if (!name) {
|
|
707
|
-
throw new Error('An app name is required to create an App Store Connect app record.');
|
|
708
|
-
}
|
|
709
|
-
const platform = 'IOS';
|
|
710
|
-
const response = await proxyAppStoreConnectRequest<AppStoreConnectEnvelope>(relay, {
|
|
711
|
-
method: 'POST',
|
|
712
|
-
path: '/iris/v1/apps',
|
|
713
|
-
payload: {
|
|
714
|
-
data: {
|
|
715
|
-
type: 'apps',
|
|
716
|
-
attributes: {
|
|
717
|
-
sku: sku || bundleId,
|
|
718
|
-
primaryLocale,
|
|
719
|
-
bundleId,
|
|
720
|
-
},
|
|
721
|
-
relationships: {
|
|
722
|
-
appStoreVersions: {
|
|
723
|
-
data: [{ type: 'appStoreVersions', id: `\${store-version-${platform}}` }],
|
|
724
|
-
},
|
|
725
|
-
appInfos: {
|
|
726
|
-
data: [{ type: 'appInfos', id: '${new-appInfo-id}' }],
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
|
-
included: [
|
|
731
|
-
{
|
|
732
|
-
type: 'appInfos',
|
|
733
|
-
id: '${new-appInfo-id}',
|
|
734
|
-
relationships: {
|
|
735
|
-
appInfoLocalizations: {
|
|
736
|
-
data: [{ type: 'appInfoLocalizations', id: '${new-appInfoLocalization-id}' }],
|
|
737
|
-
},
|
|
738
|
-
},
|
|
739
|
-
},
|
|
740
|
-
{
|
|
741
|
-
type: 'appInfoLocalizations',
|
|
742
|
-
id: '${new-appInfoLocalization-id}',
|
|
743
|
-
attributes: { locale: primaryLocale, name },
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
type: 'appStoreVersions',
|
|
747
|
-
id: `\${store-version-${platform}}`,
|
|
748
|
-
attributes: { platform, versionString },
|
|
749
|
-
relationships: {
|
|
750
|
-
appStoreVersionLocalizations: {
|
|
751
|
-
data: [
|
|
752
|
-
{ type: 'appStoreVersionLocalizations', id: `\${new-${platform}VersionLocalization-id}` },
|
|
753
|
-
],
|
|
754
|
-
},
|
|
755
|
-
},
|
|
756
|
-
},
|
|
757
|
-
{
|
|
758
|
-
type: 'appStoreVersionLocalizations',
|
|
759
|
-
id: `\${new-${platform}VersionLocalization-id}`,
|
|
760
|
-
attributes: { locale: primaryLocale },
|
|
761
|
-
},
|
|
762
|
-
],
|
|
763
|
-
},
|
|
764
|
-
});
|
|
765
|
-
assertAppStoreConnectResponseOK(response, 'App Store Connect app creation');
|
|
766
|
-
const app = singleResource(response.body?.data);
|
|
767
|
-
if (!app?.id) {
|
|
768
|
-
throw new Error('App Store Connect app creation did not return an app ID.');
|
|
769
|
-
}
|
|
770
|
-
return app;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
export type EnsureAppStoreConnectAppOptions = CreateAppStoreConnectAppOptions;
|
|
774
|
-
|
|
775
|
-
/**
|
|
776
|
-
* Returns the App Store Connect app record for the bundle ID, creating it
|
|
777
|
-
* when it does not exist yet. A first-time IPA upload fails without an app
|
|
778
|
-
* record, so run this before the first publish of a new bundle ID.
|
|
779
|
-
*/
|
|
780
|
-
export async function ensureAppStoreConnectApp(options: EnsureAppStoreConnectAppOptions) {
|
|
781
|
-
const existing = await findAppStoreConnectApp({ relay: options.relay, bundleId: options.bundleId });
|
|
782
|
-
if (existing) {
|
|
783
|
-
return { app: existing, created: false };
|
|
784
|
-
}
|
|
785
|
-
return { app: await createAppStoreConnectApp(options), created: true };
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
export function assertAppStoreConnectResponseOK(
|
|
789
|
-
response: AppleRelayResponse<AppStoreConnectEnvelope>,
|
|
790
|
-
label: string,
|
|
791
|
-
) {
|
|
792
|
-
const errors = response.body?.errors;
|
|
793
|
-
if (errors && errors.length > 0) {
|
|
794
|
-
const detail = errors
|
|
795
|
-
.map((error) => error.detail ?? error.title ?? error.code ?? error.status)
|
|
796
|
-
.filter(Boolean)
|
|
797
|
-
.join('; ');
|
|
798
|
-
throw new Error(`${label} failed: ${detail || `HTTP ${response.status}`}`);
|
|
799
|
-
}
|
|
800
|
-
if (response.status < 200 || response.status >= 300) {
|
|
801
|
-
throw new Error(`${label} failed: HTTP ${response.status} ${response.rawBody ?? ''}`.trim());
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
function resourceArray(data: AppStoreConnectResource | AppStoreConnectResource[] | undefined) {
|
|
806
|
-
if (!data) return [];
|
|
807
|
-
return Array.isArray(data) ? data : [data];
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
function singleResource(data: AppStoreConnectResource | AppStoreConnectResource[] | undefined) {
|
|
811
|
-
if (!data) return undefined;
|
|
812
|
-
return Array.isArray(data) ? data[0] : data;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
function stringField(record: Record<string, unknown> | undefined, key: string) {
|
|
816
|
-
const value = record?.[key];
|
|
817
|
-
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
export function assertAppleDeveloperPortalResponseOK(
|
|
821
|
-
response: AppleDeveloperPortalResponse | undefined,
|
|
822
|
-
label: string,
|
|
823
|
-
) {
|
|
824
|
-
if (!response) {
|
|
825
|
-
throw new Error(`${label} returned an empty response.`);
|
|
826
|
-
}
|
|
827
|
-
if (response.resultCode !== undefined && response.resultCode !== 0) {
|
|
828
|
-
throw new Error(
|
|
829
|
-
`${label} failed: ${response.userString ?? response.resultString ?? response.resultCode}`,
|
|
830
|
-
);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
function uniqueAppleTeams(teams: AppleDeveloperPortalTeam[]) {
|
|
835
|
-
const seen = new Set<string>();
|
|
836
|
-
const result: AppleDeveloperPortalTeam[] = [];
|
|
837
|
-
for (const team of teams) {
|
|
838
|
-
const key = String(
|
|
839
|
-
team.teamId ?? team.providerId ?? team.publicProviderId ?? team.name ?? JSON.stringify(team),
|
|
840
|
-
);
|
|
841
|
-
if (seen.has(key)) continue;
|
|
842
|
-
seen.add(key);
|
|
843
|
-
result.push(team);
|
|
844
|
-
}
|
|
845
|
-
return result;
|
|
846
|
-
}
|