@limrun/ui 0.9.0-rc.9 → 0.13.3
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 +25 -8
- package/dist/app-store-relay/index.cjs +9 -0
- package/dist/app-store-relay/index.d.ts +102 -0
- package/dist/app-store-relay/index.js +486 -0
- package/dist/app-store-relay/react.cjs +1 -0
- package/dist/app-store-relay/react.d.ts +15 -0
- package/dist/app-store-relay/react.js +65 -0
- package/dist/browser-storage-Cx9eW-bm.mjs +205 -0
- package/dist/browser-storage-jJaIht4K.js +1 -0
- package/dist/client-ASLtHFLh.js +1 -0
- package/dist/client-CGdh8yoJ.mjs +234 -0
- package/dist/components/device-install/index.d.ts +1 -2
- package/dist/core/device-install/apple/provisioning.d.ts +58 -7
- package/dist/core/device-install/operations/limbuild-client.d.ts +15 -1
- package/dist/core/device-install/storage/browser-storage.d.ts +9 -5
- package/dist/core/device-install/types.d.ts +4 -1
- package/dist/device-build/index.cjs +1 -0
- package/dist/device-build/index.d.ts +4 -0
- package/dist/device-build/index.js +84 -0
- package/dist/device-build/react.cjs +1 -0
- package/dist/device-build/react.d.ts +20 -0
- package/dist/device-build/react.js +68 -0
- package/dist/device-build/signing.d.ts +20 -0
- package/dist/device-install/demo/demo.d.ts +0 -0
- package/dist/device-install/index.cjs +1 -1
- package/dist/device-install/index.d.ts +18 -3
- package/dist/device-install/index.js +583 -76
- package/dist/device-install/react.cjs +1 -1
- package/dist/device-install/react.d.ts +23 -1
- package/dist/device-install/react.js +98 -2
- package/dist/hooks/index.d.ts +1 -1
- package/dist/index-CMeQfhYy.js +22 -0
- package/dist/index-DFdCvi2_.mjs +11543 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +502 -495
- package/dist/limbuild-client-CgEncrgx.js +1 -0
- package/dist/limbuild-client-D7U7w342.mjs +81 -0
- package/dist/provisioning-DKbkBO-p.mjs +239 -0
- package/dist/provisioning-Va0PH4Kk.js +1 -0
- package/package.json +21 -1
- package/src/app-store-relay/index.test.ts +70 -0
- package/src/app-store-relay/index.ts +514 -0
- package/src/app-store-relay/react.ts +129 -0
- package/src/components/device-install/index.ts +1 -2
- package/src/core/device-install/apple/client.ts +29 -22
- package/src/core/device-install/apple/crypto.ts +9 -6
- package/src/core/device-install/apple/gsa-srp.ts +3 -7
- package/src/core/device-install/apple/provisioning.test.ts +84 -0
- package/src/core/device-install/apple/provisioning.ts +98 -16
- package/src/core/device-install/apple/relay.ts +5 -1
- package/src/core/device-install/operations/limbuild-client.ts +41 -4
- package/src/core/device-install/operations/operations.ts +3 -1
- package/src/core/device-install/operations/relay-client.ts +57 -27
- package/src/core/device-install/operations/usbmux.ts +3 -3
- package/src/core/device-install/operations/webusb.ts +9 -4
- package/src/core/device-install/storage/browser-storage.ts +43 -14
- package/src/core/device-install/types.ts +6 -8
- package/src/device-build/index.ts +42 -0
- package/src/device-build/react.ts +131 -0
- package/src/device-build/signing.ts +94 -0
- package/src/device-install/README.md +423 -0
- package/src/device-install/demo/demo.css +493 -0
- package/src/device-install/demo/demo.tsx +1069 -0
- package/src/device-install/demo/index.html +12 -0
- package/src/device-install/index.ts +49 -3
- package/src/device-install/react.ts +189 -1
- package/src/hooks/index.ts +1 -1
- package/src/index.ts +0 -3
- package/vite.config.ts +4 -0
- package/dist/components/device-install/device-install-dialog.d.ts +0 -5
- package/dist/device-install-dialog-CqxDEH85.mjs +0 -443
- package/dist/device-install-dialog-jvqQ-fuo.js +0 -2
- package/dist/device-install-dialog.css +0 -1
- package/dist/hooks/use-device-install.d.ts +0 -73
- package/dist/use-device-install-CAPli9MR.js +0 -31
- package/dist/use-device-install-H8dqqvbR.mjs +0 -13624
- package/src/components/device-install/device-install-dialog.css +0 -325
- package/src/components/device-install/device-install-dialog.tsx +0 -495
- package/src/hooks/use-device-install.ts +0 -1219
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Real Device Install Demo - @limrun/ui</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="./demo.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -1,3 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
requestUSBAccess as requestCoreUSBAccess,
|
|
3
|
+
startInstallRelay,
|
|
4
|
+
startPairingRelay,
|
|
5
|
+
type RequestUSBAccessOptions as CoreRequestUSBAccessOptions,
|
|
6
|
+
type StartInstallRelayOptions,
|
|
7
|
+
type StartPairingRelayOptions,
|
|
8
|
+
} from '../core/device-install/operations';
|
|
9
|
+
import type { DeviceInstallLog } from '../core/device-install/types';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
closeDeviceRelayTarget,
|
|
13
|
+
deviceRelayWebSocketUrl,
|
|
14
|
+
startInstallRelay as startDeviceInstallRelay,
|
|
15
|
+
startPairingRelay as startDevicePairingRelay,
|
|
16
|
+
type DeviceRelayTarget,
|
|
17
|
+
type StartInstallRelayOptions,
|
|
18
|
+
type StartPairingRelayOptions,
|
|
19
|
+
} from '../core/device-install/operations';
|
|
20
|
+
export { getPairRecord, normalizeUDID, putPairRecord } from '../core/device-install/storage';
|
|
21
|
+
export type {
|
|
22
|
+
DeviceHello,
|
|
23
|
+
DeviceInstallLog,
|
|
24
|
+
PairRecordPayload,
|
|
25
|
+
StoredPairRecord,
|
|
26
|
+
} from '../core/device-install/types';
|
|
27
|
+
export { RelayClient } from '../core/device-install/operations';
|
|
28
|
+
|
|
29
|
+
export type RequestUSBAccessOptions = Partial<CoreRequestUSBAccessOptions>;
|
|
30
|
+
|
|
31
|
+
export async function requestUSBAccess(options: RequestUSBAccessOptions = {}) {
|
|
32
|
+
return requestCoreUSBAccess({ log: options.log ?? noopLog });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function pairDevice(
|
|
36
|
+
options: Omit<StartPairingRelayOptions, 'log'> & { log?: DeviceInstallLog },
|
|
37
|
+
) {
|
|
38
|
+
return startPairingRelay({ ...options, log: options.log ?? noopLog });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function startDeviceInstall(
|
|
42
|
+
options: Omit<StartInstallRelayOptions, 'log'> & { log?: DeviceInstallLog },
|
|
43
|
+
) {
|
|
44
|
+
return startInstallRelay({ ...options, log: options.log ?? noopLog });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function noopLog() {
|
|
48
|
+
// Intentionally empty. Consumers can pass a logger for progress messages.
|
|
49
|
+
}
|
|
@@ -1 +1,189 @@
|
|
|
1
|
-
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
closeDeviceRelayTarget,
|
|
4
|
+
getPairRecord,
|
|
5
|
+
pairDevice,
|
|
6
|
+
putPairRecord,
|
|
7
|
+
requestUSBAccess,
|
|
8
|
+
startDeviceInstall,
|
|
9
|
+
type DeviceInstallLog,
|
|
10
|
+
type DeviceRelayTarget,
|
|
11
|
+
type RelayClient,
|
|
12
|
+
type StoredPairRecord,
|
|
13
|
+
} from './index';
|
|
14
|
+
|
|
15
|
+
export type DeviceInstallRelayBusyAction = 'usb' | 'pair' | 'install';
|
|
16
|
+
|
|
17
|
+
export type UseDeviceInstallRelayOptions = {
|
|
18
|
+
apiUrl?: string;
|
|
19
|
+
token?: string;
|
|
20
|
+
log?: DeviceInstallLog;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type UseDeviceInstallRelayResult = {
|
|
24
|
+
device?: DeviceRelayTarget;
|
|
25
|
+
pairRecord?: StoredPairRecord;
|
|
26
|
+
busyAction?: DeviceInstallRelayBusyAction;
|
|
27
|
+
error?: string;
|
|
28
|
+
pairConfirmationRequired: boolean;
|
|
29
|
+
hasPairRecord: boolean;
|
|
30
|
+
canPair: boolean;
|
|
31
|
+
canInstall: boolean;
|
|
32
|
+
requestUSBAccess: () => Promise<DeviceRelayTarget | undefined>;
|
|
33
|
+
pairBrowser: () => Promise<StoredPairRecord | undefined>;
|
|
34
|
+
startInstallation: () => Promise<RelayClient | undefined>;
|
|
35
|
+
stopRelay: () => void;
|
|
36
|
+
clearError: () => void;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function useDeviceInstallRelay({
|
|
40
|
+
apiUrl,
|
|
41
|
+
token,
|
|
42
|
+
log = noopLog,
|
|
43
|
+
}: UseDeviceInstallRelayOptions): UseDeviceInstallRelayResult {
|
|
44
|
+
const [device, setDevice] = useState<DeviceRelayTarget | undefined>();
|
|
45
|
+
const [pairRecord, setPairRecord] = useState<StoredPairRecord | undefined>();
|
|
46
|
+
const [busyAction, setBusyAction] = useState<DeviceInstallRelayBusyAction | undefined>();
|
|
47
|
+
const [error, setError] = useState<string | undefined>();
|
|
48
|
+
const [pairConfirmationRequired, setPairConfirmationRequired] = useState(false);
|
|
49
|
+
const relayRef = useRef<RelayClient | undefined>(undefined);
|
|
50
|
+
const deviceRef = useRef<DeviceRelayTarget | undefined>(undefined);
|
|
51
|
+
// Keep the logger in a ref so callbacks below don't change identity when the
|
|
52
|
+
// consumer passes an unmemoized `log`. Otherwise every render would recreate
|
|
53
|
+
// cleanupDeviceAccess, re-run the unmount effect, and close the USB device
|
|
54
|
+
// mid-claim (surfacing as "Unable to claim interface" / "operation in progress").
|
|
55
|
+
const logRef = useRef(log);
|
|
56
|
+
logRef.current = log;
|
|
57
|
+
|
|
58
|
+
const cleanupDeviceAccess = useCallback(async () => {
|
|
59
|
+
relayRef.current?.close();
|
|
60
|
+
relayRef.current = undefined;
|
|
61
|
+
await closeDeviceRelayTarget(deviceRef.current, logRef.current);
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
deviceRef.current = device;
|
|
66
|
+
}, [device]);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
return () => {
|
|
70
|
+
void cleanupDeviceAccess();
|
|
71
|
+
};
|
|
72
|
+
}, [cleanupDeviceAccess]);
|
|
73
|
+
|
|
74
|
+
const selectUSBDevice = useCallback(async () => {
|
|
75
|
+
setBusyAction('usb');
|
|
76
|
+
setError(undefined);
|
|
77
|
+
setPairConfirmationRequired(false);
|
|
78
|
+
let target: DeviceRelayTarget | undefined;
|
|
79
|
+
try {
|
|
80
|
+
await cleanupDeviceAccess();
|
|
81
|
+
target = await requestUSBAccess({ log: logRef.current });
|
|
82
|
+
const storedPairRecord = await getPairRecord(target.hello.serialNumber);
|
|
83
|
+
setDevice(target);
|
|
84
|
+
setPairRecord(storedPairRecord);
|
|
85
|
+
logRef.current(
|
|
86
|
+
storedPairRecord ? 'Pair record found' : 'No pair record found',
|
|
87
|
+
target.hello.serialNumber,
|
|
88
|
+
);
|
|
89
|
+
return target;
|
|
90
|
+
} catch (caught) {
|
|
91
|
+
await closeDeviceRelayTarget(target, logRef.current);
|
|
92
|
+
setDevice(undefined);
|
|
93
|
+
setPairRecord(undefined);
|
|
94
|
+
setError(errorMessage(caught));
|
|
95
|
+
return undefined;
|
|
96
|
+
} finally {
|
|
97
|
+
setBusyAction(undefined);
|
|
98
|
+
}
|
|
99
|
+
}, [cleanupDeviceAccess]);
|
|
100
|
+
|
|
101
|
+
const pairBrowser = useCallback(async () => {
|
|
102
|
+
if (!apiUrl || !device) {
|
|
103
|
+
throw new Error('Select a USB device before pairing.');
|
|
104
|
+
}
|
|
105
|
+
setBusyAction('pair');
|
|
106
|
+
setError(undefined);
|
|
107
|
+
setPairConfirmationRequired(false);
|
|
108
|
+
try {
|
|
109
|
+
await cleanupDeviceAccess();
|
|
110
|
+
const result = await pairDevice({
|
|
111
|
+
limbuildApiUrl: apiUrl,
|
|
112
|
+
token,
|
|
113
|
+
log: logRef.current,
|
|
114
|
+
target: device,
|
|
115
|
+
});
|
|
116
|
+
const stored = await putPairRecord(result.pairRecord, {
|
|
117
|
+
productName: device.hello.productName,
|
|
118
|
+
});
|
|
119
|
+
result.relay.close();
|
|
120
|
+
await closeDeviceRelayTarget(device, logRef.current);
|
|
121
|
+
setPairRecord(stored);
|
|
122
|
+
setPairConfirmationRequired(false);
|
|
123
|
+
logRef.current('Device paired', 'The pair record was stored locally in this browser.');
|
|
124
|
+
return stored;
|
|
125
|
+
} catch (caught) {
|
|
126
|
+
await closeDeviceRelayTarget(device, logRef.current);
|
|
127
|
+
setPairConfirmationRequired(true);
|
|
128
|
+
setError(errorMessage(caught));
|
|
129
|
+
return undefined;
|
|
130
|
+
} finally {
|
|
131
|
+
setBusyAction(undefined);
|
|
132
|
+
}
|
|
133
|
+
}, [apiUrl, cleanupDeviceAccess, device, token]);
|
|
134
|
+
|
|
135
|
+
const startInstallation = useCallback(async () => {
|
|
136
|
+
if (!apiUrl || !device || !pairRecord) {
|
|
137
|
+
throw new Error('Select and pair a USB device before starting installation.');
|
|
138
|
+
}
|
|
139
|
+
setBusyAction('install');
|
|
140
|
+
setError(undefined);
|
|
141
|
+
try {
|
|
142
|
+
await cleanupDeviceAccess();
|
|
143
|
+
relayRef.current = await startDeviceInstall({
|
|
144
|
+
limbuildApiUrl: apiUrl,
|
|
145
|
+
token,
|
|
146
|
+
log: logRef.current,
|
|
147
|
+
target: device,
|
|
148
|
+
pairRecord,
|
|
149
|
+
});
|
|
150
|
+
logRef.current('Device install started', 'Installation will continue through the connected iPhone.');
|
|
151
|
+
return relayRef.current;
|
|
152
|
+
} catch (caught) {
|
|
153
|
+
await closeDeviceRelayTarget(device, logRef.current);
|
|
154
|
+
setError(errorMessage(caught));
|
|
155
|
+
return undefined;
|
|
156
|
+
} finally {
|
|
157
|
+
setBusyAction(undefined);
|
|
158
|
+
}
|
|
159
|
+
}, [apiUrl, cleanupDeviceAccess, device, pairRecord, token]);
|
|
160
|
+
|
|
161
|
+
const stopRelay = useCallback(() => {
|
|
162
|
+
void cleanupDeviceAccess();
|
|
163
|
+
logRef.current('Device relay stopped');
|
|
164
|
+
}, [cleanupDeviceAccess]);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
device,
|
|
168
|
+
pairRecord,
|
|
169
|
+
busyAction,
|
|
170
|
+
error,
|
|
171
|
+
pairConfirmationRequired,
|
|
172
|
+
hasPairRecord: !!pairRecord,
|
|
173
|
+
canPair: !!apiUrl && !busyAction && !!device,
|
|
174
|
+
canInstall: !!apiUrl && !busyAction && !!device && !!pairRecord,
|
|
175
|
+
requestUSBAccess: selectUSBDevice,
|
|
176
|
+
pairBrowser,
|
|
177
|
+
startInstallation,
|
|
178
|
+
stopRelay,
|
|
179
|
+
clearError: () => setError(undefined),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function noopLog() {
|
|
184
|
+
// Intentionally empty. Consumers can pass a logger for progress messages.
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function errorMessage(error: unknown) {
|
|
188
|
+
return error instanceof Error ? error.message : String(error);
|
|
189
|
+
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|
package/src/index.ts
CHANGED
package/vite.config.ts
CHANGED
|
@@ -11,6 +11,10 @@ export default defineConfig({
|
|
|
11
11
|
lib: {
|
|
12
12
|
entry: {
|
|
13
13
|
index: resolve(__dirname, 'src/index.ts'),
|
|
14
|
+
'app-store-relay/index': resolve(__dirname, 'src/app-store-relay/index.ts'),
|
|
15
|
+
'app-store-relay/react': resolve(__dirname, 'src/app-store-relay/react.ts'),
|
|
16
|
+
'device-build/index': resolve(__dirname, 'src/device-build/index.ts'),
|
|
17
|
+
'device-build/react': resolve(__dirname, 'src/device-build/react.ts'),
|
|
14
18
|
'device-install/index': resolve(__dirname, 'src/device-install/index.ts'),
|
|
15
19
|
'device-install/react': resolve(__dirname, 'src/device-install/react.ts'),
|
|
16
20
|
},
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { UseDeviceInstallOptions } from '../../hooks/use-device-install';
|
|
2
|
-
export type DeviceInstallDialogProps = UseDeviceInstallOptions & {
|
|
3
|
-
disabled?: boolean;
|
|
4
|
-
};
|
|
5
|
-
export declare function DeviceInstallDialog({ disabled, ...hookOptions }: DeviceInstallDialogProps): import("react/jsx-runtime").JSX.Element;
|