@limrun/ui 0.14.0-rc.5 → 0.14.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 (115) hide show
  1. package/README.md +9 -26
  2. package/dist/components/remote-control.d.ts +36 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1351 -1196
  6. package/package.json +10 -51
  7. package/src/components/remote-control.tsx +414 -2
  8. package/src/index.ts +1 -1
  9. package/vite.config.ts +0 -9
  10. package/dist/app-store-relay/index.cjs +0 -9
  11. package/dist/app-store-relay/index.d.ts +0 -204
  12. package/dist/app-store-relay/index.js +0 -677
  13. package/dist/app-store-relay/react.cjs +0 -1
  14. package/dist/app-store-relay/react.d.ts +0 -15
  15. package/dist/app-store-relay/react.js +0 -69
  16. package/dist/apple-credentials/index.cjs +0 -1
  17. package/dist/apple-credentials/index.d.ts +0 -110
  18. package/dist/apple-credentials/index.js +0 -291
  19. package/dist/browser-storage-BnO-Ma-e.mjs +0 -219
  20. package/dist/browser-storage-DLxIqZk5.js +0 -1
  21. package/dist/client-C9z_sgOf.js +0 -1
  22. package/dist/client-CFDB4mX5.mjs +0 -191
  23. package/dist/core/device-install/apple/client.d.ts +0 -18
  24. package/dist/core/device-install/apple/crypto.d.ts +0 -20
  25. package/dist/core/device-install/apple/gsa-srp.d.ts +0 -26
  26. package/dist/core/device-install/apple/index.d.ts +0 -5
  27. package/dist/core/device-install/apple/provisioning.d.ts +0 -237
  28. package/dist/core/device-install/apple/relay.d.ts +0 -53
  29. package/dist/core/device-install/index.d.ts +0 -4
  30. package/dist/core/device-install/operations/index.d.ts +0 -6
  31. package/dist/core/device-install/operations/limbuild-client.d.ts +0 -43
  32. package/dist/core/device-install/operations/operations.d.ts +0 -47
  33. package/dist/core/device-install/operations/relay-client.d.ts +0 -26
  34. package/dist/core/device-install/operations/relay-protocol.d.ts +0 -27
  35. package/dist/core/device-install/operations/usbmux.d.ts +0 -32
  36. package/dist/core/device-install/operations/webusb.d.ts +0 -21
  37. package/dist/core/device-install/storage/browser-storage.d.ts +0 -56
  38. package/dist/core/device-install/storage/index.d.ts +0 -2
  39. package/dist/core/device-install/storage/secret-store.d.ts +0 -124
  40. package/dist/core/device-install/types.d.ts +0 -57
  41. package/dist/core/errors.d.ts +0 -1
  42. package/dist/core/play-publish/google.d.ts +0 -13
  43. package/dist/core/play-publish/publish.d.ts +0 -34
  44. package/dist/device-build/index.cjs +0 -1
  45. package/dist/device-build/index.d.ts +0 -4
  46. package/dist/device-build/index.js +0 -85
  47. package/dist/device-build/react.cjs +0 -1
  48. package/dist/device-build/react.d.ts +0 -21
  49. package/dist/device-build/react.js +0 -68
  50. package/dist/device-build/signing.d.ts +0 -20
  51. package/dist/device-install/demo/demo.d.ts +0 -0
  52. package/dist/device-install/index.cjs +0 -1
  53. package/dist/device-install/index.d.ts +0 -18
  54. package/dist/device-install/index.js +0 -594
  55. package/dist/device-install/react.cjs +0 -1
  56. package/dist/device-install/react.d.ts +0 -24
  57. package/dist/device-install/react.js +0 -105
  58. package/dist/errors-BSkqS7U-.js +0 -1
  59. package/dist/errors-gy8yQJos.mjs +0 -6
  60. package/dist/gsa-srp-Bme3JLEi.mjs +0 -266
  61. package/dist/gsa-srp-CCIfUz7Y.js +0 -1
  62. package/dist/index-ChhtuQ7e.js +0 -22
  63. package/dist/index-YDu8K9a4.mjs +0 -11279
  64. package/dist/limbuild-client-BagK_FWE.mjs +0 -83
  65. package/dist/limbuild-client-CVDwq54z.js +0 -1
  66. package/dist/play-publish/index.cjs +0 -1
  67. package/dist/play-publish/index.d.ts +0 -2
  68. package/dist/play-publish/index.js +0 -109
  69. package/dist/play-publish/react.cjs +0 -1
  70. package/dist/play-publish/react.d.ts +0 -31
  71. package/dist/play-publish/react.js +0 -91
  72. package/dist/provisioning-CDPvxd1h.mjs +0 -270
  73. package/dist/provisioning-CbC4h--7.js +0 -1
  74. package/src/app-store-relay/index.test.ts +0 -60
  75. package/src/app-store-relay/index.ts +0 -846
  76. package/src/app-store-relay/react.ts +0 -131
  77. package/src/apple-credentials/index.test.ts +0 -180
  78. package/src/apple-credentials/index.ts +0 -388
  79. package/src/core/device-install/apple/client.ts +0 -150
  80. package/src/core/device-install/apple/crypto.ts +0 -205
  81. package/src/core/device-install/apple/gsa-srp.ts +0 -123
  82. package/src/core/device-install/apple/index.ts +0 -5
  83. package/src/core/device-install/apple/provisioning.test.ts +0 -114
  84. package/src/core/device-install/apple/provisioning.ts +0 -419
  85. package/src/core/device-install/apple/relay.ts +0 -202
  86. package/src/core/device-install/index.ts +0 -4
  87. package/src/core/device-install/operations/index.ts +0 -6
  88. package/src/core/device-install/operations/limbuild-client.ts +0 -144
  89. package/src/core/device-install/operations/operations.ts +0 -239
  90. package/src/core/device-install/operations/relay-client.ts +0 -286
  91. package/src/core/device-install/operations/relay-protocol.ts +0 -71
  92. package/src/core/device-install/operations/usbmux.ts +0 -270
  93. package/src/core/device-install/operations/webusb-dom.d.ts +0 -54
  94. package/src/core/device-install/operations/webusb.ts +0 -110
  95. package/src/core/device-install/storage/browser-storage.test.ts +0 -65
  96. package/src/core/device-install/storage/browser-storage.ts +0 -325
  97. package/src/core/device-install/storage/index.ts +0 -2
  98. package/src/core/device-install/storage/secret-store.ts +0 -332
  99. package/src/core/device-install/types.ts +0 -69
  100. package/src/core/errors.ts +0 -3
  101. package/src/core/play-publish/google.ts +0 -113
  102. package/src/core/play-publish/publish.test.ts +0 -68
  103. package/src/core/play-publish/publish.ts +0 -89
  104. package/src/device-build/index.ts +0 -42
  105. package/src/device-build/react.ts +0 -130
  106. package/src/device-build/signing.ts +0 -94
  107. package/src/device-install/README.md +0 -423
  108. package/src/device-install/demo/demo.css +0 -493
  109. package/src/device-install/demo/demo.tsx +0 -1061
  110. package/src/device-install/demo/index.html +0 -12
  111. package/src/device-install/index.ts +0 -50
  112. package/src/device-install/react.ts +0 -195
  113. package/src/play-publish/README.md +0 -66
  114. package/src/play-publish/index.ts +0 -12
  115. package/src/play-publish/react.ts +0 -208
