@limrun/ui 0.9.0-rc.2 → 0.9.0-rc.5

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 (78) hide show
  1. package/README.md +0 -9
  2. package/dist/components/inspect-overlay.d.ts +33 -0
  3. package/dist/components/remote-control.d.ts +86 -0
  4. package/dist/core/ax-fetcher.d.ts +49 -0
  5. package/dist/core/ax-tree.d.ts +99 -0
  6. package/dist/index.cjs +1 -1
  7. package/dist/index.css +1 -1
  8. package/dist/index.d.ts +3 -2
  9. package/dist/index.js +1491 -777
  10. package/package.json +8 -17
  11. package/src/components/inspect-overlay.css +223 -0
  12. package/src/components/inspect-overlay.tsx +437 -0
  13. package/src/components/remote-control.tsx +547 -9
  14. package/src/core/ax-fetcher.test.ts +418 -0
  15. package/src/core/ax-fetcher.ts +377 -0
  16. package/src/core/ax-tree.test.ts +491 -0
  17. package/src/core/ax-tree.ts +416 -0
  18. package/src/demo.tsx +93 -10
  19. package/src/index.ts +17 -2
  20. package/vite.config.ts +2 -6
  21. package/vitest.config.ts +23 -0
  22. package/dist/components/device-install/device-install-dialog.d.ts +0 -5
  23. package/dist/components/device-install/index.d.ts +0 -2
  24. package/dist/core/device-install/apple/client.d.ts +0 -17
  25. package/dist/core/device-install/apple/crypto.d.ts +0 -20
  26. package/dist/core/device-install/apple/gsa-srp.d.ts +0 -26
  27. package/dist/core/device-install/apple/index.d.ts +0 -5
  28. package/dist/core/device-install/apple/provisioning.d.ts +0 -161
  29. package/dist/core/device-install/apple/relay.d.ts +0 -29
  30. package/dist/core/device-install/index.d.ts +0 -4
  31. package/dist/core/device-install/operations/index.d.ts +0 -6
  32. package/dist/core/device-install/operations/limbuild-client.d.ts +0 -28
  33. package/dist/core/device-install/operations/operations.d.ts +0 -32
  34. package/dist/core/device-install/operations/relay-client.d.ts +0 -25
  35. package/dist/core/device-install/operations/relay-protocol.d.ts +0 -27
  36. package/dist/core/device-install/operations/usbmux.d.ts +0 -32
  37. package/dist/core/device-install/operations/webusb.d.ts +0 -21
  38. package/dist/core/device-install/storage/browser-storage.d.ts +0 -43
  39. package/dist/core/device-install/storage/index.d.ts +0 -1
  40. package/dist/core/device-install/types.d.ts +0 -48
  41. package/dist/device-install/index.cjs +0 -1
  42. package/dist/device-install/index.d.ts +0 -3
  43. package/dist/device-install/index.js +0 -77
  44. package/dist/device-install/react.cjs +0 -1
  45. package/dist/device-install/react.d.ts +0 -1
  46. package/dist/device-install/react.js +0 -4
  47. package/dist/device-install-dialog-CSwQgbBm.js +0 -2
  48. package/dist/device-install-dialog-nThj775b.mjs +0 -395
  49. package/dist/device-install-dialog.css +0 -1
  50. package/dist/hooks/index.d.ts +0 -1
  51. package/dist/hooks/use-device-install.d.ts +0 -70
  52. package/dist/use-device-install-C1uVac59.mjs +0 -13541
  53. package/dist/use-device-install-Ca4jcVKU.js +0 -31
  54. package/src/components/device-install/device-install-dialog.css +0 -244
  55. package/src/components/device-install/device-install-dialog.tsx +0 -432
  56. package/src/components/device-install/index.ts +0 -2
  57. package/src/core/device-install/apple/client.ts +0 -152
  58. package/src/core/device-install/apple/crypto.ts +0 -202
  59. package/src/core/device-install/apple/gsa-srp.ts +0 -127
  60. package/src/core/device-install/apple/index.ts +0 -5
  61. package/src/core/device-install/apple/provisioning.ts +0 -298
  62. package/src/core/device-install/apple/relay.ts +0 -221
  63. package/src/core/device-install/index.ts +0 -4
  64. package/src/core/device-install/operations/index.ts +0 -6
  65. package/src/core/device-install/operations/limbuild-client.ts +0 -104
  66. package/src/core/device-install/operations/operations.ts +0 -217
  67. package/src/core/device-install/operations/relay-client.ts +0 -255
  68. package/src/core/device-install/operations/relay-protocol.ts +0 -71
  69. package/src/core/device-install/operations/usbmux.ts +0 -270
  70. package/src/core/device-install/operations/webusb-dom.d.ts +0 -54
  71. package/src/core/device-install/operations/webusb.ts +0 -105
  72. package/src/core/device-install/storage/browser-storage.ts +0 -251
  73. package/src/core/device-install/storage/index.ts +0 -1
  74. package/src/core/device-install/types.ts +0 -65
  75. package/src/device-install/index.ts +0 -3
  76. package/src/device-install/react.ts +0 -1
  77. package/src/hooks/index.ts +0 -1
  78. package/src/hooks/use-device-install.ts +0 -1067
