@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.
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 +1189 -1089
  6. package/package.json +10 -51
  7. package/src/components/remote-control.tsx +267 -0
  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,71 +0,0 @@
1
- export const RELAY_PROTOCOL_VERSION = 1;
2
- export const RELAY_HEADER_BYTES = 16;
3
-
4
- export const RelayMessageType = {
5
- DeviceHello: 1,
6
- OpenStream: 2,
7
- OpenResult: 3,
8
- StreamData: 4,
9
- StreamClose: 5,
10
- InstallProgress: 6,
11
- Error: 7,
12
- Ping: 8,
13
- Pong: 9,
14
- StartPairing: 10,
15
- StartInstall: 11,
16
- PairRecordReady: 12,
17
- } as const;
18
-
19
- export type RelayMessageType = (typeof RelayMessageType)[keyof typeof RelayMessageType];
20
-
21
- export type RelayFrame = {
22
- type: RelayMessageType;
23
- requestId: number;
24
- streamId: number;
25
- payload: Uint8Array;
26
- };
27
-
28
- export function encodeFrame(frame: RelayFrame) {
29
- const result = new Uint8Array(RELAY_HEADER_BYTES + frame.payload.byteLength);
30
- const view = new DataView(result.buffer);
31
- view.setUint8(0, RELAY_PROTOCOL_VERSION);
32
- view.setUint8(1, frame.type);
33
- view.setUint8(2, 0);
34
- view.setUint8(3, 0);
35
- view.setUint32(4, frame.requestId);
36
- view.setUint32(8, frame.streamId);
37
- view.setUint32(12, frame.payload.byteLength);
38
- result.set(frame.payload, RELAY_HEADER_BYTES);
39
- return result;
40
- }
41
-
42
- export function decodeFrame(data: Uint8Array): RelayFrame {
43
- if (data.byteLength < RELAY_HEADER_BYTES) {
44
- throw new Error(`Relay frame too short: ${data.byteLength}`);
45
- }
46
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
47
- const version = view.getUint8(0);
48
- if (version !== RELAY_PROTOCOL_VERSION) {
49
- throw new Error(`Unsupported relay protocol version ${version}`);
50
- }
51
- const payloadLength = view.getUint32(12);
52
- if (data.byteLength !== RELAY_HEADER_BYTES + payloadLength) {
53
- throw new Error(
54
- `Relay frame length mismatch: got ${data.byteLength}, expected ${RELAY_HEADER_BYTES + payloadLength}`,
55
- );
56
- }
57
- return {
58
- type: view.getUint8(1) as RelayMessageType,
59
- requestId: view.getUint32(4),
60
- streamId: view.getUint32(8),
61
- payload: data.slice(RELAY_HEADER_BYTES),
62
- };
63
- }
64
-
65
- export function encodeJson(value: unknown) {
66
- return new TextEncoder().encode(JSON.stringify(value));
67
- }
68
-
69
- export function decodeJson<T>(payload: Uint8Array): T {
70
- return JSON.parse(new TextDecoder().decode(payload)) as T;
71
- }
@@ -1,270 +0,0 @@
1
- /// <reference path="./webusb-dom.d.ts" />
2
-
3
- import type { UsbmuxCandidate } from './webusb';
4
- import { getBulkEndpoints, transferIn, transferOutWithZlp } from './webusb';
5
-
6
- const PROTO_VERSION = 0;
7
- const PROTO_SETUP = 2;
8
- const PROTO_TCP = 6;
9
- const MAGIC = 0xfeedface;
10
- const FLAG_SYN = 0x02;
11
- const FLAG_RST = 0x04;
12
- const FLAG_ACK = 0x10;
13
- const FIRST_SPORT = 49152;
14
-
15
- export type UsbmuxSession = {
16
- device: USBDevice;
17
- candidate: UsbmuxCandidate;
18
- inEndpoint: ReturnType<typeof getBulkEndpoints>['inEndpoint'];
19
- outEndpoint: ReturnType<typeof getBulkEndpoints>['outEndpoint'];
20
- muxVersion: number;
21
- txSeq: number;
22
- rxSeq: number;
23
- nextSport: number;
24
- streams: Map<string, UsbmuxStream>;
25
- writeChain: Promise<void>;
26
- closed: boolean;
27
- };
28
-
29
- export type UsbmuxStream = {
30
- session: UsbmuxSession;
31
- sport: number;
32
- dport: number;
33
- seq: number;
34
- ack: number;
35
- queue: Uint8Array[];
36
- waiters: Array<(value: Uint8Array) => void>;
37
- error?: Error;
38
- opened: Promise<void>;
39
- resolveOpened: () => void;
40
- rejectOpened: (error: Error) => void;
41
- };
42
-
43
- export async function createUsbmuxSession(device: USBDevice, candidate: UsbmuxCandidate) {
44
- const { inEndpoint, outEndpoint } = getBulkEndpoints(candidate);
45
- const versionPayload = new Uint8Array(12);
46
- const versionView = new DataView(versionPayload.buffer);
47
- versionView.setUint32(0, 2);
48
- versionView.setUint32(4, 0);
49
- versionView.setUint32(8, 0);
50
- await transferOutWithZlp(device, outEndpoint, buildV1Packet(PROTO_VERSION, versionPayload));
51
- const versionPacket = await readPacket(device, inEndpoint, 1);
52
- const version = new DataView(versionPacket.payload.buffer, versionPacket.payload.byteOffset).getUint32(0);
53
- const session: UsbmuxSession = {
54
- device,
55
- candidate,
56
- inEndpoint,
57
- outEndpoint,
58
- muxVersion: version,
59
- txSeq: 0,
60
- rxSeq: 0xffff,
61
- nextSport: FIRST_SPORT,
62
- streams: new Map(),
63
- writeChain: Promise.resolve(),
64
- closed: false,
65
- };
66
- if (version >= 2) {
67
- await sendMux(session, PROTO_SETUP, new Uint8Array([0x07]));
68
- }
69
- void readLoop(session);
70
- return session;
71
- }
72
-
73
- export async function openStream(session: UsbmuxSession, port: number) {
74
- if (session.closed) {
75
- throw new Error('usbmux session is closed.');
76
- }
77
- let resolveOpened!: () => void;
78
- let rejectOpened!: (error: Error) => void;
79
- const stream: UsbmuxStream = {
80
- session,
81
- sport: session.nextSport++,
82
- dport: port,
83
- seq: 0,
84
- ack: 0,
85
- queue: [],
86
- waiters: [],
87
- opened: new Promise<void>((resolve, reject) => {
88
- resolveOpened = resolve;
89
- rejectOpened = reject;
90
- }),
91
- resolveOpened,
92
- rejectOpened,
93
- };
94
- session.streams.set(streamKey(port, stream.sport), stream);
95
- await sendTcp(stream, FLAG_SYN, new Uint8Array());
96
- await stream.opened;
97
- return stream;
98
- }
99
-
100
- export async function sendStreamData(stream: UsbmuxStream, bytes: Uint8Array) {
101
- if (stream.session.closed) {
102
- throw new Error('usbmux session is closed.');
103
- }
104
- await sendTcp(stream, FLAG_ACK, bytes);
105
- stream.seq += bytes.byteLength;
106
- }
107
-
108
- export async function receiveStreamData(stream: UsbmuxStream) {
109
- if (stream.queue.length > 0) {
110
- return stream.queue.shift()!;
111
- }
112
- if (stream.error) {
113
- throw stream.error;
114
- }
115
- return new Promise<Uint8Array>((resolve) => {
116
- stream.waiters.push(resolve);
117
- });
118
- }
119
-
120
- export function closeUsbmuxSession(session: UsbmuxSession) {
121
- session.closed = true;
122
- for (const stream of session.streams.values()) {
123
- stream.error = new Error('usbmux session closed');
124
- stream.rejectOpened(stream.error);
125
- while (stream.waiters.length > 0) {
126
- stream.waiters.shift()!(new Uint8Array());
127
- }
128
- }
129
- session.streams.clear();
130
- }
131
-
132
- async function sendTcp(stream: UsbmuxStream, flags: number, payload: Uint8Array) {
133
- const tcp = new Uint8Array(20 + payload.byteLength);
134
- const view = new DataView(tcp.buffer);
135
- view.setUint16(0, stream.sport);
136
- view.setUint16(2, stream.dport);
137
- view.setUint32(4, stream.seq);
138
- view.setUint32(8, stream.ack);
139
- view.setUint8(12, 0x50);
140
- view.setUint8(13, flags);
141
- view.setUint16(14, 512);
142
- tcp.set(payload, 20);
143
- await sendMux(stream.session, PROTO_TCP, tcp);
144
- }
145
-
146
- async function sendMux(session: UsbmuxSession, protocol: number, payload: Uint8Array) {
147
- if (session.closed) {
148
- throw new Error('usbmux session is closed.');
149
- }
150
- session.writeChain = session.writeChain.then(async () => {
151
- if (session.closed) return;
152
- const packet =
153
- session.muxVersion >= 2 ?
154
- buildV2Packet(protocol, payload, session.txSeq++, session.rxSeq)
155
- : buildV1Packet(protocol, payload);
156
- await transferOutWithZlp(session.device, session.outEndpoint, packet);
157
- });
158
- return session.writeChain;
159
- }
160
-
161
- async function readLoop(session: UsbmuxSession) {
162
- try {
163
- for (;;) {
164
- if (session.closed) return;
165
- const packet = await readPacket(session.device, session.inEndpoint, session.muxVersion);
166
- if (session.closed) return;
167
- if (packet.rxSeq !== undefined) {
168
- session.rxSeq = packet.rxSeq;
169
- }
170
- if (packet.protocol !== PROTO_TCP) {
171
- continue;
172
- }
173
- const tcp = parseTcp(packet.payload);
174
- const stream = session.streams.get(streamKey(tcp.sport, tcp.dport));
175
- if (!stream) {
176
- continue;
177
- }
178
- if (tcp.flags & FLAG_RST) {
179
- stream.error = new Error(`Device reset stream ${stream.dport}`);
180
- stream.rejectOpened(stream.error);
181
- while (stream.waiters.length > 0) {
182
- stream.waiters.shift()!(new Uint8Array());
183
- }
184
- session.streams.delete(streamKey(stream.dport, stream.sport));
185
- continue;
186
- }
187
- if ((tcp.flags & (FLAG_SYN | FLAG_ACK)) === (FLAG_SYN | FLAG_ACK)) {
188
- stream.seq += 1;
189
- stream.ack = tcp.seq + 1;
190
- await sendTcp(stream, FLAG_ACK, new Uint8Array());
191
- stream.resolveOpened();
192
- continue;
193
- }
194
- if (tcp.payload.byteLength === 0) {
195
- continue;
196
- }
197
- stream.ack = tcp.seq + tcp.payload.byteLength;
198
- await sendTcp(stream, FLAG_ACK, new Uint8Array());
199
- if (stream.waiters.length > 0) {
200
- stream.waiters.shift()!(tcp.payload);
201
- } else {
202
- stream.queue.push(tcp.payload);
203
- }
204
- }
205
- } catch (error) {
206
- if (!session.closed) {
207
- closeUsbmuxSession(session);
208
- }
209
- }
210
- }
211
-
212
- async function readPacket(device: USBDevice, endpoint: UsbmuxSession['inEndpoint'], version: number) {
213
- for (;;) {
214
- const bytes = await transferIn(device, endpoint);
215
- const packet = parseMux(bytes, version);
216
- if (version === 1 && packet.protocol !== PROTO_VERSION) continue;
217
- return packet;
218
- }
219
- }
220
-
221
- function buildV1Packet(protocol: number, payload: Uint8Array) {
222
- const bytes = new Uint8Array(8 + payload.byteLength);
223
- const view = new DataView(bytes.buffer);
224
- view.setUint32(0, protocol);
225
- view.setUint32(4, bytes.byteLength);
226
- bytes.set(payload, 8);
227
- return bytes;
228
- }
229
-
230
- function buildV2Packet(protocol: number, payload: Uint8Array, txSeq: number, rxSeq: number) {
231
- const bytes = new Uint8Array(16 + payload.byteLength);
232
- const view = new DataView(bytes.buffer);
233
- view.setUint32(0, protocol);
234
- view.setUint32(4, bytes.byteLength);
235
- view.setUint32(8, MAGIC);
236
- view.setUint16(12, txSeq);
237
- view.setUint16(14, rxSeq);
238
- bytes.set(payload, 16);
239
- return bytes;
240
- }
241
-
242
- function parseMux(bytes: Uint8Array, version: number) {
243
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
244
- const protocol = view.getUint32(0);
245
- const length = view.getUint32(4);
246
- const headerSize = version >= 2 ? 16 : 8;
247
- return {
248
- protocol,
249
- length,
250
- rxSeq: version >= 2 ? view.getUint16(14) : undefined,
251
- payload: bytes.slice(headerSize, length),
252
- };
253
- }
254
-
255
- function parseTcp(bytes: Uint8Array) {
256
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
257
- const dataOffset = (view.getUint8(12) >> 4) * 4;
258
- return {
259
- sport: view.getUint16(0),
260
- dport: view.getUint16(2),
261
- seq: view.getUint32(4),
262
- ack: view.getUint32(8),
263
- flags: view.getUint8(13),
264
- payload: bytes.slice(dataOffset),
265
- };
266
- }
267
-
268
- function streamKey(devicePort: number, hostPort: number) {
269
- return `${devicePort}:${hostPort}`;
270
- }
@@ -1,54 +0,0 @@
1
- type USBTransferStatus = 'ok' | 'stall' | 'babble';
2
-
3
- interface USBInTransferResult {
4
- data?: DataView;
5
- status: USBTransferStatus;
6
- }
7
-
8
- interface USBOutTransferResult {
9
- bytesWritten: number;
10
- status: USBTransferStatus;
11
- }
12
-
13
- interface USBDevice {
14
- vendorId: number;
15
- productId: number;
16
- productName?: string;
17
- manufacturerName?: string;
18
- serialNumber?: string;
19
- opened: boolean;
20
- configuration: { configurationValue: number } | null;
21
- configurations: Array<{
22
- configurationValue: number;
23
- interfaces: Array<{
24
- interfaceNumber: number;
25
- alternates: Array<{
26
- alternateSetting: number;
27
- interfaceClass: number;
28
- interfaceSubclass: number;
29
- interfaceProtocol: number;
30
- endpoints: Array<{
31
- endpointNumber: number;
32
- direction: 'in' | 'out';
33
- type: string;
34
- packetSize: number;
35
- }>;
36
- }>;
37
- }>;
38
- }>;
39
- open(): Promise<void>;
40
- close(): Promise<void>;
41
- selectConfiguration(configurationValue: number): Promise<void>;
42
- claimInterface(interfaceNumber: number): Promise<void>;
43
- releaseInterface(interfaceNumber: number): Promise<void>;
44
- selectAlternateInterface(interfaceNumber: number, alternateSetting: number): Promise<void>;
45
- transferIn(endpointNumber: number, length: number): Promise<USBInTransferResult>;
46
- transferOut(endpointNumber: number, data: Uint8Array): Promise<USBOutTransferResult>;
47
- }
48
-
49
- interface Navigator {
50
- usb?: {
51
- requestDevice(options: { filters: Array<{ vendorId: number }> }): Promise<USBDevice>;
52
- getDevices(): Promise<USBDevice[]>;
53
- };
54
- }
@@ -1,110 +0,0 @@
1
- /// <reference path="./webusb-dom.d.ts" />
2
-
3
- export type UsbEndpoint = {
4
- endpointNumber: number;
5
- direction: 'in' | 'out';
6
- type: string;
7
- packetSize: number;
8
- };
9
-
10
- export type UsbmuxCandidate = {
11
- configurationValue: number;
12
- interfaceNumber: number;
13
- alternateSetting: number;
14
- endpoints: UsbEndpoint[];
15
- };
16
-
17
- export async function requestAppleDevice() {
18
- if (!navigator.usb) {
19
- throw new Error('WebUSB is not available in this browser.');
20
- }
21
- return navigator.usb.requestDevice({ filters: [{ vendorId: 0x05ac }] });
22
- }
23
-
24
- export function findUsbmuxCandidates(device: USBDevice): UsbmuxCandidate[] {
25
- const candidates: UsbmuxCandidate[] = [];
26
- const activeConfigurationValue = device.configuration?.configurationValue;
27
- const activeConfiguration = device.configurations.find(
28
- (configuration) => configuration.configurationValue === activeConfigurationValue,
29
- );
30
- if (activeConfiguration) {
31
- candidates.push(...findConfigurationUsbmuxCandidates(activeConfiguration));
32
- }
33
- for (const configuration of device.configurations) {
34
- if (configuration.configurationValue === activeConfigurationValue) {
35
- continue;
36
- }
37
- candidates.push(...findConfigurationUsbmuxCandidates(configuration));
38
- }
39
- return candidates;
40
- }
41
-
42
- function findConfigurationUsbmuxCandidates(
43
- configuration: USBDevice['configurations'][number],
44
- ): UsbmuxCandidate[] {
45
- const candidates: UsbmuxCandidate[] = [];
46
- for (const usbInterface of configuration.interfaces) {
47
- for (const alternate of usbInterface.alternates) {
48
- if (
49
- alternate.interfaceClass === 0xff &&
50
- alternate.interfaceSubclass === 0xfe &&
51
- alternate.interfaceProtocol === 0x02
52
- ) {
53
- candidates.push({
54
- configurationValue: configuration.configurationValue,
55
- interfaceNumber: usbInterface.interfaceNumber,
56
- alternateSetting: alternate.alternateSetting,
57
- endpoints: alternate.endpoints,
58
- });
59
- }
60
- }
61
- }
62
- return candidates;
63
- }
64
-
65
- export async function claimUsbmux(device: USBDevice, candidate: UsbmuxCandidate) {
66
- if (!device.opened) {
67
- await device.open();
68
- }
69
- if (!device.configuration || device.configuration.configurationValue !== candidate.configurationValue) {
70
- await device.selectConfiguration(candidate.configurationValue);
71
- }
72
- await device.claimInterface(candidate.interfaceNumber);
73
- // Always select the alternate (even alt 0). On macOS Chrome the
74
- // interface's endpoints are not treated as active until the alternate
75
- // is explicitly selected, which otherwise surfaces as
76
- // "endpoint is not part of a claimed and selected alternate interface"
77
- // on the first transferIn/transferOut.
78
- await device.selectAlternateInterface(candidate.interfaceNumber, candidate.alternateSetting);
79
- }
80
-
81
- export function getBulkEndpoints(candidate: UsbmuxCandidate) {
82
- const outEndpoint = candidate.endpoints.find(
83
- (endpoint) => endpoint.direction === 'out' && endpoint.type === 'bulk',
84
- );
85
- const inEndpoint = candidate.endpoints.find(
86
- (endpoint) => endpoint.direction === 'in' && endpoint.type === 'bulk',
87
- );
88
- if (!outEndpoint || !inEndpoint) {
89
- throw new Error('Could not find usbmux bulk endpoints.');
90
- }
91
- return { outEndpoint, inEndpoint };
92
- }
93
-
94
- export async function transferOutWithZlp(device: USBDevice, endpoint: UsbEndpoint, bytes: Uint8Array) {
95
- const result = await device.transferOut(endpoint.endpointNumber, bytes);
96
- if (result.status !== 'ok') {
97
- throw new Error(`USB transferOut failed: ${result.status}`);
98
- }
99
- if (bytes.byteLength % endpoint.packetSize === 0) {
100
- await device.transferOut(endpoint.endpointNumber, new Uint8Array());
101
- }
102
- }
103
-
104
- export async function transferIn(device: USBDevice, endpoint: UsbEndpoint, size = 16384) {
105
- const result = await device.transferIn(endpoint.endpointNumber, size);
106
- if (result.status !== 'ok' || !result.data) {
107
- throw new Error(`USB transferIn failed: ${result.status}`);
108
- }
109
- return new Uint8Array(result.data.buffer, result.data.byteOffset, result.data.byteLength);
110
- }
@@ -1,65 +0,0 @@
1
- import forge from 'node-forge';
2
- import { describe, expect, test } from 'vitest';
3
- import { normalizeCertificateSerial, parseProvisioningProfileBase64 } from './browser-storage';
4
-
5
- function selfSignedCertificateBase64(serialNumber: string) {
6
- const keys = forge.pki.rsa.generateKeyPair(1024);
7
- const cert = forge.pki.createCertificate();
8
- cert.publicKey = keys.publicKey;
9
- cert.serialNumber = serialNumber;
10
- cert.validity.notBefore = new Date();
11
- cert.validity.notAfter = new Date(Date.now() + 24 * 60 * 60 * 1000);
12
- const attrs = [{ name: 'commonName', value: 'Apple Development Test' }];
13
- cert.setSubject(attrs);
14
- cert.setIssuer(attrs);
15
- cert.sign(keys.privateKey);
16
- return forge.util.encode64(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes());
17
- }
18
-
19
- function profileBase64(certificatesBase64: string[]) {
20
- const xml = `<?xml version="1.0" encoding="UTF-8"?>
21
- <plist version="1.0">
22
- <dict>
23
- <key>Name</key><string>Limrun Dev</string>
24
- <key>UUID</key><string>11111111-2222-3333-4444-555555555555</string>
25
- <key>TeamIdentifier</key><array><string>TEAM123</string></array>
26
- <key>ProvisionedDevices</key><array><string>00008030-000A</string></array>
27
- <key>DeveloperCertificates</key>
28
- <array>${certificatesBase64.map((c) => `<data>${c}</data>`).join('')}</array>
29
- <key>Entitlements</key>
30
- <dict>
31
- <key>application-identifier</key><string>TEAM123.com.example.app</string>
32
- <key>com.apple.developer.team-identifier</key><string>TEAM123</string>
33
- </dict>
34
- </dict>
35
- </plist>`;
36
- return btoa(xml);
37
- }
38
-
39
- describe('parseProvisioningProfileBase64', () => {
40
- test('extracts embedded certificate serial numbers in normalized form', () => {
41
- const parsed = parseProvisioningProfileBase64(
42
- profileBase64([selfSignedCertificateBase64('00ab12cd'), selfSignedCertificateBase64('7f')]),
43
- );
44
- expect(parsed.certificateSerialNumbers).toEqual(['AB12CD', '7F']);
45
- expect(parsed.bundleID).toBe('com.example.app');
46
- expect(parsed.teamID).toBe('TEAM123');
47
- expect(parsed.provisionedDevices).toEqual(['00008030-000A']);
48
- });
49
-
50
- test('skips unparseable certificates instead of failing the profile', () => {
51
- const parsed = parseProvisioningProfileBase64(
52
- profileBase64([btoa('not a certificate'), selfSignedCertificateBase64('1a')]),
53
- );
54
- expect(parsed.certificateSerialNumbers).toEqual(['1A']);
55
- });
56
- });
57
-
58
- describe('normalizeCertificateSerial', () => {
59
- test('uppercases and strips leading zeros', () => {
60
- expect(normalizeCertificateSerial('00ab12')).toBe('AB12');
61
- expect(normalizeCertificateSerial('AB12')).toBe('AB12');
62
- expect(normalizeCertificateSerial('')).toBeUndefined();
63
- expect(normalizeCertificateSerial(undefined)).toBeUndefined();
64
- });
65
- });