@limrun/ui 0.9.0-rc.1 → 0.9.0-rc.4
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/dist/core/device-install/apple/client.d.ts +1 -0
- package/dist/core/device-install/apple/provisioning.d.ts +42 -31
- package/dist/core/device-install/apple/relay.d.ts +5 -9
- package/dist/core/device-install/storage/browser-storage.d.ts +19 -0
- package/dist/core/device-install/types.d.ts +2 -2
- package/dist/device-install/index.cjs +1 -9
- package/dist/device-install/index.js +76 -210
- package/dist/device-install/react.cjs +1 -1
- package/dist/device-install/react.js +1 -1
- package/dist/device-install-dialog-86RDdoK9.js +2 -0
- package/dist/device-install-dialog-CnyDWf0q.mjs +462 -0
- package/dist/device-install-dialog.css +1 -1
- package/dist/hooks/use-device-install.d.ts +21 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -3
- package/dist/use-device-install-CbGVvwPp.js +31 -0
- package/dist/use-device-install-j1Gekpl4.mjs +13623 -0
- package/package.json +1 -1
- package/src/components/device-install/device-install-dialog.css +82 -1
- package/src/components/device-install/device-install-dialog.tsx +337 -187
- package/src/core/device-install/apple/client.ts +92 -4
- package/src/core/device-install/apple/provisioning.ts +67 -24
- package/src/core/device-install/apple/relay.ts +121 -205
- package/src/core/device-install/storage/browser-storage.ts +26 -1
- package/src/core/device-install/types.ts +2 -2
- package/src/hooks/use-device-install.ts +748 -60
- package/dist/device-install-dialog-CTwVViYY.js +0 -2
- package/dist/device-install-dialog-zzKJu7SM.mjs +0 -328
- package/dist/use-device-install-CgrOKKyi.mjs +0 -13042
- package/dist/use-device-install-DDKRf6IL.js +0 -23
|
@@ -6,14 +6,7 @@ export type AppleRelayResponse<T = unknown> = {
|
|
|
6
6
|
headers?: Record<string, string>;
|
|
7
7
|
body?: T;
|
|
8
8
|
rawBody?: string;
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type AppleRelayRequest = {
|
|
13
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
14
|
-
url: string;
|
|
15
|
-
headers?: Record<string, string>;
|
|
16
|
-
body?: BodyInit;
|
|
9
|
+
rawBodyBase64?: string;
|
|
17
10
|
};
|
|
18
11
|
|
|
19
12
|
export type AppleProvisioningRequest = {
|
|
@@ -33,11 +26,7 @@ export async function createAppleRelaySession(limbuildApiUrl: string, token?: st
|
|
|
33
26
|
return (await response.json()) as { appleSessionId: string };
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
export async function deleteAppleRelaySession(
|
|
37
|
-
limbuildApiUrl: string,
|
|
38
|
-
appleSessionId: string,
|
|
39
|
-
token?: string,
|
|
40
|
-
) {
|
|
29
|
+
export async function deleteAppleRelaySession(limbuildApiUrl: string, appleSessionId: string, token?: string) {
|
|
41
30
|
const response = await fetch(limbuildURL(limbuildApiUrl, '/apple/auth/session/delete', token), {
|
|
42
31
|
method: 'POST',
|
|
43
32
|
headers: jsonHeaders(token),
|
|
@@ -48,38 +37,17 @@ export async function deleteAppleRelaySession(
|
|
|
48
37
|
}
|
|
49
38
|
}
|
|
50
39
|
|
|
51
|
-
export async function relayAppleRequest<T = unknown>(
|
|
52
|
-
limbuildApiUrl: string,
|
|
53
|
-
appleSessionId: string,
|
|
54
|
-
request: AppleRelayRequest,
|
|
55
|
-
token?: string,
|
|
56
|
-
) {
|
|
57
|
-
const response = await fetch(appleRelayURL(limbuildApiUrl, appleSessionId, request.url, token), {
|
|
58
|
-
method: request.method ?? 'GET',
|
|
59
|
-
headers: {
|
|
60
|
-
...(request.headers ?? {}),
|
|
61
|
-
...authHeaders(token),
|
|
62
|
-
},
|
|
63
|
-
body: request.body,
|
|
64
|
-
});
|
|
65
|
-
return responseToAppleRelayResponse<T>(response);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
40
|
export async function proxySrpInit(
|
|
69
41
|
limbuildApiUrl: string,
|
|
70
42
|
appleSessionId: string,
|
|
71
43
|
payload: AppleSRPInitRequest,
|
|
72
44
|
token?: string,
|
|
73
45
|
) {
|
|
74
|
-
return
|
|
46
|
+
return postAppleProxy<AppleSRPInitResponse>(
|
|
75
47
|
limbuildApiUrl,
|
|
48
|
+
'/apple/auth/srp/init',
|
|
76
49
|
appleSessionId,
|
|
77
|
-
|
|
78
|
-
method: 'POST',
|
|
79
|
-
url: 'https://idmsa.apple.com/appleauth/auth/signin/init',
|
|
80
|
-
headers: jsonContentHeaders(),
|
|
81
|
-
body: JSON.stringify(payload),
|
|
82
|
-
},
|
|
50
|
+
payload,
|
|
83
51
|
token,
|
|
84
52
|
);
|
|
85
53
|
}
|
|
@@ -93,213 +61,161 @@ export async function proxySrpComplete(
|
|
|
93
61
|
},
|
|
94
62
|
token?: string,
|
|
95
63
|
) {
|
|
96
|
-
|
|
97
|
-
return relayAppleRequest(
|
|
98
|
-
limbuildApiUrl,
|
|
99
|
-
appleSessionId,
|
|
100
|
-
{
|
|
101
|
-
method: 'POST',
|
|
102
|
-
url: 'https://idmsa.apple.com/appleauth/auth/signin/complete?isRememberMeEnabled=false',
|
|
103
|
-
headers: {
|
|
104
|
-
...jsonContentHeaders(),
|
|
105
|
-
...(hashcash ? { 'X-Apple-HC': hashcash } : {}),
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify(payload),
|
|
108
|
-
},
|
|
109
|
-
token,
|
|
110
|
-
);
|
|
64
|
+
return postAppleProxy(limbuildApiUrl, '/apple/auth/srp/complete', appleSessionId, payload, token);
|
|
111
65
|
}
|
|
112
66
|
|
|
113
|
-
export async function
|
|
67
|
+
export async function triggerTrustedDeviceTwoFactor(
|
|
114
68
|
limbuildApiUrl: string,
|
|
115
69
|
appleSessionId: string,
|
|
116
|
-
code: string,
|
|
117
70
|
token?: string,
|
|
118
71
|
) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
token,
|
|
129
|
-
);
|
|
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;
|
|
130
81
|
}
|
|
131
82
|
|
|
132
|
-
export async function
|
|
83
|
+
export async function triggerPhoneTwoFactor(
|
|
133
84
|
limbuildApiUrl: string,
|
|
134
85
|
appleSessionId: string,
|
|
86
|
+
phoneNumberId: number,
|
|
87
|
+
mode = 'sms',
|
|
135
88
|
token?: string,
|
|
136
89
|
) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
);
|
|
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;
|
|
147
99
|
}
|
|
148
100
|
|
|
149
|
-
export async function
|
|
101
|
+
export async function proxyTwoFactorCode(
|
|
150
102
|
limbuildApiUrl: string,
|
|
151
103
|
appleSessionId: string,
|
|
152
|
-
|
|
104
|
+
code: string,
|
|
153
105
|
token?: string,
|
|
154
106
|
) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Accept: 'application/json',
|
|
163
|
-
...(request.payload ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}),
|
|
164
|
-
},
|
|
165
|
-
body: request.payload ? formEncode(request.payload) : undefined,
|
|
166
|
-
},
|
|
167
|
-
token,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function limbuildURL(limbuildApiUrl: string, path: string, token?: string) {
|
|
172
|
-
const url = new URL(path, limbuildApiUrl);
|
|
173
|
-
if (token) {
|
|
174
|
-
url.searchParams.set('token', token);
|
|
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()}`);
|
|
175
114
|
}
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function appleRelayURL(limbuildApiUrl: string, appleSessionId: string, appleURL: string, token?: string) {
|
|
180
|
-
const url = limbuildURL(limbuildApiUrl, '/apple/relay', token);
|
|
181
|
-
url.searchParams.set('appleSessionId', appleSessionId);
|
|
182
|
-
url.searchParams.set('url', appleURL);
|
|
183
|
-
return url;
|
|
115
|
+
return (await response.json()) as AppleRelayResponse;
|
|
184
116
|
}
|
|
185
117
|
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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()}`);
|
|
192
133
|
}
|
|
193
|
-
return
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function authHeaders(token?: string): Record<string, string> {
|
|
197
|
-
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
134
|
+
return (await response.json()) as AppleRelayResponse;
|
|
198
135
|
}
|
|
199
136
|
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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;
|
|
206
151
|
}
|
|
207
152
|
|
|
208
|
-
async function
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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()}`);
|
|
215
166
|
}
|
|
216
|
-
return
|
|
217
|
-
status: response.status,
|
|
218
|
-
statusText: `${response.status} ${response.statusText}`.trim(),
|
|
219
|
-
headers: Object.fromEntries(response.headers.entries()),
|
|
220
|
-
body,
|
|
221
|
-
rawBody,
|
|
222
|
-
bodyBase64: bytesToBase64(new TextEncoder().encode(rawBody)),
|
|
223
|
-
};
|
|
167
|
+
return normalizeAppleProxyResponse<T>((await response.json()) as AppleRelayResponse<T>);
|
|
224
168
|
}
|
|
225
169
|
|
|
226
|
-
async function
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
token,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
appleSessionId,
|
|
241
|
-
{
|
|
242
|
-
method: 'GET',
|
|
243
|
-
url: `https://idmsa.apple.com/appleauth/auth/signin${widgetKey ? `?widgetKey=${encodeURIComponent(widgetKey)}` : ''}`,
|
|
244
|
-
headers: { Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' },
|
|
245
|
-
},
|
|
246
|
-
token,
|
|
247
|
-
);
|
|
248
|
-
const bits = response.headers?.['x-apple-hc-bits'];
|
|
249
|
-
const challenge = response.headers?.['x-apple-hc-challenge'];
|
|
250
|
-
if (!bits || !challenge) {
|
|
251
|
-
return undefined;
|
|
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()}`);
|
|
252
184
|
}
|
|
253
|
-
return
|
|
185
|
+
return normalizeAppleProxyResponse<T>((await response.json()) as AppleRelayResponse<T>);
|
|
254
186
|
}
|
|
255
187
|
|
|
256
|
-
|
|
257
|
-
if (
|
|
258
|
-
return
|
|
188
|
+
function normalizeAppleProxyResponse<T>(response: AppleRelayResponse<T>) {
|
|
189
|
+
if (response.body !== undefined || !response.rawBody) {
|
|
190
|
+
return response;
|
|
259
191
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
192
|
+
try {
|
|
193
|
+
return {
|
|
194
|
+
...response,
|
|
195
|
+
body: JSON.parse(response.rawBody) as T,
|
|
196
|
+
};
|
|
197
|
+
} catch {
|
|
198
|
+
return response;
|
|
267
199
|
}
|
|
268
200
|
}
|
|
269
201
|
|
|
270
|
-
function
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
return byte >> (8 - bits) === 0;
|
|
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);
|
|
279
208
|
}
|
|
280
|
-
return
|
|
209
|
+
return url;
|
|
281
210
|
}
|
|
282
211
|
|
|
283
|
-
function
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
289
|
-
if (value === undefined || value === null) continue;
|
|
290
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
291
|
-
params.set(key, String(value));
|
|
292
|
-
} else {
|
|
293
|
-
params.set(key, JSON.stringify(value));
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return params;
|
|
212
|
+
function jsonHeaders(token?: string): Record<string, string> {
|
|
213
|
+
return {
|
|
214
|
+
'Content-Type': 'application/json',
|
|
215
|
+
...authHeaders(token),
|
|
216
|
+
};
|
|
297
217
|
}
|
|
298
218
|
|
|
299
|
-
function
|
|
300
|
-
|
|
301
|
-
for (const byte of bytes) {
|
|
302
|
-
binary += String.fromCharCode(byte);
|
|
303
|
-
}
|
|
304
|
-
return btoa(binary);
|
|
219
|
+
function authHeaders(token?: string): Record<string, string> {
|
|
220
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
305
221
|
}
|
|
@@ -73,6 +73,18 @@ export async function getLatestSigningAssets() {
|
|
|
73
73
|
)[0];
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
export async function getLatestSigningAssetsWithCertificate(teamID?: string) {
|
|
77
|
+
const all = await getAllSigningAssets();
|
|
78
|
+
return all
|
|
79
|
+
.filter((asset) => {
|
|
80
|
+
if (!asset.certificateID || !asset.certificateP12Base64 || !asset.certificatePassword) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return !teamID || !asset.teamID || asset.teamID === teamID;
|
|
84
|
+
})
|
|
85
|
+
.sort((left, right) => new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime())[0];
|
|
86
|
+
}
|
|
87
|
+
|
|
76
88
|
export async function putSigningAssets(input: PutSigningAssetsInput) {
|
|
77
89
|
const normalizedBundleID = normalizeBundleID(input.bundleID);
|
|
78
90
|
if (!normalizedBundleID) {
|
|
@@ -118,7 +130,20 @@ export function profileMatchesBundleID(profile: ProvisioningProfileInfo, bundleI
|
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
export async function parseProvisioningProfile(file: File) {
|
|
121
|
-
|
|
133
|
+
return parseProvisioningProfileBytes(new Uint8Array(await file.arrayBuffer()));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function parseProvisioningProfileBase64(base64: string) {
|
|
137
|
+
const binary = atob(base64);
|
|
138
|
+
const bytes = new Uint8Array(binary.length);
|
|
139
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
140
|
+
bytes[index] = binary.charCodeAt(index);
|
|
141
|
+
}
|
|
142
|
+
return parseProvisioningProfileBytes(bytes);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function parseProvisioningProfileBytes(bytes: Uint8Array) {
|
|
146
|
+
const text = new TextDecoder('latin1').decode(bytes);
|
|
122
147
|
const start = text.indexOf('<?xml');
|
|
123
148
|
const end = text.indexOf('</plist>');
|
|
124
149
|
if (start < 0 || end < start) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export type DeviceInstallLog = (message: string, detail?: string) => void;
|
|
2
2
|
|
|
3
|
-
export type DeviceInstallStep = '
|
|
3
|
+
export type DeviceInstallStep = 'signing' | 'connect' | 'build' | 'install';
|
|
4
4
|
|
|
5
5
|
export type DeviceInstallStepStatus = 'idle' | 'active' | 'complete' | 'error';
|
|
6
6
|
|
|
7
|
-
export type DeviceInstallBusyAction = '
|
|
7
|
+
export type DeviceInstallBusyAction = 'signing' | 'usb' | 'pair' | 'build' | 'install';
|
|
8
8
|
|
|
9
9
|
export type DeviceInstallBuildStatus =
|
|
10
10
|
| 'idle'
|