@@ -1,298 +0,0 @@
1
- import type { ProvisioningProfileInfo, StoredSigningAssets } from '../types';
2
- import {
3
- getSigningAssets,
4
- normalizeBundleID,
5
- normalizeUDID,
6
- profileContainsDevice,
7
- profileMatchesBundleID,
8
- putSigningAssets,
9
- } from '../storage';
10
- import type { AppleProvisioningRequest } from './relay';
11
-
12
- export type AppleDeveloperPortalTeam = {
13
- name?: string;
14
- teamId?: string;
15
- providerId?: string | number;
16
- publicProviderId?: string;
17
- type?: string;
18
- subType?: string;
19
- };
20
-
21
- export type AppleDeveloperPortalDevice = {
22
- deviceId?: string;
23
- name?: string;
24
- deviceNumber?: string;
25
- deviceClass?: string;
26
- model?: string;
27
- status?: string;
28
- };
29
-
30
- export type AppleDeveloperPortalAppID = {
31
- appId?: string;
32
- appIdId?: string;
33
- identifier?: string;
34
- bundleId?: string;
35
- name?: string;
36
- prefix?: string;
37
- platform?: string;
38
- };
39
-
40
- export type AppleDeveloperPortalResponse = {
41
- resultCode?: number;
42
- resultString?: string;
43
- userString?: string;
44
- teams?: AppleDeveloperPortalTeam[];
45
- provider?: AppleDeveloperPortalTeam;
46
- availableProviders?: AppleDeveloperPortalTeam[];
47
- appIds?: AppleDeveloperPortalAppID[];
48
- devices?: AppleDeveloperPortalDevice[];
49
- certRequests?: Array<Record<string, unknown>>;
50
- certRequest?: Record<string, unknown>;
51
- appId?: Record<string, unknown>;
52
- device?: Record<string, unknown>;
53
- provisioningProfile?: Record<string, unknown>;
54
- provisioningProfiles?: Array<Record<string, unknown>>;
55
- };
56
-
57
- export type AppleProvisioningContext = {
58
- bundleID: string;
59
- deviceUDID: string;
60
- teamID?: string;
61
- };
62
-
63
- export type AppleSigningAssetCacheInput = {
64
- bundleID: string;
65
- deviceUDID?: string;
66
- teamID?: string;
67
- };
68
-
69
- export type PutAppleGeneratedSigningAssetsInput = {
70
- bundleID: string;
71
- deviceUDID?: string;
72
- teamID?: string;
73
- certificateID?: string;
74
- certificateP12Base64: string;
75
- certificatePassword: string;
76
- provisioningProfileBase64: string;
77
- profile: ProvisioningProfileInfo;
78
- };
79
-
80
- export function listTeamsRequest(): AppleProvisioningRequest {
81
- return {
82
- method: 'POST',
83
- path: '/account/listTeams.action',
84
- payload: {},
85
- };
86
- }
87
-
88
- export function findBundleIDRequest({ bundleID, teamID = '' }: Pick<AppleProvisioningContext, 'bundleID' | 'teamID'>) {
89
- void bundleID;
90
- return pagedRequest('/account/ios/identifiers/listAppIds.action', teamID, { sort: 'name=asc' });
91
- }
92
-
93
- export function findDeviceRequest({ deviceUDID, teamID = '' }: Pick<AppleProvisioningContext, 'deviceUDID' | 'teamID'>) {
94
- void deviceUDID;
95
- return pagedRequest('/account/ios/device/listDevices.action', teamID, {
96
- sort: 'name=asc',
97
- includeRemovedDevices: false,
98
- });
99
- }
100
-
101
- export function findDevelopmentCertificatesRequest(teamID = '') {
102
- return pagedRequest('/account/ios/certificate/listCertRequests.action', teamID, {
103
- sort: 'name=asc',
104
- types: '83Q87W3TGH,5QPB9NHCEI',
105
- });
106
- }
107
-
108
- export function findDevelopmentProfilesRequest({
109
- bundleID,
110
- teamID = '',
111
- }: Pick<AppleProvisioningContext, 'bundleID' | 'teamID'>) {
112
- return pagedRequest('/account/ios/profile/listProvisioningProfiles.action', teamID, {
113
- search: bundleID,
114
- sort: 'name=asc',
115
- });
116
- }
117
-
118
- export function registerDeviceRequest({
119
- deviceUDID,
120
- teamID = '',
121
- name = 'Limrun iPhone',
122
- }: Pick<AppleProvisioningContext, 'deviceUDID' | 'teamID'> & { name?: string }) {
123
- return {
124
- method: 'POST',
125
- path: '/account/ios/device/addDevices.action',
126
- payload: {
127
- teamId: teamID,
128
- deviceNames: name,
129
- deviceNumbers: normalizeUDID(deviceUDID),
130
- deviceClasses: 'iphone',
131
- register: 'single',
132
- },
133
- } satisfies AppleProvisioningRequest;
134
- }
135
-
136
- export function createBundleIDRequest({
137
- bundleID,
138
- teamID = '',
139
- name,
140
- }: Pick<AppleProvisioningContext, 'bundleID' | 'teamID'> & { name?: string }) {
141
- return {
142
- method: 'POST',
143
- path: '/account/ios/identifiers/addAppId.action',
144
- payload: {
145
- teamId: teamID,
146
- name: name ?? bundleID,
147
- identifier: bundleID,
148
- type: 'explicit',
149
- },
150
- } satisfies AppleProvisioningRequest;
151
- }
152
-
153
- export function submitDevelopmentCSRRequest({
154
- csrPEM,
155
- teamID = '',
156
- }: {
157
- csrPEM: string;
158
- teamID?: string;
159
- }) {
160
- return {
161
- method: 'POST',
162
- path: '/account/ios/certificate/submitCertificateRequest.action',
163
- payload: {
164
- teamId: teamID,
165
- type: '83Q87W3TGH',
166
- csrContent: csrPEM,
167
- },
168
- } satisfies AppleProvisioningRequest;
169
- }
170
-
171
- export function downloadCertificateRequest(certificateID: string, teamID = '') {
172
- return {
173
- method: 'GET',
174
- path: '/account/ios/certificate/downloadCertificateContent.action',
175
- payload: {
176
- teamId: teamID,
177
- certificateId: certificateID,
178
- type: '83Q87W3TGH',
179
- },
180
- } satisfies AppleProvisioningRequest;
181
- }
182
-
183
- export function createDevelopmentProfileRequest({
184
- bundleID,
185
- teamID = '',
186
- appIDID,
187
- certificateID,
188
- deviceIDs,
189
- name,
190
- }: Pick<AppleProvisioningContext, 'bundleID' | 'teamID'> & {
191
- appIDID: string;
192
- certificateID: string;
193
- deviceIDs: string[];
194
- name?: string;
195
- }) {
196
- return {
197
- method: 'POST',
198
- path: '/account/ios/profile/createProvisioningProfile.action',
199
- payload: {
200
- teamId: teamID,
201
- provisioningProfileName: name ?? `Limrun ${bundleID}`,
202
- certificateIds: [certificateID],
203
- appIdId: appIDID,
204
- deviceIds: deviceIDs,
205
- distributionType: 'limited',
206
- subPlatform: 'ios',
207
- },
208
- } satisfies AppleProvisioningRequest;
209
- }
210
-
211
- export function downloadProfileRequest(profileID: string, teamID = '') {
212
- return {
213
- method: 'GET',
214
- path: '/account/ios/profile/downloadProfileContent',
215
- payload: {
216
- teamId: teamID,
217
- provisioningProfileId: profileID,
218
- },
219
- } satisfies AppleProvisioningRequest;
220
- }
221
-
222
- export async function getReusableAppleSigningAssets({
223
- bundleID,
224
- deviceUDID,
225
- teamID,
226
- }: AppleSigningAssetCacheInput) {
227
- const stored = await getSigningAssets({ bundleID, deviceUDID });
228
- if (!stored || !storedSigningAssetsReusable(stored, { bundleID, deviceUDID, teamID })) {
229
- return undefined;
230
- }
231
- return stored;
232
- }
233
-
234
- export async function putAppleGeneratedSigningAssets(input: PutAppleGeneratedSigningAssetsInput) {
235
- return putSigningAssets({
236
- ...input,
237
- certificateFileName: input.certificateID ? `${input.certificateID}.p12` : 'apple-development.p12',
238
- profileFileName: input.profile.uuid ? `${input.profile.uuid}.mobileprovision` : 'apple-development.mobileprovision',
239
- });
240
- }
241
-
242
- export function storedSigningAssetsReusable(
243
- stored: StoredSigningAssets,
244
- { bundleID, deviceUDID, teamID }: AppleSigningAssetCacheInput,
245
- ) {
246
- if (!profileMatchesBundleID(stored.profile, bundleID)) {
247
- return false;
248
- }
249
- if (teamID && stored.teamID && stored.teamID !== teamID) {
250
- return false;
251
- }
252
- if (deviceUDID && !profileContainsDevice(stored.profile, deviceUDID)) {
253
- return false;
254
- }
255
- if (stored.profile.expirationDate && new Date(stored.profile.expirationDate).getTime() <= Date.now()) {
256
- return false;
257
- }
258
- return normalizeBundleID(stored.bundleID) === normalizeBundleID(bundleID);
259
- }
260
-
261
- export function selectDeveloperPortalTeam(body: unknown): AppleDeveloperPortalTeam | undefined {
262
- const response = body as AppleDeveloperPortalResponse | undefined;
263
- return response?.teams?.[0] ?? response?.provider ?? response?.availableProviders?.[0];
264
- }
265
-
266
- export function teamIDCandidates(body: unknown): string[] {
267
- const response = body as AppleDeveloperPortalResponse | undefined;
268
- const teams = [
269
- ...(response?.teams ?? []),
270
- ...(response?.provider ? [response.provider] : []),
271
- ...(response?.availableProviders ?? []),
272
- ];
273
- const ids = new Set<string>();
274
- for (const team of teams) {
275
- for (const value of [team.teamId, team.providerId, team.publicProviderId]) {
276
- if (value !== undefined && value !== '') {
277
- ids.add(String(value));
278
- }
279
- }
280
- }
281
- return [...ids];
282
- }
283
-
284
- function pagedRequest(path: string, teamID: string, payload: Record<string, unknown> = {}) {
285
- const basePayload: Record<string, unknown> = {
286
- pageNumber: 1,
287
- pageSize: 200,
288
- ...payload,
289
- };
290
- if (teamID) {
291
- basePayload.teamId = teamID;
292
- }
293
- return {
294
- method: 'POST',
295
- path,
296
- payload: basePayload,
297
- } satisfies AppleProvisioningRequest;
298
- }
@@ -1,221 +0,0 @@
1
- import type { AppleSRPCompleteProof, AppleSRPInitRequest, AppleSRPInitResponse } from './gsa-srp';
2
-
3
- export type AppleRelayResponse<T = unknown> = {
4
- status: number;
5
- statusText: string;
6
- headers?: Record<string, string>;
7
- body?: T;
8
- rawBody?: string;
9
- rawBodyBase64?: string;
10
- };
11
-
12
- export type AppleProvisioningRequest = {
13
- method?: 'GET' | 'POST';
14
- path: string;
15
- payload?: unknown;
16
- };
17
-
18
- export async function createAppleRelaySession(limbuildApiUrl: string, token?: string) {
19
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/session', token), {
20
- method: 'POST',
21
- headers: authHeaders(token),
22
- });
23
- if (!response.ok) {
24
- throw new Error(`Apple relay session failed: HTTP ${response.status} ${await response.text()}`);
25
- }
26
- return (await response.json()) as { appleSessionId: string };
27
- }
28
-
29
- export async function deleteAppleRelaySession(limbuildApiUrl: string, appleSessionId: string, token?: string) {
30
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/session/delete', token), {
31
- method: 'POST',
32
- headers: jsonHeaders(token),
33
- body: JSON.stringify({ appleSessionId }),
34
- });
35
- if (!response.ok) {
36
- throw new Error(`Apple relay session delete failed: HTTP ${response.status} ${await response.text()}`);
37
- }
38
- }
39
-
40
- export async function proxySrpInit(
41
- limbuildApiUrl: string,
42
- appleSessionId: string,
43
- payload: AppleSRPInitRequest,
44
- token?: string,
45
- ) {
46
- return postAppleProxy<AppleSRPInitResponse>(
47
- limbuildApiUrl,
48
- '/apple/auth/srp/init',
49
- appleSessionId,
50
- payload,
51
- token,
52
- );
53
- }
54
-
55
- export async function proxySrpComplete(
56
- limbuildApiUrl: string,
57
- appleSessionId: string,
58
- payload: AppleSRPCompleteProof & {
59
- rememberMe: boolean;
60
- trustTokens: string[];
61
- },
62
- token?: string,
63
- ) {
64
- return postAppleProxy(limbuildApiUrl, '/apple/auth/srp/complete', appleSessionId, payload, token);
65
- }
66
-
67
- export async function triggerTrustedDeviceTwoFactor(
68
- limbuildApiUrl: string,
69
- appleSessionId: string,
70
- token?: string,
71
- ) {
72
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/2fa/trigger', token), {
73
- method: 'POST',
74
- headers: jsonHeaders(token),
75
- body: JSON.stringify({ appleSessionId }),
76
- });
77
- if (!response.ok) {
78
- throw new Error(`Apple 2FA trigger failed: HTTP ${response.status} ${await response.text()}`);
79
- }
80
- return (await response.json()) as AppleRelayResponse;
81
- }
82
-
83
- export async function triggerPhoneTwoFactor(
84
- limbuildApiUrl: string,
85
- appleSessionId: string,
86
- phoneNumberId: number,
87
- mode = 'sms',
88
- token?: string,
89
- ) {
90
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/2fa/phone/trigger', token), {
91
- method: 'POST',
92
- headers: jsonHeaders(token),
93
- body: JSON.stringify({ appleSessionId, phoneNumberId, mode }),
94
- });
95
- if (!response.ok) {
96
- throw new Error(`Apple phone 2FA trigger failed: HTTP ${response.status} ${await response.text()}`);
97
- }
98
- return (await response.json()) as AppleRelayResponse;
99
- }
100
-
101
- export async function proxyTwoFactorCode(
102
- limbuildApiUrl: string,
103
- appleSessionId: string,
104
- code: string,
105
- token?: string,
106
- ) {
107
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/2fa', token), {
108
- method: 'POST',
109
- headers: jsonHeaders(token),
110
- body: JSON.stringify({ appleSessionId, code }),
111
- });
112
- if (!response.ok) {
113
- throw new Error(`Apple 2FA proxy failed: HTTP ${response.status} ${await response.text()}`);
114
- }
115
- return (await response.json()) as AppleRelayResponse;
116
- }
117
-
118
- export async function proxyPhoneTwoFactorCode(
119
- limbuildApiUrl: string,
120
- appleSessionId: string,
121
- phoneNumberId: number,
122
- code: string,
123
- mode = 'sms',
124
- token?: string,
125
- ) {
126
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/2fa/phone', token), {
127
- method: 'POST',
128
- headers: jsonHeaders(token),
129
- body: JSON.stringify({ appleSessionId, phoneNumberId, mode, code }),
130
- });
131
- if (!response.ok) {
132
- throw new Error(`Apple phone 2FA proxy failed: HTTP ${response.status} ${await response.text()}`);
133
- }
134
- return (await response.json()) as AppleRelayResponse;
135
- }
136
-
137
- export async function fetchAppleAccountSession(
138
- limbuildApiUrl: string,
139
- appleSessionId: string,
140
- token?: string,
141
- ) {
142
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/finalize', token), {
143
- method: 'POST',
144
- headers: jsonHeaders(token),
145
- body: JSON.stringify({ appleSessionId }),
146
- });
147
- if (!response.ok) {
148
- throw new Error(`Apple session finalization failed: HTTP ${response.status} ${await response.text()}`);
149
- }
150
- return (await response.json()) as AppleRelayResponse;
151
- }
152
-
153
- export async function proxyProvisioningRequest<T = unknown>(
154
- limbuildApiUrl: string,
155
- appleSessionId: string,
156
- request: AppleProvisioningRequest,
157
- token?: string,
158
- ) {
159
- const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/provisioning', token), {
160
- method: 'POST',
161
- headers: jsonHeaders(token),
162
- body: JSON.stringify({ appleSessionId, ...request }),
163
- });
164
- if (!response.ok) {
165
- throw new Error(`Apple provisioning proxy failed: HTTP ${response.status} ${await response.text()}`);
166
- }
167
- return normalizeAppleProxyResponse<T>((await response.json()) as AppleRelayResponse<T>);
168
- }
169
-
170
- async function postAppleProxy<T>(
171
- limbuildApiUrl: string,
172
- path: string,
173
- appleSessionId: string,
174
- payload: unknown,
175
- token?: string,
176
- ) {
177
- const response = await fetch(limbuildURL(limbuildApiUrl, path, token), {
178
- method: 'POST',
179
- headers: jsonHeaders(token),
180
- body: JSON.stringify({ appleSessionId, payload }),
181
- });
182
- if (!response.ok) {
183
- throw new Error(`Apple proxy ${path} failed: HTTP ${response.status} ${await response.text()}`);
184
- }
185
- return normalizeAppleProxyResponse<T>((await response.json()) as AppleRelayResponse<T>);
186
- }
187
-
188
- function normalizeAppleProxyResponse<T>(response: AppleRelayResponse<T>) {
189
- if (response.body !== undefined || !response.rawBody) {
190
- return response;
191
- }
192
- try {
193
- return {
194
- ...response,
195
- body: JSON.parse(response.rawBody) as T,
196
- };
197
- } catch {
198
- return response;
199
- }
200
- }
201
-
202
- function limbuildURL(limbuildApiUrl: string, path: string, token?: string) {
203
- const base = limbuildApiUrl.replace(/\/$/, '');
204
- const suffix = path.startsWith('/') ? path : `/${path}`;
205
- const url = new URL(`${base}${suffix}`);
206
- if (token) {
207
- url.searchParams.set('token', token);
208
- }
209
- return url;
210
- }
211
-
212
- function jsonHeaders(token?: string): Record<string, string> {
213
- return {
214
- 'Content-Type': 'application/json',
215
- ...authHeaders(token),
216
- };
217
- }
218
-
219
- function authHeaders(token?: string): Record<string, string> {
220
- return token ? { Authorization: `Bearer ${token}` } : {};
221
- }
@@ -1,4 +0,0 @@
1
- export * from './types';
2
- export * from './apple';
3
- export * from './operations';
4
- export * from './storage';
@@ -1,6 +0,0 @@
1
- export * from './limbuild-client';
2
- export * from './operations';
3
- export * from './relay-client';
4
- export * from './relay-protocol';
5
- export * from './usbmux';
6
- export * from './webusb';
@@ -1,104 +0,0 @@
1
- import type { BuildLogLine, DeviceInstallBuildStatus } from '../types';
2
-
3
- export type LimbuildInfo = {
4
- homeDir?: string;
5
- lastBuildConfig?: {
6
- bundleId?: string;
7
- sdk?: string;
8
- };
9
- };
10
-
11
- export type StartSignedDeviceBuildOptions = {
12
- limbuildApiUrl: string;
13
- token?: string;
14
- certificateP12Base64: string;
15
- certificatePassword: string;
16
- provisioningProfileBase64: string;
17
- };
18
-
19
- export type BuildLogEventsOptions = {
20
- limbuildApiUrl: string;
21
- execId: string;
22
- token?: string;
23
- onLine: (line: BuildLogLine) => void;
24
- onStatus: (status: DeviceInstallBuildStatus) => void;
25
- onError?: (error: Error) => void;
26
- };
27
-
28
- export async function fetchLimbuildInfo(limbuildApiUrl: string, token?: string) {
29
- const url = new URL(`${limbuildApiUrl}/info`);
30
- if (token) {
31
- url.searchParams.set('token', token);
32
- }
33
- const response = await fetch(url.toString(), {
34
- headers: token ? { Authorization: `Bearer ${token}` } : undefined,
35
- });
36
- if (!response.ok) {
37
- const body = await response.text();
38
- throw new Error(`Info request failed: HTTP ${response.status} ${body}`);
39
- }
40
- return (await response.json()) as LimbuildInfo;
41
- }
42
-
43
- export async function startSignedDeviceBuild({
44
- limbuildApiUrl,
45
- token,
46
- certificateP12Base64,
47
- certificatePassword,
48
- provisioningProfileBase64,
49
- }: StartSignedDeviceBuildOptions) {
50
- const url = new URL(`${limbuildApiUrl}/exec`);
51
- if (token) {
52
- url.searchParams.set('token', token);
53
- }
54
- const response = await fetch(url.toString(), {
55
- method: 'POST',
56
- headers: {
57
- 'Content-Type': 'application/json',
58
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
59
- },
60
- body: JSON.stringify({
61
- command: 'xcodebuild',
62
- xcodebuild: { sdk: 'iphoneos' },
63
- signing: {
64
- certificateP12Base64,
65
- certificatePassword,
66
- provisioningProfileBase64,
67
- },
68
- }),
69
- });
70
- if (!response.ok) {
71
- const body = await response.text();
72
- throw new Error(`Build request failed: HTTP ${response.status} ${body}`);
73
- }
74
- return (await response.json()) as { execId?: string };
75
- }
76
-
77
- export function watchBuildLogEvents({
78
- limbuildApiUrl,
79
- execId,
80
- token,
81
- onLine,
82
- onStatus,
83
- onError,
84
- }: BuildLogEventsOptions) {
85
- const url = new URL(`${limbuildApiUrl}/exec/${execId}/events`);
86
- if (token) {
87
- url.searchParams.set('token', token);
88
- }
89
- const events = new EventSource(url.toString());
90
- onStatus('running');
91
- events.addEventListener('command', (event) => onLine({ type: 'command', data: event.data }));
92
- events.addEventListener('stdout', (event) => onLine({ type: 'stdout', data: event.data }));
93
- events.addEventListener('stderr', (event) => onLine({ type: 'stderr', data: event.data }));
94
- events.addEventListener('exitCode', (event) => {
95
- const code = parseInt(event.data, 10);
96
- onStatus(code === 0 ? 'succeeded' : code < 0 ? 'cancelled' : 'failed');
97
- events.close();
98
- });
99
- events.onerror = () => {
100
- events.close();
101
- onError?.(new Error('Build log stream closed before completion.'));
102
- };
103
- return () => events.close();
104
- }