@limrun/ui 0.9.0-rc.9 → 0.13.3-rc.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 (67) hide show
  1. package/README.md +25 -8
  2. package/dist/app-store-relay/index.cjs +9 -0
  3. package/dist/app-store-relay/index.d.ts +102 -0
  4. package/dist/app-store-relay/index.js +430 -0
  5. package/dist/app-store-relay/react.cjs +1 -0
  6. package/dist/app-store-relay/react.d.ts +15 -0
  7. package/dist/app-store-relay/react.js +65 -0
  8. package/dist/browser-storage-BJ__DGJ6.mjs +202 -0
  9. package/dist/browser-storage-C1jQLXat.js +1 -0
  10. package/dist/client-Bk1N3XIF.mjs +228 -0
  11. package/dist/client-CnbCWvCs.js +1 -0
  12. package/dist/components/device-install/index.d.ts +1 -2
  13. package/dist/core/device-install/apple/provisioning.d.ts +55 -4
  14. package/dist/core/device-install/operations/limbuild-client.d.ts +15 -1
  15. package/dist/core/device-install/storage/browser-storage.d.ts +9 -5
  16. package/dist/core/device-install/types.d.ts +4 -1
  17. package/dist/device-build/index.cjs +1 -0
  18. package/dist/device-build/index.d.ts +4 -0
  19. package/dist/device-build/index.js +84 -0
  20. package/dist/device-build/react.cjs +1 -0
  21. package/dist/device-build/react.d.ts +20 -0
  22. package/dist/device-build/react.js +66 -0
  23. package/dist/device-build/signing.d.ts +20 -0
  24. package/dist/device-install/index.cjs +1 -1
  25. package/dist/device-install/index.d.ts +18 -3
  26. package/dist/device-install/index.js +570 -76
  27. package/dist/device-install/react.cjs +1 -1
  28. package/dist/device-install/react.d.ts +23 -1
  29. package/dist/device-install/react.js +93 -2
  30. package/dist/hooks/index.d.ts +1 -1
  31. package/dist/index-BXg7HdIs.mjs +11547 -0
  32. package/dist/index-CMeQfhYy.js +22 -0
  33. package/dist/index.cjs +1 -1
  34. package/dist/index.d.ts +0 -2
  35. package/dist/index.js +502 -495
  36. package/dist/limbuild-client-CFJhYsRx.mjs +79 -0
  37. package/dist/limbuild-client-C_CMNLYV.js +1 -0
  38. package/dist/provisioning-CdseoMJQ.mjs +239 -0
  39. package/dist/provisioning-D2ZZQeyX.js +1 -0
  40. package/package.json +21 -1
  41. package/src/app-store-relay/index.test.ts +74 -0
  42. package/src/app-store-relay/index.ts +447 -0
  43. package/src/app-store-relay/react.ts +125 -0
  44. package/src/components/device-install/index.ts +1 -2
  45. package/src/core/device-install/apple/provisioning.test.ts +84 -0
  46. package/src/core/device-install/apple/provisioning.ts +91 -7
  47. package/src/core/device-install/operations/limbuild-client.ts +33 -3
  48. package/src/core/device-install/storage/browser-storage.ts +29 -14
  49. package/src/core/device-install/types.ts +5 -1
  50. package/src/device-build/index.ts +42 -0
  51. package/src/device-build/react.ts +128 -0
  52. package/src/device-build/signing.ts +94 -0
  53. package/src/device-install/index.ts +49 -3
  54. package/src/device-install/react.ts +180 -1
  55. package/src/hooks/index.ts +1 -1
  56. package/src/index.ts +1 -4
  57. package/vite.config.ts +4 -0
  58. package/dist/components/device-install/device-install-dialog.d.ts +0 -5
  59. package/dist/device-install-dialog-CqxDEH85.mjs +0 -443
  60. package/dist/device-install-dialog-jvqQ-fuo.js +0 -2
  61. package/dist/device-install-dialog.css +0 -1
  62. package/dist/hooks/use-device-install.d.ts +0 -73
  63. package/dist/use-device-install-CAPli9MR.js +0 -31
  64. package/dist/use-device-install-H8dqqvbR.mjs +0 -13624
  65. package/src/components/device-install/device-install-dialog.css +0 -325
  66. package/src/components/device-install/device-install-dialog.tsx +0 -495
  67. package/src/hooks/use-device-install.ts +0 -1219
