@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,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'>;
|
package/src/core/errors.ts
DELETED
|
@@ -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
|
-
}
|