@onekeyfe/hd-transport-usb 1.2.0-alpha.7 → 1.2.0-alpha.71
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/__tests__/protocol-v2-link.test.ts +439 -0
- package/dist/index.d.ts +86 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +203 -160
- package/dist/transportLog.d.ts +2 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/jest.config.js +5 -0
- package/package.json +5 -4
- package/src/index.ts +262 -199
- package/src/transportLog.ts +1 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import transportPackage, { PROTOCOL_V2_CHANNEL_USB, ProtocolV2 } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
3
|
+
import NodeUsbTransport from '../src';
|
|
4
|
+
|
|
5
|
+
let mockUsbDevices: any[] = [];
|
|
6
|
+
|
|
7
|
+
jest.mock('usb', () => ({
|
|
8
|
+
getDeviceList: jest.fn(() => mockUsbDevices),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const { parseConfigure } = transportPackage;
|
|
12
|
+
|
|
13
|
+
const protocolV1Schema = {
|
|
14
|
+
nested: {
|
|
15
|
+
Initialize: { fields: {} },
|
|
16
|
+
Success: {
|
|
17
|
+
fields: {
|
|
18
|
+
message: { type: 'string', id: 1 },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
MessageType: {
|
|
22
|
+
values: {
|
|
23
|
+
MessageType_Initialize: 1,
|
|
24
|
+
MessageType_Success: 2,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const protocolV2Schema = {
|
|
31
|
+
nested: {
|
|
32
|
+
Ping: {
|
|
33
|
+
fields: {
|
|
34
|
+
message: { type: 'string', id: 1 },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
Success: {
|
|
38
|
+
fields: {
|
|
39
|
+
message: { type: 'string', id: 1 },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
MessageType: {
|
|
43
|
+
values: {
|
|
44
|
+
MessageType_Ping: 60206,
|
|
45
|
+
MessageType_Success: 60207,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const schemas = {
|
|
52
|
+
protocolV1: parseConfigure(protocolV1Schema),
|
|
53
|
+
protocolV2: parseConfigure(protocolV2Schema),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
type PendingRead = {
|
|
57
|
+
started: Promise<void>;
|
|
58
|
+
fail: (error?: Error) => void;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const createHarness = () => {
|
|
62
|
+
const path = '6136';
|
|
63
|
+
const responseQueue: Buffer[] = [];
|
|
64
|
+
const sentSeqs: number[] = [];
|
|
65
|
+
let cancelledTransferCount = 0;
|
|
66
|
+
let writeError: Error | undefined;
|
|
67
|
+
let holdNextRead:
|
|
68
|
+
| {
|
|
69
|
+
markStarted: () => void;
|
|
70
|
+
started: Promise<void>;
|
|
71
|
+
callback?: (error?: Error, data?: Buffer) => void;
|
|
72
|
+
}
|
|
73
|
+
| undefined;
|
|
74
|
+
|
|
75
|
+
const performInTransfer = (callback: (error?: Error, data?: Buffer) => void) => {
|
|
76
|
+
if (epIn.timeout === 50) {
|
|
77
|
+
callback(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (holdNextRead) {
|
|
81
|
+
const pending = holdNextRead;
|
|
82
|
+
holdNextRead = undefined;
|
|
83
|
+
pending.callback = callback;
|
|
84
|
+
pending.markStarted();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const response = responseQueue.shift();
|
|
88
|
+
if (!response) {
|
|
89
|
+
callback(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
callback(undefined, response);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const epIn = {
|
|
96
|
+
direction: 'in',
|
|
97
|
+
address: 0x81,
|
|
98
|
+
timeout: 30_000,
|
|
99
|
+
transfer: jest.fn((_length: number, callback: (error?: Error, data?: Buffer) => void) => {
|
|
100
|
+
performInTransfer(callback);
|
|
101
|
+
}),
|
|
102
|
+
makeTransfer: jest.fn(
|
|
103
|
+
(
|
|
104
|
+
_timeout: number,
|
|
105
|
+
callback: (error: Error | undefined, data: Buffer, actualLength: number) => void
|
|
106
|
+
) => {
|
|
107
|
+
let settled = false;
|
|
108
|
+
const finish = (error?: Error, data = Buffer.alloc(0)) => {
|
|
109
|
+
if (settled) return;
|
|
110
|
+
settled = true;
|
|
111
|
+
callback(error, data, data.length);
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
submit: jest.fn((buffer: Buffer) => {
|
|
115
|
+
performInTransfer((error, data) => {
|
|
116
|
+
if (error) {
|
|
117
|
+
finish(error);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
data?.copy(buffer);
|
|
121
|
+
finish(undefined, buffer.subarray(0, data?.length ?? 0));
|
|
122
|
+
});
|
|
123
|
+
}),
|
|
124
|
+
cancel: jest.fn(() => {
|
|
125
|
+
cancelledTransferCount += 1;
|
|
126
|
+
finish(new Error('LIBUSB_TRANSFER_CANCELLED'));
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const performOutTransfer = (data: Buffer, callback: (error?: Error) => void) => {
|
|
134
|
+
const seq = data[6];
|
|
135
|
+
sentSeqs.push(seq);
|
|
136
|
+
if (writeError) {
|
|
137
|
+
const error = writeError;
|
|
138
|
+
writeError = undefined;
|
|
139
|
+
callback(error);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
responseQueue.push(
|
|
143
|
+
Buffer.from(
|
|
144
|
+
ProtocolV2.encodeFrame(
|
|
145
|
+
schemas,
|
|
146
|
+
'Success',
|
|
147
|
+
{ message: 'ok' },
|
|
148
|
+
{ router: PROTOCOL_V2_CHANNEL_USB, seq }
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
callback();
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const epOut = {
|
|
156
|
+
direction: 'out',
|
|
157
|
+
address: 0x01,
|
|
158
|
+
timeout: 30_000,
|
|
159
|
+
transfer: jest.fn((data: Buffer, callback: (error?: Error) => void) => {
|
|
160
|
+
performOutTransfer(data, callback);
|
|
161
|
+
}),
|
|
162
|
+
makeTransfer: jest.fn(
|
|
163
|
+
(
|
|
164
|
+
_timeout: number,
|
|
165
|
+
callback: (error: Error | undefined, data: Buffer, actualLength: number) => void
|
|
166
|
+
) => {
|
|
167
|
+
let settled = false;
|
|
168
|
+
const finish = (error: Error | undefined, data: Buffer) => {
|
|
169
|
+
if (settled) return;
|
|
170
|
+
settled = true;
|
|
171
|
+
callback(error, data, data.length);
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
submit: jest.fn((data: Buffer) => {
|
|
175
|
+
performOutTransfer(data, error => finish(error, data));
|
|
176
|
+
}),
|
|
177
|
+
cancel: jest.fn(() => {
|
|
178
|
+
cancelledTransferCount += 1;
|
|
179
|
+
finish(new Error('LIBUSB_TRANSFER_CANCELLED'), Buffer.alloc(0));
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const iface = {
|
|
187
|
+
descriptor: { bInterfaceClass: 0xff, bInterfaceNumber: 0 },
|
|
188
|
+
endpoints: [epIn, epOut],
|
|
189
|
+
claim: jest.fn(),
|
|
190
|
+
release: jest.fn((closeEndpointsOrCallback: boolean | (() => void), callback?: () => void) => {
|
|
191
|
+
if (typeof closeEndpointsOrCallback === 'function') closeEndpointsOrCallback();
|
|
192
|
+
else callback?.();
|
|
193
|
+
}),
|
|
194
|
+
isKernelDriverActive: jest.fn(() => false),
|
|
195
|
+
detachKernelDriver: jest.fn(),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const device = {
|
|
199
|
+
busNumber: 1,
|
|
200
|
+
deviceAddress: 2,
|
|
201
|
+
timeout: 30_000,
|
|
202
|
+
deviceDescriptor: {
|
|
203
|
+
idVendor: 0x1209,
|
|
204
|
+
idProduct: 0x4f4a,
|
|
205
|
+
iSerialNumber: 1,
|
|
206
|
+
},
|
|
207
|
+
interfaces: [iface],
|
|
208
|
+
open: jest.fn(),
|
|
209
|
+
close: jest.fn(),
|
|
210
|
+
getStringDescriptor: jest.fn(
|
|
211
|
+
(_index: number, callback: (error?: Error, value?: string) => void) =>
|
|
212
|
+
callback(undefined, path)
|
|
213
|
+
),
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
mockUsbDevices = [device];
|
|
217
|
+
const transport = new NodeUsbTransport();
|
|
218
|
+
transport.init({ debug: jest.fn(), error: jest.fn() });
|
|
219
|
+
transport.configure(protocolV1Schema);
|
|
220
|
+
transport.configureProtocolV2(protocolV2Schema);
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
transport,
|
|
224
|
+
path,
|
|
225
|
+
device,
|
|
226
|
+
iface,
|
|
227
|
+
epIn,
|
|
228
|
+
epOut,
|
|
229
|
+
sentSeqs,
|
|
230
|
+
getCancelledTransferCount: () => cancelledTransferCount,
|
|
231
|
+
async acquire() {
|
|
232
|
+
await transport.enumerate();
|
|
233
|
+
await transport.acquire({ path, expectedProtocol: 'V2' });
|
|
234
|
+
},
|
|
235
|
+
failNextWrite(error: Error) {
|
|
236
|
+
writeError = error;
|
|
237
|
+
},
|
|
238
|
+
holdRead(): PendingRead {
|
|
239
|
+
let markStarted: () => void = () => undefined;
|
|
240
|
+
const started = new Promise<void>(resolve => {
|
|
241
|
+
markStarted = resolve;
|
|
242
|
+
});
|
|
243
|
+
const pending = { markStarted, started, callback: undefined };
|
|
244
|
+
holdNextRead = pending;
|
|
245
|
+
return {
|
|
246
|
+
started,
|
|
247
|
+
fail(error = new Error('read released after test')) {
|
|
248
|
+
pending.callback?.(error);
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
describe('NodeUsbTransport Protocol V2 link lifecycle', () => {
|
|
256
|
+
test('falls back to Protocol V2 when a cached V1 hint is stale', async () => {
|
|
257
|
+
const transport = new NodeUsbTransport() as any;
|
|
258
|
+
const events: string[] = [];
|
|
259
|
+
transport.probeProtocolV1 = jest.fn().mockImplementation(() => {
|
|
260
|
+
events.push('probe-v1');
|
|
261
|
+
return Promise.resolve(false);
|
|
262
|
+
});
|
|
263
|
+
transport.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
264
|
+
events.push('reset');
|
|
265
|
+
return Promise.resolve();
|
|
266
|
+
});
|
|
267
|
+
transport.probeProtocolV2 = jest.fn().mockImplementation(() => {
|
|
268
|
+
events.push('probe-v2');
|
|
269
|
+
return Promise.resolve(true);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
await expect(transport.detectProtocol('pro-usb', undefined, 'V1')).resolves.toBe('V2');
|
|
273
|
+
|
|
274
|
+
expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
|
|
275
|
+
expect(transport.getProtocolType('pro-usb')).toBe('V2');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
|
|
279
|
+
const transport = new NodeUsbTransport() as any;
|
|
280
|
+
transport.invalidateAllProtocolV2UsbLinks = jest.fn().mockResolvedValue(undefined);
|
|
281
|
+
const schemaSource = JSON.stringify(protocolV2Schema);
|
|
282
|
+
|
|
283
|
+
transport.configureProtocolV2(schemaSource);
|
|
284
|
+
transport.configureProtocolV2(schemaSource);
|
|
285
|
+
|
|
286
|
+
expect(transport.invalidateAllProtocolV2UsbLinks).not.toHaveBeenCalled();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test('does not retry a native transfer cancelled by probe cleanup', () => {
|
|
290
|
+
const { transport } = createHarness();
|
|
291
|
+
|
|
292
|
+
expect((transport as any).isRetryableError(new Error('LIBUSB_TRANSFER_CANCELLED'))).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('cancels pending native transfers before closing a timed-out protocol probe', async () => {
|
|
296
|
+
const harness = createHarness();
|
|
297
|
+
const { transport, path } = harness;
|
|
298
|
+
await harness.acquire();
|
|
299
|
+
const cancelActiveTransfers = jest.spyOn(transport as any, 'cancelActiveTransfers');
|
|
300
|
+
const closeOpenDevice = jest.spyOn(transport as any, 'closeOpenDevice');
|
|
301
|
+
|
|
302
|
+
await (transport as any).resetConnectionAfterProbe(path);
|
|
303
|
+
|
|
304
|
+
expect(cancelActiveTransfers).toHaveBeenCalledWith(path);
|
|
305
|
+
expect(closeOpenDevice).toHaveBeenCalledWith(path);
|
|
306
|
+
expect(cancelActiveTransfers.mock.invocationCallOrder[0]).toBeLessThan(
|
|
307
|
+
closeOpenDevice.mock.invocationCallOrder[0]
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('releases the USB interface when protocol detection rejects acquire', async () => {
|
|
312
|
+
const harness = createHarness();
|
|
313
|
+
const { transport, path, device, iface } = harness;
|
|
314
|
+
await transport.enumerate();
|
|
315
|
+
device.close.mockClear();
|
|
316
|
+
iface.release.mockClear();
|
|
317
|
+
jest
|
|
318
|
+
.spyOn(transport as any, 'detectProtocol')
|
|
319
|
+
.mockRejectedValueOnce(new Error('terminal protocol probe failure'));
|
|
320
|
+
|
|
321
|
+
await expect(transport.acquire({ path })).rejects.toMatchObject({
|
|
322
|
+
errorCode: expect.any(Number),
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
expect(iface.release).toHaveBeenCalledTimes(1);
|
|
326
|
+
expect(device.close).toHaveBeenCalledTimes(1);
|
|
327
|
+
expect((transport as any).openDevices.has(path)).toBe(false);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test('stop releases an acquired USB interface even before a Protocol V2 call', async () => {
|
|
331
|
+
const harness = createHarness();
|
|
332
|
+
const { transport, path, device, iface } = harness;
|
|
333
|
+
await transport.enumerate();
|
|
334
|
+
await transport.acquire({ path, expectedProtocol: 'V2' });
|
|
335
|
+
device.close.mockClear();
|
|
336
|
+
iface.release.mockClear();
|
|
337
|
+
|
|
338
|
+
await transport.stop();
|
|
339
|
+
|
|
340
|
+
expect(iface.release).toHaveBeenCalledTimes(1);
|
|
341
|
+
expect(device.close).toHaveBeenCalledTimes(1);
|
|
342
|
+
expect(transport.getProtocolType(path)).toBeUndefined();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test('actively probes explicit Protocol V2 during bootloader reconnect', async () => {
|
|
346
|
+
const harness = createHarness();
|
|
347
|
+
const { transport, path, epOut } = harness;
|
|
348
|
+
|
|
349
|
+
await transport.enumerate();
|
|
350
|
+
await transport.acquire({ path, expectedProtocol: 'V2' });
|
|
351
|
+
|
|
352
|
+
expect(epOut.makeTransfer).toHaveBeenCalledTimes(1);
|
|
353
|
+
expect(transport.getProtocolType(path)).toBe('V2');
|
|
354
|
+
await transport.release(path);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test('keeps seq across calls and actively probed reacquire', async () => {
|
|
358
|
+
const harness = createHarness();
|
|
359
|
+
const { transport, path, sentSeqs } = harness;
|
|
360
|
+
|
|
361
|
+
await harness.acquire();
|
|
362
|
+
await transport.call(path, 'Ping', { message: 'first' });
|
|
363
|
+
await transport.release(path);
|
|
364
|
+
await harness.acquire();
|
|
365
|
+
await transport.call(path, 'Ping', { message: 'second' });
|
|
366
|
+
|
|
367
|
+
expect(sentSeqs).toEqual([1, 2, 3, 4]);
|
|
368
|
+
await transport.release(path);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test('does not resend a Protocol V2 frame after transferOut fails', async () => {
|
|
372
|
+
const harness = createHarness();
|
|
373
|
+
const { transport, path, epOut } = harness;
|
|
374
|
+
await harness.acquire();
|
|
375
|
+
epOut.makeTransfer.mockClear();
|
|
376
|
+
harness.failNextWrite(new Error('LIBUSB_ERROR_IO'));
|
|
377
|
+
|
|
378
|
+
await expect(transport.call(path, 'Ping', { message: 'write-failure' })).rejects.toThrow(
|
|
379
|
+
'LIBUSB_ERROR_IO'
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
expect(epOut.makeTransfer).toHaveBeenCalledTimes(1);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('rejects a pending read when release invalidates the link', async () => {
|
|
386
|
+
const harness = createHarness();
|
|
387
|
+
const { transport, path } = harness;
|
|
388
|
+
await harness.acquire();
|
|
389
|
+
const pendingRead = harness.holdRead();
|
|
390
|
+
|
|
391
|
+
const call = transport.call(path, 'Ping', { message: 'pending' }, { timeoutMs: 5000 });
|
|
392
|
+
const outcome = call.then(
|
|
393
|
+
() => 'resolved',
|
|
394
|
+
error => error.message
|
|
395
|
+
);
|
|
396
|
+
await pendingRead.started;
|
|
397
|
+
await transport.release(path);
|
|
398
|
+
const settled = await Promise.race([
|
|
399
|
+
outcome,
|
|
400
|
+
new Promise<string>(resolve => {
|
|
401
|
+
setTimeout(() => resolve('still pending'), 50);
|
|
402
|
+
}),
|
|
403
|
+
]);
|
|
404
|
+
pendingRead.fail();
|
|
405
|
+
|
|
406
|
+
expect(settled).not.toBe('still pending');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test('stop cancels an in-flight native USB transfer before releasing the interface', async () => {
|
|
410
|
+
const harness = createHarness();
|
|
411
|
+
const { transport, path } = harness;
|
|
412
|
+
await harness.acquire();
|
|
413
|
+
const pendingRead = harness.holdRead();
|
|
414
|
+
|
|
415
|
+
const call = transport.call(path, 'Ping', { message: 'pending' }, { timeoutMs: 5000 });
|
|
416
|
+
await pendingRead.started;
|
|
417
|
+
await transport.stop();
|
|
418
|
+
|
|
419
|
+
await expect(call).rejects.toThrow();
|
|
420
|
+
expect(harness.getCancelledTransferCount()).toBe(1);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('keeps the cursor after a response timeout rebuilds the USB connection', async () => {
|
|
424
|
+
const harness = createHarness();
|
|
425
|
+
const { transport, path, sentSeqs } = harness;
|
|
426
|
+
await harness.acquire();
|
|
427
|
+
const pendingRead = harness.holdRead();
|
|
428
|
+
|
|
429
|
+
await expect(
|
|
430
|
+
transport.call(path, 'Ping', { message: 'timeout' }, { timeoutMs: 20 })
|
|
431
|
+
).rejects.toThrow('20ms');
|
|
432
|
+
pendingRead.fail();
|
|
433
|
+
await harness.acquire();
|
|
434
|
+
await transport.call(path, 'Ping', { message: 'after-timeout' });
|
|
435
|
+
|
|
436
|
+
expect(sentSeqs).toEqual([1, 2, 3, 4]);
|
|
437
|
+
await transport.release(path);
|
|
438
|
+
});
|
|
439
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,51 +1,120 @@
|
|
|
1
1
|
import * as transport from '@onekeyfe/hd-transport';
|
|
2
|
-
import transport__default, { OneKeyDeviceInfo, AcquireInput, TransportCallOptions, ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
import transport__default, { ProtocolV2UsbTransportBase, OneKeyDeviceInfo, AcquireInput, TransportCallOptions, ProtocolV2Schemas, ProtocolV2CallContext, ProtocolType } from '@onekeyfe/hd-transport';
|
|
3
3
|
import EventEmitter from 'events';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Node.js USB Transport — complete transport implementation using libusb.
|
|
7
|
+
*
|
|
8
|
+
* Unlike the old UsbPlugin (which was a LowlevelTransportSharedPlugin piped
|
|
9
|
+
* through LowlevelTransport), this class is a standalone transport that handles
|
|
10
|
+
* both protocol encoding/decoding and USB I/O directly.
|
|
11
|
+
*
|
|
12
|
+
* Modeled after WebUsbTransport.
|
|
13
|
+
*/
|
|
14
|
+
declare class NodeUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
6
15
|
messages: ReturnType<typeof transport__default.parseConfigure> | undefined;
|
|
16
|
+
/** Protobuf schema for Protocol V2 transports. */
|
|
7
17
|
messagesV2: ReturnType<typeof transport__default.parseConfigure> | undefined;
|
|
18
|
+
private protocolV2SchemaSource;
|
|
8
19
|
name: string;
|
|
9
20
|
version: string;
|
|
10
21
|
configured: boolean;
|
|
11
22
|
isOutdated: boolean;
|
|
12
23
|
Log?: any;
|
|
13
24
|
emitter?: EventEmitter;
|
|
25
|
+
/** serial → bus id, built during enumerate */
|
|
14
26
|
private serialToBusId;
|
|
27
|
+
/** path → opened device state */
|
|
15
28
|
private openDevices;
|
|
29
|
+
/** Per-path protocol type detected by active wire-level probe. */
|
|
16
30
|
private deviceProtocol;
|
|
17
|
-
|
|
18
|
-
private protocolV2Sessions;
|
|
19
|
-
private protocolV2ReadTimeouts;
|
|
31
|
+
/** per-path reconnect lock to prevent concurrent reconnects */
|
|
20
32
|
private reconnectLocks;
|
|
33
|
+
/**
|
|
34
|
+
* Retain the low-level Transfer so release()/stop() can cancel native pending reads.
|
|
35
|
+
* Endpoint.transfer() hides it and may keep the CLI alive after output completes.
|
|
36
|
+
*/
|
|
37
|
+
private activeTransfers;
|
|
38
|
+
/** set to true when cancel() is called; checked by retry loops */
|
|
21
39
|
private cancelled;
|
|
40
|
+
constructor();
|
|
41
|
+
/**
|
|
42
|
+
* Initialize transport.
|
|
43
|
+
* Signature matches the Transport.init interface (logger, emitter).
|
|
44
|
+
*/
|
|
22
45
|
init(logger: any, emitter?: EventEmitter): Promise<string>;
|
|
23
46
|
configure(signedData: any): Promise<void>;
|
|
24
47
|
configureProtocolV2(signedData: any): void;
|
|
25
48
|
listen(): void;
|
|
26
|
-
stop(): void
|
|
49
|
+
stop(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Low-level post (send only, no response). Not used by NodeUsbTransport
|
|
52
|
+
* since call() handles the full send+receive cycle, but required by the Transport interface.
|
|
53
|
+
*/
|
|
27
54
|
post(path: string, name: string, data: Record<string, unknown>): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Low-level read (receive only). Not used by NodeUsbTransport
|
|
57
|
+
* since call() handles the full send+receive cycle, but required by the Transport interface.
|
|
58
|
+
*/
|
|
28
59
|
read(path: string): Promise<{
|
|
29
60
|
message: {
|
|
30
61
|
[key: string]: any;
|
|
31
62
|
};
|
|
32
63
|
type: string;
|
|
33
64
|
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Enumerate connected OneKey USB devices.
|
|
67
|
+
* Opens each device briefly to read its serial number (used as `path`),
|
|
68
|
+
* then closes it. acquire() re-opens from a fresh getDeviceList().
|
|
69
|
+
*/
|
|
34
70
|
enumerate(): Promise<OneKeyDeviceInfo[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Acquire device — open USB device, claim interface, return path (string).
|
|
73
|
+
*/
|
|
35
74
|
acquire(input: AcquireInput): Promise<string>;
|
|
75
|
+
/**
|
|
76
|
+
* Release device — release interface and close.
|
|
77
|
+
*/
|
|
36
78
|
release(path: string, _onclose?: boolean): Promise<void>;
|
|
79
|
+
private cancelActiveTransfers;
|
|
80
|
+
private createTrackedTransfer;
|
|
81
|
+
private transferInOnce;
|
|
82
|
+
private transferOutOnce;
|
|
37
83
|
private closeOpenDevice;
|
|
84
|
+
/**
|
|
85
|
+
* Call device method — encode protobuf, send packets, receive response.
|
|
86
|
+
* This is the core method that replaces LowlevelTransport's call + UsbPlugin's send/receive.
|
|
87
|
+
*/
|
|
38
88
|
call(path: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<transport.MessageFromOneKey>;
|
|
39
89
|
private callProtocolV1;
|
|
40
90
|
cancel(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Get the current open device for a path, re-resolving from the map
|
|
93
|
+
* so callers always use a fresh reference after reconnect.
|
|
94
|
+
*/
|
|
41
95
|
private getOpenDevice;
|
|
42
96
|
private getErrorMessage;
|
|
43
97
|
private isRetryableError;
|
|
44
98
|
private isUsbTransferTimeout;
|
|
45
99
|
private getDeviceInterface;
|
|
100
|
+
/**
|
|
101
|
+
* Reconnect device before retrying a failed transfer (aligned with WebUsbTransport).
|
|
102
|
+
* Uses per-path lock to prevent concurrent reconnects to the same device.
|
|
103
|
+
*/
|
|
46
104
|
private reconnectForRetry;
|
|
105
|
+
/**
|
|
106
|
+
* Send all encoded chunks to the device with retry.
|
|
107
|
+
* If a chunk fails and triggers reconnect, the entire sequence restarts
|
|
108
|
+
* from chunk 0 because the device resets protocol state on reconnect.
|
|
109
|
+
*/
|
|
47
110
|
private sendAllChunksWithRetry;
|
|
111
|
+
/**
|
|
112
|
+
* USB IN transfer with retry and reconnect (aligned with WebUsbTransport).
|
|
113
|
+
*/
|
|
48
114
|
private transferInWithRetry;
|
|
115
|
+
/**
|
|
116
|
+
* Open a USB device by path (serial number), claim interface, cache endpoints.
|
|
117
|
+
*/
|
|
49
118
|
private openDevice;
|
|
50
119
|
private drainStaleInput;
|
|
51
120
|
private createProtocolMismatchError;
|
|
@@ -55,9 +124,18 @@ declare class NodeUsbTransport {
|
|
|
55
124
|
private withProtocolReadTimeout;
|
|
56
125
|
private probeProtocolV1;
|
|
57
126
|
private probeProtocolV2;
|
|
58
|
-
|
|
59
|
-
|
|
127
|
+
protected getProtocolV2UsbSchemas(): ProtocolV2Schemas;
|
|
128
|
+
protected getProtocolV2UsbLogger(): any;
|
|
129
|
+
protected writeProtocolV2UsbPacket(path: string, frame: Uint8Array, _context: ProtocolV2CallContext): Promise<void>;
|
|
130
|
+
protected readProtocolV2UsbPacket(path: string, _context: ProtocolV2CallContext): Promise<Uint8Array>;
|
|
131
|
+
protected resetProtocolV2UsbNativeLink(path: string, _reason: string): Promise<void>;
|
|
132
|
+
protected onProtocolV2UsbLinkInvalidated(path: string, reason: string): void;
|
|
133
|
+
protected createProtocolV2UsbTimeoutError(name: string, timeoutMs: number): Error;
|
|
60
134
|
private callProtocolV2;
|
|
135
|
+
/**
|
|
136
|
+
* Receive a complete protobuf response from the device.
|
|
137
|
+
* Reads 64-byte packets, strips 0x3F marker, reassembles into hex string.
|
|
138
|
+
*/
|
|
61
139
|
private receiveData;
|
|
62
140
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
63
141
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|