@@ -0,0 +1,447 @@
1
+ import {
2
+ createAdHocProfileRequest,
3
+ createBundleIDRequest,
4
+ createDevelopmentProfileRequest,
5
+ downloadCertificateRequest,
6
+ downloadDistributionCertificateRequest,
7
+ downloadProfileRequest,
8
+ findAdHocProfilesRequest,
9
+ findBundleIDRequest,
10
+ findDevelopmentCertificatesRequest,
11
+ findDevelopmentProfilesRequest,
12
+ findDeviceRequest,
13
+ findDistributionCertificatesRequest,
14
+ listTeamsRequest,
15
+ proxyProvisioningRequest,
16
+ registerDeviceRequest,
17
+ submitDevelopmentCSRRequest,
18
+ submitDistributionCSRRequest,
19
+ type AppleDeveloperPortalAppID,
20
+ type AppleDeveloperPortalDevice,
21
+ type AppleDeveloperPortalResponse,
22
+ type AppleDeveloperPortalTeam,
23
+ type AppleProvisioningRequest,
24
+ type AppleRelayResponse,
25
+ } from '../core/device-install/apple';
26
+
27
+ export * from '../core/device-install/apple/client';
28
+ export * from '../core/device-install/apple/crypto';
29
+ export * from '../core/device-install/apple/gsa-srp';
30
+ export type {
31
+ AppleDeveloperPortalAppID,
32
+ AppleDeveloperPortalDevice,
33
+ AppleDeveloperPortalResponse,
34
+ AppleDeveloperPortalTeam,
35
+ AppleProvisioningRequest,
36
+ AppleRelayResponse,
37
+ } from '../core/device-install/apple';
38
+ export {
39
+ createAppleRelaySession,
40
+ deleteAppleRelaySession,
41
+ fetchAppleAccountSession,
42
+ proxyPhoneTwoFactorCode,
43
+ proxySrpComplete,
44
+ proxySrpInit,
45
+ proxyTwoFactorCode,
46
+ triggerPhoneTwoFactor,
47
+ triggerTrustedDeviceTwoFactor,
48
+ } from '../core/device-install/apple';
49
+
50
+ export type AppleRelayClientOptions = {
51
+ apiUrl: string;
52
+ token?: string;
53
+ appleSessionId: string;
54
+ };
55
+
56
+ export type AppleTeamScopedOptions = AppleRelayClientOptions & {
57
+ teamId?: string;
58
+ };
59
+
60
+ export type AppleCertificateKind = 'development' | 'distribution';
61
+
62
+ export type AppleProfileKind = 'development' | 'adhoc';
63
+
64
+ export type RequestAppleProvisioningOptions<T = unknown> = AppleRelayClientOptions & {
65
+ request: AppleProvisioningRequest;
66
+ label?: string;
67
+ validatePortalResponse?: boolean;
68
+ mapBody?: (body: AppleDeveloperPortalResponse | undefined, response: AppleRelayResponse<T>) => T;
69
+ };
70
+
71
+ export type MappedAppleProvisioningOptions<T> = RequestAppleProvisioningOptions<T> & {
72
+ mapBody: (body: AppleDeveloperPortalResponse | undefined, response: AppleRelayResponse<T>) => T;
73
+ };
74
+
75
+ export type ListAppleCertificatesOptions = AppleTeamScopedOptions & {
76
+ certificateKind?: AppleCertificateKind;
77
+ };
78
+
79
+ export type CreateAppleCertificateOptions = AppleTeamScopedOptions & {
80
+ certificateKind?: AppleCertificateKind;
81
+ csrPEM: string;
82
+ };
83
+
84
+ export type DownloadAppleCertificateOptions = AppleTeamScopedOptions & {
85
+ certificateKind?: AppleCertificateKind;
86
+ certificateId: string;
87
+ };
88
+
89
+ export type DeleteAppleCertificateOptions = DownloadAppleCertificateOptions;
90
+
91
+ export type ListAppleBundleIDsOptions = AppleTeamScopedOptions & {
92
+ search?: string;
93
+ };
94
+
95
+ export type CreateAppleBundleIDOptions = AppleTeamScopedOptions & {
96
+ bundleId: string;
97
+ name?: string;
98
+ };
99
+
100
+ export type UpdateAppleBundleIDOptions = AppleTeamScopedOptions & {
101
+ appIdId: string;
102
+ bundleId?: string;
103
+ name?: string;
104
+ };
105
+
106
+ export type DeleteAppleBundleIDOptions = AppleTeamScopedOptions & {
107
+ appIdId: string;
108
+ };
109
+
110
+ export type ListAppleDevicesOptions = AppleTeamScopedOptions & {
111
+ deviceUDID?: string;
112
+ };
113
+
114
+ export type RegisterAppleDeviceOptions = AppleTeamScopedOptions & {
115
+ deviceUDID: string;
116
+ name?: string;
117
+ };
118
+
119
+ export type UpdateAppleDeviceOptions = AppleTeamScopedOptions & {
120
+ deviceId: string;
121
+ name?: string;
122
+ };
123
+
124
+ export type DeleteAppleDeviceOptions = AppleTeamScopedOptions & {
125
+ deviceId: string;
126
+ };
127
+
128
+ export type ListAppleProfilesOptions = AppleTeamScopedOptions & {
129
+ profileKind?: AppleProfileKind;
130
+ bundleId?: string;
131
+ };
132
+
133
+ export type CreateAppleProfileOptions = AppleTeamScopedOptions & {
134
+ profileKind?: AppleProfileKind;
135
+ bundleId: string;
136
+ appIdId: string;
137
+ certificateIds: string[];
138
+ deviceIds: string[];
139
+ name?: string;
140
+ };
141
+
142
+ export type DownloadAppleProfileOptions = AppleTeamScopedOptions & {
143
+ profileId: string;
144
+ };
145
+
146
+ export type DeleteAppleProfileOptions = DownloadAppleProfileOptions;
147
+
148
+ export function requestAppleProvisioning<T>(options: MappedAppleProvisioningOptions<T>): Promise<T>;
149
+ export function requestAppleProvisioning<T = AppleDeveloperPortalResponse>(
150
+ options: RequestAppleProvisioningOptions<T>,
151
+ ): Promise<AppleRelayResponse<T>>;
152
+ export async function requestAppleProvisioning<T = AppleDeveloperPortalResponse>({
153
+ apiUrl,
154
+ token,
155
+ appleSessionId,
156
+ request,
157
+ label = 'Apple Developer Portal request',
158
+ validatePortalResponse = true,
159
+ mapBody,
160
+ }: RequestAppleProvisioningOptions<T>) {
161
+ const response = await proxyProvisioningRequest<T>(apiUrl, appleSessionId, request, token);
162
+ if (validatePortalResponse) {
163
+ assertAppleDeveloperPortalResponseOK(response.body as AppleDeveloperPortalResponse | undefined, label);
164
+ }
165
+ return mapBody ? mapBody(response.body as AppleDeveloperPortalResponse | undefined, response) : response;
166
+ }
167
+
168
+ export async function listAppleTeams(options: AppleRelayClientOptions) {
169
+ return requestAppleProvisioning<AppleDeveloperPortalTeam[]>({
170
+ ...options,
171
+ request: listTeamsRequest(),
172
+ label: 'Apple Developer team list',
173
+ mapBody: (body) => uniqueAppleTeams([...(body?.teams ?? []), ...(body?.availableProviders ?? []), ...(body?.provider ? [body.provider] : [])]),
174
+ });
175
+ }
176
+
177
+ export async function listAppleCertificates({
178
+ certificateKind = 'development',
179
+ teamId = '',
180
+ ...options
181
+ }: ListAppleCertificatesOptions) {
182
+ return requestAppleProvisioning<Array<Record<string, unknown>>>({
183
+ ...options,
184
+ request:
185
+ certificateKind === 'distribution'
186
+ ? findDistributionCertificatesRequest(teamId)
187
+ : findDevelopmentCertificatesRequest(teamId),
188
+ label: 'Apple Developer certificate list',
189
+ mapBody: (body) => body?.certRequests ?? [],
190
+ });
191
+ }
192
+
193
+ export async function createAppleCertificate({
194
+ certificateKind = 'development',
195
+ teamId = '',
196
+ csrPEM,
197
+ ...options
198
+ }: CreateAppleCertificateOptions) {
199
+ return requestAppleProvisioning<Record<string, unknown> | undefined>({
200
+ ...options,
201
+ request:
202
+ certificateKind === 'distribution'
203
+ ? submitDistributionCSRRequest({ csrPEM, teamID: teamId })
204
+ : submitDevelopmentCSRRequest({ csrPEM, teamID: teamId }),
205
+ label: certificateKind === 'distribution' ? 'Apple Distribution certificate creation' : 'Apple Development certificate creation',
206
+ mapBody: (body) => body?.certRequest,
207
+ });
208
+ }
209
+
210
+ export async function downloadAppleCertificate({
211
+ certificateKind = 'development',
212
+ certificateId,
213
+ teamId = '',
214
+ ...options
215
+ }: DownloadAppleCertificateOptions) {
216
+ return requestAppleProvisioning<AppleRelayResponse>({
217
+ ...options,
218
+ request:
219
+ certificateKind === 'distribution'
220
+ ? downloadDistributionCertificateRequest(certificateId, teamId)
221
+ : downloadCertificateRequest(certificateId, teamId),
222
+ label: 'Apple certificate download',
223
+ validatePortalResponse: false,
224
+ mapBody: (_body, response) => response,
225
+ });
226
+ }
227
+
228
+ export async function deleteAppleCertificate({
229
+ certificateKind = 'development',
230
+ certificateId,
231
+ teamId = '',
232
+ ...options
233
+ }: DeleteAppleCertificateOptions) {
234
+ return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
235
+ ...options,
236
+ request: {
237
+ method: 'POST',
238
+ path: '/account/ios/certificate/revokeCertificate.action',
239
+ payload: {
240
+ teamId,
241
+ certificateId,
242
+ type: certificateKind === 'distribution' ? 'WXV89964HE' : '83Q87W3TGH',
243
+ },
244
+ },
245
+ label: 'Apple certificate deletion',
246
+ mapBody: (body) => body,
247
+ });
248
+ }
249
+
250
+ export async function listAppleBundleIDs({ teamId = '', search = '', ...options }: ListAppleBundleIDsOptions) {
251
+ return requestAppleProvisioning<AppleDeveloperPortalAppID[]>({
252
+ ...options,
253
+ request: findBundleIDRequest({ bundleID: search, teamID: teamId }),
254
+ label: 'Apple bundle ID list',
255
+ mapBody: (body) => body?.appIds ?? [],
256
+ });
257
+ }
258
+
259
+ export async function createAppleBundleID({ teamId = '', bundleId, name, ...options }: CreateAppleBundleIDOptions) {
260
+ return requestAppleProvisioning<Record<string, unknown> | undefined>({
261
+ ...options,
262
+ request: createBundleIDRequest({ bundleID: bundleId, teamID: teamId, name }),
263
+ label: 'Apple bundle ID creation',
264
+ mapBody: (body) => body?.appId,
265
+ });
266
+ }
267
+
268
+ export async function updateAppleBundleID({
269
+ teamId = '',
270
+ appIdId,
271
+ bundleId,
272
+ name,
273
+ ...options
274
+ }: UpdateAppleBundleIDOptions) {
275
+ return requestAppleProvisioning<Record<string, unknown> | undefined>({
276
+ ...options,
277
+ request: {
278
+ method: 'POST',
279
+ path: '/account/ios/identifiers/updateAppId.action',
280
+ payload: {
281
+ teamId,
282
+ appIdId,
283
+ ...(bundleId ? { identifier: bundleId } : {}),
284
+ ...(name ? { name } : {}),
285
+ },
286
+ },
287
+ label: 'Apple bundle ID update',
288
+ mapBody: (body) => body?.appId,
289
+ });
290
+ }
291
+
292
+ export async function deleteAppleBundleID({ teamId = '', appIdId, ...options }: DeleteAppleBundleIDOptions) {
293
+ return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
294
+ ...options,
295
+ request: {
296
+ method: 'POST',
297
+ path: '/account/ios/identifiers/deleteAppId.action',
298
+ payload: { teamId, appIdId },
299
+ },
300
+ label: 'Apple bundle ID deletion',
301
+ mapBody: (body) => body,
302
+ });
303
+ }
304
+
305
+ export async function listAppleDevices({ teamId = '', deviceUDID = '', ...options }: ListAppleDevicesOptions) {
306
+ return requestAppleProvisioning<AppleDeveloperPortalDevice[]>({
307
+ ...options,
308
+ request: findDeviceRequest({ deviceUDID, teamID: teamId }),
309
+ label: 'Apple device list',
310
+ mapBody: (body) => body?.devices ?? [],
311
+ });
312
+ }
313
+
314
+ export async function registerAppleDevice({ teamId = '', deviceUDID, name, ...options }: RegisterAppleDeviceOptions) {
315
+ return requestAppleProvisioning<Record<string, unknown> | undefined>({
316
+ ...options,
317
+ request: registerDeviceRequest({ deviceUDID, teamID: teamId, name }),
318
+ label: 'Apple device registration',
319
+ mapBody: (body) => body?.device,
320
+ });
321
+ }
322
+
323
+ export async function updateAppleDevice({ teamId = '', deviceId, name, ...options }: UpdateAppleDeviceOptions) {
324
+ return requestAppleProvisioning<Record<string, unknown> | undefined>({
325
+ ...options,
326
+ request: {
327
+ method: 'POST',
328
+ path: '/account/ios/device/updateDevice.action',
329
+ payload: { teamId, deviceId, ...(name ? { name } : {}) },
330
+ },
331
+ label: 'Apple device update',
332
+ mapBody: (body) => body?.device,
333
+ });
334
+ }
335
+
336
+ export async function deleteAppleDevice({ teamId = '', deviceId, ...options }: DeleteAppleDeviceOptions) {
337
+ return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
338
+ ...options,
339
+ request: {
340
+ method: 'POST',
341
+ path: '/account/ios/device/deleteDevice.action',
342
+ payload: { teamId, deviceId },
343
+ },
344
+ label: 'Apple device deletion',
345
+ mapBody: (body) => body,
346
+ });
347
+ }
348
+
349
+ export async function listAppleProfiles({
350
+ profileKind = 'development',
351
+ teamId = '',
352
+ bundleId = '',
353
+ ...options
354
+ }: ListAppleProfilesOptions) {
355
+ return requestAppleProvisioning<Array<Record<string, unknown>>>({
356
+ ...options,
357
+ request:
358
+ profileKind === 'adhoc'
359
+ ? findAdHocProfilesRequest({ bundleID: bundleId, teamID: teamId })
360
+ : findDevelopmentProfilesRequest({ bundleID: bundleId, teamID: teamId }),
361
+ label: 'Apple provisioning profile list',
362
+ mapBody: (body) => body?.provisioningProfiles ?? [],
363
+ });
364
+ }
365
+
366
+ export async function createAppleProfile({
367
+ profileKind = 'development',
368
+ teamId = '',
369
+ bundleId,
370
+ appIdId,
371
+ certificateIds,
372
+ deviceIds,
373
+ name,
374
+ ...options
375
+ }: CreateAppleProfileOptions) {
376
+ const [certificateId] = certificateIds;
377
+ if (!certificateId) {
378
+ throw new Error('At least one certificate ID is required to create an Apple provisioning profile.');
379
+ }
380
+ return requestAppleProvisioning<Record<string, unknown> | undefined>({
381
+ ...options,
382
+ request:
383
+ profileKind === 'adhoc'
384
+ ? createAdHocProfileRequest({
385
+ bundleID: bundleId,
386
+ teamID: teamId,
387
+ appIDID: appIdId,
388
+ certificateID: certificateId,
389
+ deviceIDs: deviceIds,
390
+ name,
391
+ })
392
+ : createDevelopmentProfileRequest({
393
+ bundleID: bundleId,
394
+ teamID: teamId,
395
+ appIDID: appIdId,
396
+ certificateID: certificateId,
397
+ deviceIDs: deviceIds,
398
+ name,
399
+ }),
400
+ label: 'Apple provisioning profile creation',
401
+ mapBody: (body) => body?.provisioningProfile,
402
+ });
403
+ }
404
+
405
+ export async function downloadAppleProfile({ teamId = '', profileId, ...options }: DownloadAppleProfileOptions) {
406
+ return requestAppleProvisioning<AppleRelayResponse>({
407
+ ...options,
408
+ request: downloadProfileRequest(profileId, teamId),
409
+ label: 'Apple provisioning profile download',
410
+ validatePortalResponse: false,
411
+ mapBody: (_body, response) => response,
412
+ });
413
+ }
414
+
415
+ export async function deleteAppleProfile({ teamId = '', profileId, ...options }: DeleteAppleProfileOptions) {
416
+ return requestAppleProvisioning<AppleDeveloperPortalResponse | undefined>({
417
+ ...options,
418
+ request: {
419
+ method: 'POST',
420
+ path: '/account/ios/profile/deleteProvisioningProfile.action',
421
+ payload: { teamId, provisioningProfileId: profileId },
422
+ },
423
+ label: 'Apple provisioning profile deletion',
424
+ mapBody: (body) => body,
425
+ });
426
+ }
427
+
428
+ export function assertAppleDeveloperPortalResponseOK(response: AppleDeveloperPortalResponse | undefined, label: string) {
429
+ if (!response) {
430
+ throw new Error(`${label} returned an empty response.`);
431
+ }
432
+ if (response.resultCode !== undefined && response.resultCode !== 0) {
433
+ throw new Error(`${label} failed: ${response.userString ?? response.resultString ?? response.resultCode}`);
434
+ }
435
+ }
436
+
437
+ function uniqueAppleTeams(teams: AppleDeveloperPortalTeam[]) {
438
+ const seen = new Set<string>();
439
+ const result: AppleDeveloperPortalTeam[] = [];
440
+ for (const team of teams) {
441
+ const key = String(team.teamId ?? team.providerId ?? team.publicProviderId ?? team.name ?? JSON.stringify(team));
442
+ if (seen.has(key)) continue;
443
+ seen.add(key);
444
+ result.push(team);
445
+ }
446
+ return result;
447
+ }
@@ -0,0 +1,125 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import {
3
+ startBrowserOwnedAppleIDLogin,
4
+ type AppleIDLoginInput,
5
+ type AppleIDLoginResult,
6
+ type AppleRelayResponse,
7
+ } from './index';
8
+
9
+ export type UseAppleIDLoginOptions = Pick<AppleIDLoginInput, 'limbuildApiUrl' | 'token'>;
10
+
11
+ export type AppleIDLoginStatus =
12
+ | 'idle'
13
+ | 'authenticating'
14
+ | 'two-factor-required'
15
+ | 'authenticated'
16
+ | 'error';
17
+
18
+ export type UseAppleIDLoginResult = {
19
+ status: AppleIDLoginStatus;
20
+ session?: AppleIDLoginResult;
21
+ completeResponse?: AppleRelayResponse;
22
+ twoFactorChallengeResponse?: AppleRelayResponse;
23
+ error?: string;
24
+ startLogin: (input: Pick<AppleIDLoginInput, 'accountName' | 'password'>) => Promise<AppleIDLoginResult | undefined>;
25
+ submitTwoFactorCode: (code: string) => Promise<AppleRelayResponse | undefined>;
26
+ finalize: () => Promise<AppleRelayResponse | undefined>;
27
+ close: () => Promise<void>;
28
+ };
29
+
30
+ export function useAppleIDLogin({ limbuildApiUrl, token }: UseAppleIDLoginOptions): UseAppleIDLoginResult {
31
+ const [status, setStatus] = useState<AppleIDLoginStatus>('idle');
32
+ const [session, setSession] = useState<AppleIDLoginResult | undefined>();
33
+ const [completeResponse, setCompleteResponse] = useState<AppleRelayResponse | undefined>();
34
+ const [twoFactorChallengeResponse, setTwoFactorChallengeResponse] = useState<AppleRelayResponse | undefined>();
35
+ const [error, setError] = useState<string | undefined>();
36
+ const sessionRef = useRef<AppleIDLoginResult | undefined>(undefined);
37
+
38
+ const close = useCallback(async () => {
39
+ await sessionRef.current?.close().catch(() => undefined);
40
+ sessionRef.current = undefined;
41
+ setSession(undefined);
42
+ setCompleteResponse(undefined);
43
+ setTwoFactorChallengeResponse(undefined);
44
+ setError(undefined);
45
+ setStatus('idle');
46
+ }, []);
47
+
48
+ useEffect(() => {
49
+ return () => {
50
+ void sessionRef.current?.close();
51
+ sessionRef.current = undefined;
52
+ };
53
+ }, []);
54
+
55
+ const startLogin = useCallback(
56
+ async ({ accountName, password }: Pick<AppleIDLoginInput, 'accountName' | 'password'>) => {
57
+ setStatus('authenticating');
58
+ setError(undefined);
59
+ await sessionRef.current?.close().catch(() => undefined);
60
+ try {
61
+ const next = await startBrowserOwnedAppleIDLogin({
62
+ limbuildApiUrl,
63
+ token,
64
+ accountName,
65
+ password,
66
+ });
67
+ sessionRef.current = next;
68
+ setSession(next);
69
+ setCompleteResponse(next.completeResponse);
70
+ setTwoFactorChallengeResponse(next.twoFactorChallengeResponse);
71
+ setStatus(next.requiresTwoFactor ? 'two-factor-required' : 'authenticated');
72
+ return next;
73
+ } catch (caught) {
74
+ setSession(undefined);
75
+ sessionRef.current = undefined;
76
+ setError(errorMessage(caught));
77
+ setStatus('error');
78
+ return undefined;
79
+ }
80
+ },
81
+ [limbuildApiUrl, token],
82
+ );
83
+
84
+ const submitTwoFactorCode = useCallback(async (code: string) => {
85
+ const current = sessionRef.current;
86
+ if (!current) {
87
+ throw new Error('Start Apple ID login before submitting a two-factor code.');
88
+ }
89
+ setStatus('authenticating');
90
+ setError(undefined);
91
+ try {
92
+ const response = await current.finishTwoFactor(code);
93
+ setStatus('authenticated');
94
+ return response;
95
+ } catch (caught) {
96
+ setError(errorMessage(caught));
97
+ setStatus('error');
98
+ return undefined;
99
+ }
100
+ }, []);
101
+
102
+ const finalize = useCallback(async () => {
103
+ const current = sessionRef.current;
104
+ if (!current) {
105
+ throw new Error('Start Apple ID login before finalizing the Apple session.');
106
+ }
107
+ return current.finalize();
108
+ }, []);
109
+
110
+ return {
111
+ status,
112
+ session,
113
+ completeResponse,
114
+ twoFactorChallengeResponse,
115
+ error,
116
+ startLogin,
117
+ submitTwoFactorCode,
118
+ finalize,
119
+ close,
120
+ };
121
+ }
122
+
123
+ function errorMessage(error: unknown) {
124
+ return error instanceof Error ? error.message : String(error);
125
+ }
@@ -1,2 +1 @@
1
- export { DeviceInstallDialog, DeviceInstallDialog as DeviceInstallRelay } from './device-install-dialog';
2
- export type { DeviceInstallDialogProps } from './device-install-dialog';
1
+ export {};
@@ -0,0 +1,84 @@
1
+ // @vitest-environment node
2
+
3
+ import { describe, expect, test } from 'vitest';
4
+ import {
5
+ createAdHocProfileRequest,
6
+ createDevelopmentProfileRequest,
7
+ downloadDistributionCertificateRequest,
8
+ findAdHocProfilesRequest,
9
+ findDevelopmentCertificatesRequest,
10
+ findDistributionCertificatesRequest,
11
+ submitDistributionCSRRequest,
12
+ } from './provisioning';
13
+
14
+ describe('Apple provisioning request helpers', () => {
15
+ test('keeps development certificate requests on Apple Development types', () => {
16
+ expect(findDevelopmentCertificatesRequest('TEAM').payload).toMatchObject({
17
+ teamId: 'TEAM',
18
+ types: '83Q87W3TGH,5QPB9NHCEI',
19
+ });
20
+ });
21
+
22
+ test('uses distribution certificate requests for Ad Hoc signing', () => {
23
+ expect(findDistributionCertificatesRequest('TEAM').payload).toMatchObject({
24
+ teamId: 'TEAM',
25
+ types: 'WXV89964HE,R58UK2EWSO',
26
+ });
27
+ expect(submitDistributionCSRRequest({ csrPEM: 'csr', teamID: 'TEAM' }).payload).toMatchObject({
28
+ teamId: 'TEAM',
29
+ type: 'WXV89964HE',
30
+ csrContent: 'csr',
31
+ });
32
+ expect(downloadDistributionCertificateRequest('CERT', 'TEAM').payload).toMatchObject({
33
+ teamId: 'TEAM',
34
+ certificateId: 'CERT',
35
+ type: 'WXV89964HE',
36
+ });
37
+ });
38
+
39
+ test('builds separate development and Ad Hoc profile payloads', () => {
40
+ expect(
41
+ createDevelopmentProfileRequest({
42
+ bundleID: 'com.example.app',
43
+ teamID: 'TEAM',
44
+ appIDID: 'APP',
45
+ certificateID: 'CERT',
46
+ deviceIDs: ['DEVICE'],
47
+ }).payload,
48
+ ).toMatchObject({
49
+ teamId: 'TEAM',
50
+ provisioningProfileName: 'Limrun com.example.app',
51
+ certificateIds: ['CERT'],
52
+ appIdId: 'APP',
53
+ deviceIds: ['DEVICE'],
54
+ distributionType: 'limited',
55
+ subPlatform: 'ios',
56
+ });
57
+
58
+ expect(
59
+ createAdHocProfileRequest({
60
+ bundleID: 'com.example.app',
61
+ teamID: 'TEAM',
62
+ appIDID: 'APP',
63
+ certificateID: 'CERT',
64
+ deviceIDs: ['DEVICE'],
65
+ }).payload,
66
+ ).toMatchObject({
67
+ teamId: 'TEAM',
68
+ provisioningProfileName: 'Limrun Ad Hoc com.example.app',
69
+ certificateIds: ['CERT'],
70
+ appIdId: 'APP',
71
+ deviceIds: ['DEVICE'],
72
+ distributionType: 'adhoc',
73
+ subPlatform: 'ios',
74
+ });
75
+ });
76
+
77
+ test('filters Ad Hoc profile list requests by Ad Hoc distribution type', () => {
78
+ expect(findAdHocProfilesRequest({ bundleID: 'com.example.app', teamID: 'TEAM' }).payload).toMatchObject({
79
+ teamId: 'TEAM',
80
+ search: 'com.example.app',
81
+ distributionType: 'adhoc',
82
+ });
83
+ });
84
+ });