@@ -1,69 +0,0 @@
1
- export type DeviceInstallLog = (message: string, detail?: string) => void;
2
-
3
- export type DeviceInstallSigningMode = 'development' | 'adhoc';
4
-
5
- export type DeviceInstallStep = 'signing' | 'connect' | 'build' | 'install';
6
-
7
- export type DeviceInstallStepStatus = 'idle' | 'active' | 'complete' | 'error';
8
-
9
- export type DeviceInstallBusyAction = 'signing' | 'usb' | 'pair' | 'build' | 'install';
10
-
11
- export type DeviceInstallBuildStatus = 'idle' | 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
12
-
13
- export type BuildLogLine = {
14
- type: 'command' | 'stdout' | 'stderr' | 'meta';
15
- data: string;
16
- };
17
-
18
- export type DeviceHello = {
19
- serialNumber?: string;
20
- productName?: string;
21
- manufacturerName?: string;
22
- productId: number;
23
- vendorId: number;
24
- };
25
-
26
- export type PairRecordPayload = {
27
- udid: string;
28
- pairRecordBase64: string;
29
- };
30
-
31
- export type StoredPairRecord = PairRecordPayload & {
32
- productName?: string;
33
- updatedAt: string;
34
- };
35
-
36
- export type ProvisioningProfileInfo = {
37
- name?: string;
38
- uuid?: string;
39
- teamID?: string;
40
- applicationIdentifier?: string;
41
- bundleID?: string;
42
- provisionedDevices: string[];
43
- /**
44
- * Serial numbers (uppercase hex, no leading zeros) of the developer
45
- * certificates embedded in the profile; the profile is only usable for
46
- * signing with one of these certificates.
47
- */
48
- certificateSerialNumbers: string[];
49
- getTaskAllow?: boolean;
50
- expirationDate?: string;
51
- };
52
-
53
- export type StoredSigningAssets = {
54
- id: string;
55
- deviceUDID?: string;
56
- teamID?: string;
57
- signingMode?: DeviceInstallSigningMode;
58
- bundleID: string;
59
- certificateID?: string;
60
- certificateP12Base64: string;
61
- certificateFileName?: string;
62
- certificatePassword?: string;
63
- provisioningProfileBase64: string;
64
- profileFileName?: string;
65
- profile: ProvisioningProfileInfo;
66
- updatedAt: string;
67
- };
68
-
69
- export type PutSigningAssetsInput = Omit<StoredSigningAssets, 'id' | 'updatedAt'>;
@@ -1,3 +0,0 @@
1
- export function errorMessage(error: unknown): string {
2
- return error instanceof Error ? error.message : String(error);
3
- }
@@ -1,113 +0,0 @@
1
- const GSI_SRC = 'https://accounts.google.com/gsi/client';
2
-
3
- export const ANDROID_PUBLISHER_SCOPE = 'https://www.googleapis.com/auth/androidpublisher';
4
-
5
- type TokenResponse = { access_token?: string; error?: string; error_description?: string };
6
- type TokenClient = { requestAccessToken: () => void };
7
- type GoogleOAuth2 = {
8
- initTokenClient: (config: {
9
- client_id: string;
10
- scope: string;
11
- callback: (response: TokenResponse) => void;
12
- error_callback?: (error: { type?: string; message?: string }) => void;
13
- }) => TokenClient;
14
- };
15
-
16
- function googleOAuth2(): GoogleOAuth2 | undefined {
17
- return (globalThis as { google?: { accounts?: { oauth2?: GoogleOAuth2 } } }).google?.accounts?.oauth2;
18
- }
19
-
20
- let gsiLoad: Promise<void> | undefined;
21
-
22
- export function loadGoogleIdentityServices(): Promise<void> {
23
- if (googleOAuth2()) {
24
- return Promise.resolve();
25
- }
26
- if (typeof document === 'undefined') {
27
- return Promise.reject(new Error('Google sign-in requires a browser environment.'));
28
- }
29
- if (!gsiLoad) {
30
- gsiLoad = new Promise<void>((resolve, reject) => {
31
- const script = document.createElement('script');
32
- script.src = GSI_SRC;
33
- script.async = true;
34
- script.addEventListener(
35
- 'load',
36
- () => {
37
- if (googleOAuth2()) {
38
- resolve();
39
- } else {
40
- reject(new Error('Google sign-in script loaded without the OAuth client.'));
41
- }
42
- },
43
- { once: true },
44
- );
45
- script.addEventListener(
46
- 'error',
47
- () => {
48
- script.remove();
49
- reject(new Error('Failed to load the Google sign-in script.'));
50
- },
51
- { once: true },
52
- );
53
- document.head.appendChild(script);
54
- });
55
- // A failed load (offline, blocked CDN) must not poison retries.
56
- gsiLoad.catch(() => {
57
- gsiLoad = undefined;
58
- });
59
- }
60
- return gsiLoad;
61
- }
62
-
63
- export type RequestGoogleAccessTokenInput = {
64
- clientId: string;
65
- scope?: string;
66
- };
67
-
68
- /**
69
- * Opens the Google consent popup and resolves with a short-lived OAuth
70
- * access token. Call from a user gesture so popup blockers allow it, and
71
- * call loadGoogleIdentityServices beforehand (e.g. when the surrounding
72
- * dialog opens) to keep this call synchronous with the click.
73
- */
74
- export function requestGoogleAccessToken(input: RequestGoogleAccessTokenInput): Promise<string> {
75
- // When GSI is already loaded the popup must open inside the caller's
76
- // click turn: even an await of a resolved promise yields a microtask,
77
- // which the strictest popup blockers treat as leaving the gesture.
78
- if (googleOAuth2()) {
79
- return openTokenPopup(input);
80
- }
81
- return loadGoogleIdentityServices().then(() => openTokenPopup(input));
82
- }
83
-
84
- function openTokenPopup({
85
- clientId,
86
- scope = ANDROID_PUBLISHER_SCOPE,
87
- }: RequestGoogleAccessTokenInput): Promise<string> {
88
- return new Promise<string>((resolve, reject) => {
89
- const client = googleOAuth2()!.initTokenClient({
90
- client_id: clientId,
91
- scope,
92
- callback: (response) => {
93
- if (response.error) {
94
- reject(new Error(response.error_description || response.error));
95
- return;
96
- }
97
- if (!response.access_token) {
98
- reject(new Error('Google returned no access token.'));
99
- return;
100
- }
101
- resolve(response.access_token);
102
- },
103
- error_callback: (error) => {
104
- const fallback =
105
- error?.type === 'popup_failed_to_open' ?
106
- 'Google sign-in popup was blocked by the browser.'
107
- : 'Google sign-in was cancelled.';
108
- reject(new Error(error?.message || fallback));
109
- },
110
- });
111
- client.requestAccessToken();
112
- });
113
- }
@@ -1,68 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import { PlaystorePublishError, publishToPlaystore } from './publish';
3
-
4
- function mockFetch(status: number, body: string) {
5
- const fetchMock = vi.fn(async () => new Response(body, { status }));
6
- vi.stubGlobal('fetch', fetchMock);
7
- return fetchMock;
8
- }
9
-
10
- afterEach(() => {
11
- vi.unstubAllGlobals();
12
- });
13
-
14
- describe('publishToPlaystore', () => {
15
- it('posts to the publish path with auth and org headers and returns the versionCode', async () => {
16
- const fetchMock = mockFetch(200, JSON.stringify({ versionCode: 42 }));
17
- const result = await publishToPlaystore({
18
- registryApiUrl: 'https://registry-staging.limrun.dev',
19
- token: 'limrun-token',
20
- organizationId: 'org_x',
21
- accessToken: 'google-token',
22
- packageName: 'com.example.app',
23
- assetName: 'app-release.aab',
24
- });
25
- expect(result.versionCode).toBe(42);
26
- const [url, init] = fetchMock.mock.calls[0] as unknown as [string, RequestInit];
27
- expect(url).toBe('https://registry-staging.limrun.dev/android/playstore/publish');
28
- const headers = init.headers as Record<string, string>;
29
- expect(headers.Authorization).toBe('Bearer limrun-token');
30
- expect(headers['X-Limrun-Organization']).toBe('org_x');
31
- const body = JSON.parse(init.body as string) as Record<string, unknown>;
32
- expect(body).toEqual({
33
- accessToken: 'google-token',
34
- packageName: 'com.example.app',
35
- assetName: 'app-release.aab',
36
- });
37
- });
38
-
39
- it('surfaces the registry error message and code', async () => {
40
- mockFetch(
41
- 409,
42
- JSON.stringify({ message: 'Version code 2 has already been used', code: 'versionCodeExists' }),
43
- );
44
- const error = await publishToPlaystore({
45
- registryApiUrl: 'https://registry-staging.limrun.dev',
46
- accessToken: 'google-token',
47
- packageName: 'com.example.app',
48
- assetName: 'app-release.aab',
49
- }).catch((caught: unknown) => caught);
50
- expect(error).toBeInstanceOf(PlaystorePublishError);
51
- const publishError = error as PlaystorePublishError;
52
- expect(publishError.code).toBe('versionCodeExists');
53
- expect(publishError.status).toBe(409);
54
- expect(publishError.message).toContain('already been used');
55
- });
56
-
57
- it('handles non-JSON error bodies', async () => {
58
- mockFetch(502, '<html>bad gateway</html>');
59
- const error = await publishToPlaystore({
60
- registryApiUrl: 'https://registry-staging.limrun.dev',
61
- accessToken: 'google-token',
62
- packageName: 'com.example.app',
63
- assetId: 'asset_1',
64
- }).catch((caught: unknown) => caught);
65
- expect(error).toBeInstanceOf(PlaystorePublishError);
66
- expect((error as PlaystorePublishError).message).toContain('HTTP 502');
67
- });
68
- });
@@ -1,89 +0,0 @@
1
- export type PlaystorePublishInput = {
2
- registryApiUrl: string;
3
- /** Limrun API token authenticating the registry call. */
4
- token?: string;
5
- /** Organization tid; required when it differs from the token's default organization. */
6
- organizationId?: string;
7
- /** Google OAuth access token with the androidpublisher scope. Sent per request, never stored. */
8
- accessToken: string;
9
- packageName: string;
10
- assetId?: string;
11
- assetName?: string;
12
- /** Play track ID, defaults to internal on the server. */
13
- track?: string;
14
- /** Required by the server when track is production. */
15
- releaseStatus?: 'draft' | 'completed';
16
- };
17
-
18
- export type PlaystorePublishResult = {
19
- versionCode: number;
20
- };
21
-
22
- /**
23
- * Registry publish failure. `code` carries the registry's machine-readable
24
- * error code when available: invalidRequest, assetNotFound, internal, busy,
25
- * listingNotFound, permissionDenied, versionCodeExists, uploadKeyMismatch
26
- * or unknown; the set grows additively.
27
- */
28
- export class PlaystorePublishError extends Error {
29
- readonly code?: string;
30
- readonly status?: number;
31
-
32
- constructor(message: string, options: { code?: string; status?: number } = {}) {
33
- super(message);
34
- this.name = 'PlaystorePublishError';
35
- this.code = options.code;
36
- this.status = options.status;
37
- }
38
- }
39
-
40
- export async function publishToPlaystore(input: PlaystorePublishInput): Promise<PlaystorePublishResult> {
41
- const { registryApiUrl, token, organizationId, ...body } = input;
42
- let url: URL;
43
- try {
44
- url = new URL(registryApiUrl);
45
- } catch {
46
- throw new PlaystorePublishError(`Invalid registry URL: ${registryApiUrl}`);
47
- }
48
- url.pathname = `${url.pathname.replace(/\/$/, '')}/android/playstore/publish`;
49
- url.search = '';
50
- url.hash = '';
51
- const headers: Record<string, string> = { 'Content-Type': 'application/json' };
52
- if (token) {
53
- headers.Authorization = `Bearer ${token}`;
54
- }
55
- if (organizationId) {
56
- headers['X-Limrun-Organization'] = organizationId;
57
- }
58
- let response: Response;
59
- try {
60
- response = await fetch(url.toString(), {
61
- method: 'POST',
62
- headers,
63
- body: JSON.stringify(body),
64
- });
65
- } catch (caught) {
66
- throw new PlaystorePublishError(
67
- `Cannot reach the registry: ${caught instanceof Error ? caught.message : String(caught)}`,
68
- );
69
- }
70
- const raw = await response.text();
71
- let parsed: { versionCode?: number; message?: string; code?: string } | undefined;
72
- try {
73
- parsed = raw ? (JSON.parse(raw) as typeof parsed) : undefined;
74
- } catch {
75
- parsed = undefined;
76
- }
77
- if (!response.ok) {
78
- throw new PlaystorePublishError(parsed?.message || `Play publish failed with HTTP ${response.status}`, {
79
- code: parsed?.code,
80
- status: response.status,
81
- });
82
- }
83
- if (typeof parsed?.versionCode !== 'number') {
84
- throw new PlaystorePublishError('Play publish response is missing versionCode.', {
85
- status: response.status,
86
- });
87
- }
88
- return { versionCode: parsed.versionCode };
89
- }
@@ -1,42 +0,0 @@
1
- export {
2
- fetchLimbuildInfo,
3
- getIOSOTAInstall,
4
- startSignedDeviceBuild,
5
- watchBuildLogEvents,
6
- type BuildLogEventsOptions,
7
- type GetIOSOTAInstallOptions,
8
- type IOSOTAInstall,
9
- type LimbuildInfo,
10
- type StartSignedDeviceBuildOptions,
11
- } from '../core/device-install/operations/limbuild-client';
12
- export {
13
- getLatestSigningAssets,
14
- getLatestSigningAssetsWithCertificate,
15
- getReusableAppleSigningAssets,
16
- getSigningAssets,
17
- parseProvisioningProfile,
18
- parseProvisioningProfileBase64,
19
- parseProvisioningProfileBytes,
20
- profileContainsDevice,
21
- profileMatchesBundleID,
22
- putAppleGeneratedSigningAssets,
23
- putSigningAssets,
24
- type AppleSigningAssetCacheInput,
25
- type PutAppleGeneratedSigningAssetsInput,
26
- } from '../core/device-install';
27
- export {
28
- fileToBase64,
29
- importSigningAssetsFromFiles,
30
- validateProvisioningProfileForInstall,
31
- validateSigningAssetsForInstall,
32
- type ImportSigningAssetsFromFilesInput,
33
- type ValidateSigningAssetsOptions,
34
- } from './signing';
35
- export type {
36
- BuildLogLine,
37
- DeviceInstallBuildStatus,
38
- DeviceInstallSigningMode,
39
- ProvisioningProfileInfo,
40
- PutSigningAssetsInput,
41
- StoredSigningAssets,
42
- } from '../core/device-install/types';
@@ -1,130 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
- import {
3
- getIOSOTAInstall,
4
- startSignedDeviceBuild,
5
- watchBuildLogEvents,
6
- type BuildLogLine,
7
- type DeviceInstallBuildStatus,
8
- type IOSOTAInstall,
9
- type StoredSigningAssets,
10
- } from './index';
11
- import { errorMessage } from '../core/errors';
12
-
13
- export type UseDeviceBuildOptions = {
14
- apiUrl?: string;
15
- token?: string;
16
- signingAssets?: StoredSigningAssets;
17
- loadOTAInstall?: boolean;
18
- };
19
-
20
- export type StartDeviceBuildInput = {
21
- signingAssets?: StoredSigningAssets;
22
- signedUploadUrl?: string;
23
- };
24
-
25
- export type UseDeviceBuildResult = {
26
- status: DeviceInstallBuildStatus;
27
- logs: BuildLogLine[];
28
- execId?: string;
29
- otaInstall?: IOSOTAInstall;
30
- error?: string;
31
- startBuild: (input?: StartDeviceBuildInput) => Promise<string | undefined>;
32
- reset: () => void;
33
- };
34
-
35
- export function useDeviceBuild({
36
- apiUrl,
37
- token,
38
- signingAssets,
39
- loadOTAInstall = false,
40
- }: UseDeviceBuildOptions): UseDeviceBuildResult {
41
- const [status, setStatus] = useState<DeviceInstallBuildStatus>('idle');
42
- const [logs, setLogs] = useState<BuildLogLine[]>([]);
43
- const [execId, setExecId] = useState<string | undefined>();
44
- const [otaInstall, setOTAInstall] = useState<IOSOTAInstall | undefined>();
45
- const [error, setError] = useState<string | undefined>();
46
- const stopWatcherRef = useRef<(() => void) | undefined>(undefined);
47
-
48
- useEffect(() => {
49
- return () => {
50
- stopWatcherRef.current?.();
51
- stopWatcherRef.current = undefined;
52
- };
53
- }, []);
54
-
55
- const reset = useCallback(() => {
56
- stopWatcherRef.current?.();
57
- stopWatcherRef.current = undefined;
58
- setStatus('idle');
59
- setLogs([]);
60
- setExecId(undefined);
61
- setOTAInstall(undefined);
62
- setError(undefined);
63
- }, []);
64
-
65
- const startBuild = useCallback(
66
- async (input: StartDeviceBuildInput = {}) => {
67
- if (!apiUrl) {
68
- throw new Error('apiUrl is required to start a device build.');
69
- }
70
- const activeSigningAssets = input.signingAssets ?? signingAssets;
71
- if (!activeSigningAssets) {
72
- throw new Error('Signing assets are required to start a device build.');
73
- }
74
- stopWatcherRef.current?.();
75
- setStatus('queued');
76
- setLogs([]);
77
- setExecId(undefined);
78
- setOTAInstall(undefined);
79
- setError(undefined);
80
- try {
81
- const result = await startSignedDeviceBuild({
82
- limbuildApiUrl: apiUrl,
83
- token,
84
- certificateP12Base64: activeSigningAssets.certificateP12Base64,
85
- certificatePassword: activeSigningAssets.certificatePassword,
86
- provisioningProfileBase64: activeSigningAssets.provisioningProfileBase64,
87
- signedUploadUrl: input.signedUploadUrl,
88
- });
89
- if (!result.execId) {
90
- throw new Error('Build request did not return an exec ID.');
91
- }
92
- setExecId(result.execId);
93
- stopWatcherRef.current = watchBuildLogEvents({
94
- limbuildApiUrl: apiUrl,
95
- token,
96
- execId: result.execId,
97
- onLine: (line) => setLogs((current) => [...current, line]),
98
- onStatus: (nextStatus) => {
99
- setStatus(nextStatus);
100
- if (nextStatus === 'succeeded' && loadOTAInstall) {
101
- void getIOSOTAInstall({ limbuildApiUrl: apiUrl, token, execId: result.execId! })
102
- .then(setOTAInstall)
103
- .catch((caught) => setError(errorMessage(caught)));
104
- }
105
- },
106
- onError: (caught) => {
107
- setStatus('failed');
108
- setError(caught.message);
109
- },
110
- });
111
- return result.execId;
112
- } catch (caught) {
113
- setStatus('failed');
114
- setError(errorMessage(caught));
115
- return undefined;
116
- }
117
- },
118
- [apiUrl, loadOTAInstall, signingAssets, token],
119
- );
120
-
121
- return {
122
- status,
123
- logs,
124
- execId,
125
- otaInstall,
126
- error,
127
- startBuild,
128
- reset,
129
- };
130
- }
@@ -1,94 +0,0 @@
1
- import {
2
- parseProvisioningProfile,
3
- profileContainsDevice,
4
- profileMatchesBundleID,
5
- putSigningAssets,
6
- } from '../core/device-install/storage';
7
- import type { DeviceInstallSigningMode, StoredSigningAssets } from '../core/device-install/types';
8
-
9
- export type ImportSigningAssetsFromFilesInput = {
10
- certificateFile: File;
11
- provisioningProfileFile: File;
12
- certificatePassword?: string;
13
- bundleId?: string;
14
- deviceUDID?: string;
15
- teamId?: string;
16
- signingMode?: DeviceInstallSigningMode;
17
- certificateId?: string;
18
- };
19
-
20
- export type ValidateSigningAssetsOptions = {
21
- bundleId?: string;
22
- deviceUDID?: string;
23
- signingMode?: DeviceInstallSigningMode;
24
- };
25
-
26
- export async function importSigningAssetsFromFiles({
27
- certificateFile,
28
- provisioningProfileFile,
29
- certificatePassword,
30
- bundleId,
31
- deviceUDID,
32
- teamId,
33
- signingMode = 'development',
34
- certificateId,
35
- }: ImportSigningAssetsFromFilesInput) {
36
- const [certificateP12Base64, provisioningProfileBase64, profile] = await Promise.all([
37
- fileToBase64(certificateFile),
38
- fileToBase64(provisioningProfileFile),
39
- parseProvisioningProfile(provisioningProfileFile),
40
- ]);
41
- validateProvisioningProfileForInstall({ profile, bundleId, deviceUDID, signingMode });
42
- return putSigningAssets({
43
- bundleID: bundleId ?? profile.bundleID ?? profile.applicationIdentifier ?? provisioningProfileFile.name,
44
- deviceUDID,
45
- teamID: teamId ?? profile.teamID,
46
- signingMode,
47
- certificateID: certificateId,
48
- certificateP12Base64,
49
- certificateFileName: certificateFile.name,
50
- certificatePassword: certificatePassword || undefined,
51
- provisioningProfileBase64,
52
- profileFileName: provisioningProfileFile.name,
53
- profile,
54
- });
55
- }
56
-
57
- export function validateSigningAssetsForInstall(
58
- assets: StoredSigningAssets,
59
- options: ValidateSigningAssetsOptions = {},
60
- ) {
61
- validateProvisioningProfileForInstall({
62
- profile: assets.profile,
63
- bundleId: options.bundleId,
64
- deviceUDID: options.deviceUDID,
65
- signingMode: options.signingMode ?? assets.signingMode,
66
- });
67
- }
68
-
69
- export function validateProvisioningProfileForInstall({
70
- profile,
71
- bundleId,
72
- deviceUDID,
73
- signingMode,
74
- }: Pick<StoredSigningAssets, 'profile'> & ValidateSigningAssetsOptions) {
75
- if (bundleId && !profileMatchesBundleID(profile, bundleId)) {
76
- throw new Error(`Provisioning profile does not match bundle ID ${bundleId}.`);
77
- }
78
- if (deviceUDID && !profileContainsDevice(profile, deviceUDID)) {
79
- throw new Error('Provisioning profile does not include the selected iPhone.');
80
- }
81
- if (signingMode === 'adhoc' && profile.getTaskAllow) {
82
- throw new Error('Ad Hoc mode requires an Ad Hoc provisioning profile, not a development profile.');
83
- }
84
- }
85
-
86
- export async function fileToBase64(file: File) {
87
- const buffer = await file.arrayBuffer();
88
- let binary = '';
89
- const bytes = new Uint8Array(buffer);
90
- for (const byte of bytes) {
91
- binary += String.fromCharCode(byte);
92
- }
93
- return btoa(binary);
94
- }