@onekeyfe/hd-transport-react-native 1.2.0-alpha.66 → 1.2.0-alpha.67

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/jest.config.js CHANGED
@@ -2,4 +2,9 @@ module.exports = {
2
2
  preset: '../../jest.config.js',
3
3
  testEnvironment: 'node',
4
4
  modulePathIgnorePatterns: ['node_modules', '<rootDir>/dist'],
5
+ moduleNameMapper: {
6
+ // The workspace symlinks resolve to prebuilt dist outputs that can lag behind src.
7
+ '^@onekeyfe/hd-transport$': '<rootDir>/../hd-transport/src/index.ts',
8
+ '^@onekeyfe/hd-shared$': '<rootDir>/../shared/src/index.ts',
9
+ },
5
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.0-alpha.66",
3
+ "version": "1.2.0-alpha.67",
4
4
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -20,11 +20,11 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-core": "1.2.0-alpha.66",
24
- "@onekeyfe/hd-shared": "1.2.0-alpha.66",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.66",
23
+ "@onekeyfe/hd-core": "1.2.0-alpha.67",
24
+ "@onekeyfe/hd-shared": "1.2.0-alpha.67",
25
+ "@onekeyfe/hd-transport": "1.2.0-alpha.67",
26
26
  "@onekeyfe/react-native-ble-utils": "^0.1.6",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "81629c086665bb92ce4d52087ae47f084976da79"
29
+ "gitHead": "5fea66e8c65a1fab2ccc099b2811c8749b2487e0"
30
30
  }
@@ -1,5 +1,3 @@
1
- import { Platform } from 'react-native';
2
-
3
1
  import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
4
2
 
5
3
  export default class BleTransport {
@@ -35,10 +33,6 @@ export default class BleTransport {
35
33
  }
36
34
 
37
35
  async writeWithRetry(data: string): Promise<void> {
38
- if (Platform.OS === 'ios' && this.writeCharacteristic.isWritableWithResponse) {
39
- await this.writeCharacteristic.writeWithResponse(data);
40
- return;
41
- }
42
36
  await this.writeCharacteristic.writeWithoutResponse(data);
43
37
  }
44
38
  }
@@ -1,16 +1,7 @@
1
1
  import { BleErrorCode } from 'react-native-ble-plx';
2
- import { Platform } from 'react-native';
3
2
 
4
3
  import BleTransport from '../BleTransport';
5
4
 
6
- jest.mock(
7
- 'react-native',
8
- () => ({
9
- Platform: { OS: 'ios' },
10
- }),
11
- { virtual: true }
12
- );
13
-
14
5
  jest.mock(
15
6
  'react-native-ble-plx',
16
7
  () => ({
@@ -28,18 +19,12 @@ jest.mock('@onekeyfe/hd-shared', () => ({
28
19
  }));
29
20
 
30
21
  describe('BleTransport side-effecting writes', () => {
31
- beforeEach(() => {
32
- Platform.OS = 'ios';
33
- });
34
-
35
22
  test('does not reconnect or replay after the device disconnects', async () => {
36
23
  const error = Object.assign(new Error('device disconnected'), {
37
24
  errorCode: BleErrorCode.DeviceDisconnected,
38
25
  });
39
26
  const writeCharacteristic = {
40
- isWritableWithResponse: true,
41
- writeWithResponse: jest.fn(() => Promise.reject(error)),
42
- writeWithoutResponse: jest.fn(() => Promise.resolve()),
27
+ writeWithoutResponse: jest.fn(() => Promise.reject(error)),
43
28
  };
44
29
  const device = {
45
30
  id: 'classic-id',
@@ -50,44 +35,7 @@ describe('BleTransport side-effecting writes', () => {
50
35
 
51
36
  await expect(transport.writeWithRetry('payload')).rejects.toBe(error);
52
37
 
53
- expect(writeCharacteristic.writeWithResponse).toHaveBeenCalledTimes(1);
38
+ expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
54
39
  expect(device.connect).not.toHaveBeenCalled();
55
40
  });
56
-
57
- test('keeps Android Protocol V1 writes on writeWithoutResponse', async () => {
58
- Platform.OS = 'android';
59
- const writeCharacteristic = {
60
- isWritableWithResponse: true,
61
- writeWithResponse: jest.fn(() => Promise.resolve()),
62
- writeWithoutResponse: jest.fn(() => Promise.resolve()),
63
- };
64
- const transport = new BleTransport(
65
- { id: 'classic-id' } as any,
66
- writeCharacteristic as any,
67
- {} as any
68
- );
69
-
70
- await transport.writeWithRetry('payload');
71
-
72
- expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
73
- expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledWith('payload');
74
- });
75
-
76
- test('uses writeWithoutResponse on iOS when the characteristic lacks response support', async () => {
77
- const writeCharacteristic = {
78
- isWritableWithResponse: false,
79
- writeWithResponse: jest.fn(() => Promise.resolve()),
80
- writeWithoutResponse: jest.fn(() => Promise.resolve()),
81
- };
82
- const transport = new BleTransport(
83
- { id: 'classic-id' } as any,
84
- writeCharacteristic as any,
85
- {} as any
86
- );
87
-
88
- await transport.writeWithRetry('payload');
89
-
90
- expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
91
- expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledWith('payload');
92
- });
93
41
  });
@@ -0,0 +1,281 @@
1
+ import { EventEmitter } from 'events';
2
+ import { BleErrorCode } from 'react-native-ble-plx';
3
+
4
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
5
+
6
+ import ReactNativeBleTransport, {
7
+ BLE_CONNECT_TIMEOUT_MS,
8
+ BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD,
9
+ BLE_GATT_SETUP_TIMEOUT_MS,
10
+ } from '../index';
11
+ import protocolV1Schema from './protocolV1SchemaFixture';
12
+
13
+ jest.mock(
14
+ 'react-native',
15
+ () => ({
16
+ Platform: { OS: 'ios', select: (spec: Record<string, unknown>) => spec.ios },
17
+ PermissionsAndroid: {
18
+ PERMISSIONS: {},
19
+ RESULTS: {},
20
+ request: jest.fn(),
21
+ requestMultiple: jest.fn(),
22
+ },
23
+ }),
24
+ { virtual: true }
25
+ );
26
+
27
+ jest.mock('react-native-ble-plx', () => ({
28
+ BleATTErrorCode: { InvalidHandle: 1, UnlikelyError: 14 },
29
+ BleError: Error,
30
+ BleErrorCode: {
31
+ DeviceDisconnected: 201,
32
+ OperationStartFailed: 601,
33
+ DeviceMTUChangeFailed: 401,
34
+ OperationCancelled: 2,
35
+ OperationTimedOut: 3,
36
+ DeviceAlreadyConnected: 203,
37
+ },
38
+ BleManager: jest.fn(),
39
+ ScanMode: { LowLatency: 2 },
40
+ }));
41
+
42
+ jest.mock('@onekeyfe/react-native-ble-utils', () => ({
43
+ __esModule: true,
44
+ default: {
45
+ getConnectedPeripherals: jest.fn(() => Promise.resolve([])),
46
+ getBondedPeripherals: jest.fn(() => Promise.resolve([])),
47
+ pairDevice: jest.fn(() => Promise.resolve()),
48
+ },
49
+ }));
50
+
51
+ const UUID = 'stalled-connect-device';
52
+
53
+ const flush = () =>
54
+ new Promise(resolve => {
55
+ setImmediate(resolve);
56
+ });
57
+
58
+ async function advanceUntil(settled: () => boolean, totalMs: number, stepMs = 250) {
59
+ for (let elapsed = 0; elapsed < totalMs; elapsed += stepMs) {
60
+ jest.advanceTimersByTime(stepMs);
61
+ // eslint-disable-next-line no-await-in-loop
62
+ await flush();
63
+ if (settled()) return;
64
+ }
65
+ throw new Error(`fake timers exhausted after ${totalMs}ms before the connect settled`);
66
+ }
67
+
68
+ /** A device whose native connect() never settles — the observed iOS failure mode. */
69
+ function createHarness(connectImpl: () => Promise<unknown>) {
70
+ const connect = jest.fn(connectImpl);
71
+ const writeCharacteristic = {
72
+ uuid: '00000002-0000-1000-8000-00805f9b34fb',
73
+ isWritableWithResponse: true,
74
+ };
75
+ const notifyCharacteristic = {
76
+ uuid: '00000003-0000-1000-8000-00805f9b34fb',
77
+ isNotifiable: true,
78
+ };
79
+ const device = {
80
+ id: UUID,
81
+ name: 'OneKey Classic',
82
+ localName: 'OneKey Classic',
83
+ serviceUUIDs: ['00000001-0000-1000-8000-00805f9b34fb'],
84
+ isConnected: jest.fn(() => Promise.resolve(false)),
85
+ cancelConnection: jest.fn(() => Promise.resolve()),
86
+ connect,
87
+ discoverAllServicesAndCharacteristics: jest.fn(() => Promise.resolve()),
88
+ characteristicsForService: jest.fn(() =>
89
+ Promise.resolve([writeCharacteristic, notifyCharacteristic])
90
+ ),
91
+ services: jest.fn(() => Promise.resolve([])),
92
+ onDisconnected: jest.fn(() => ({ remove: jest.fn() })),
93
+ };
94
+ const transport = new ReactNativeBleTransport({ scanTimeout: 1 });
95
+ const bleManager = {
96
+ devices: jest.fn(() => Promise.resolve([device])),
97
+ connectedDevices: jest.fn(() => Promise.resolve([])),
98
+ connectToDevice: jest.fn(connectImpl),
99
+ cancelTransaction: jest.fn(() => Promise.resolve()),
100
+ cancelDeviceConnection: jest.fn(() => Promise.resolve()),
101
+ onStateChange: jest.fn((listener: (state: string) => void) => {
102
+ // Dispatch asynchronously: subscribeBleOn wires its own cleanup after
103
+ // registering, so a synchronous callback would run before it is ready.
104
+ setImmediate(() => listener('PoweredOn'));
105
+ return { remove: jest.fn() };
106
+ }),
107
+ state: jest.fn(() => Promise.resolve('PoweredOn')),
108
+ startDeviceScan: jest.fn(),
109
+ stopDeviceScan: jest.fn(),
110
+ };
111
+ (transport as any).blePlxManager = bleManager;
112
+ transport.init(
113
+ { debug: jest.fn(), error: jest.fn(), warn: jest.fn() } as any,
114
+ new EventEmitter()
115
+ );
116
+ transport.configure(protocolV1Schema);
117
+ return { transport, device, bleManager, connect };
118
+ }
119
+
120
+ describe('BLE connect timeout', () => {
121
+ beforeAll(() => {
122
+ jest.useFakeTimers({ doNotFake: ['setImmediate', 'performance'] });
123
+ });
124
+
125
+ afterAll(() => {
126
+ jest.useRealTimers();
127
+ });
128
+
129
+ afterEach(() => {
130
+ jest.clearAllTimers();
131
+ jest.restoreAllMocks();
132
+ });
133
+
134
+ test('a native connect that never settles is bounded instead of blocking forever', async () => {
135
+ // iOS applies its own connect timeout on a serial queue; when that queue is busy
136
+ // the timeout never fires and acquire() blocks until the app-level 60s timeout.
137
+ const { transport } = createHarness(
138
+ () =>
139
+ new Promise(() => {
140
+ // never settles
141
+ })
142
+ );
143
+
144
+ const errors: Array<{ errorCode?: unknown }> = [];
145
+ let settled = false;
146
+ transport.acquire({ uuid: UUID }).catch(e => {
147
+ errors.push(e);
148
+ settled = true;
149
+ });
150
+ await flush();
151
+
152
+ await advanceUntil(() => settled, BLE_CONNECT_TIMEOUT_MS + 5000);
153
+
154
+ expect(errors).toHaveLength(1);
155
+ expect(errors[0]?.errorCode).toBe(HardwareErrorCode.BleConnectedError);
156
+ });
157
+
158
+ test('the connect budget leaves generous headroom over a healthy connect', () => {
159
+ // Healthy connects finish in ~2-3s (the native budget is 3s); this backstop only
160
+ // fires when the native timeout itself fails to.
161
+ expect(BLE_CONNECT_TIMEOUT_MS).toBeGreaterThanOrEqual(6000);
162
+ expect(BLE_CONNECT_TIMEOUT_MS).toBeLessThanOrEqual(12000);
163
+ });
164
+
165
+ test('a stalled connect is abandoned natively so the next attempt is not cancelled by it', async () => {
166
+ const { transport, bleManager } = createHarness(
167
+ () =>
168
+ new Promise(() => {
169
+ // never settles
170
+ })
171
+ );
172
+
173
+ let settled = false;
174
+ transport.acquire({ uuid: UUID }).catch(() => {
175
+ settled = true;
176
+ });
177
+ await flush();
178
+ await advanceUntil(() => settled, BLE_CONNECT_TIMEOUT_MS + 5000);
179
+
180
+ expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(UUID);
181
+ });
182
+
183
+ test('repeated stalled connects recreate the BLE manager', async () => {
184
+ const { transport, bleManager } = createHarness(
185
+ () =>
186
+ new Promise(() => {
187
+ // never settles
188
+ })
189
+ );
190
+ const destroy = jest.fn();
191
+ (bleManager as unknown as { destroy: jest.Mock }).destroy = destroy;
192
+
193
+ for (let attempt = 0; attempt < BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD; attempt += 1) {
194
+ let settled = false;
195
+ transport.acquire({ uuid: UUID }).catch(() => {
196
+ settled = true;
197
+ });
198
+ // eslint-disable-next-line no-await-in-loop
199
+ await flush();
200
+ // eslint-disable-next-line no-await-in-loop
201
+ await advanceUntil(() => settled, BLE_CONNECT_TIMEOUT_MS + 5000);
202
+ }
203
+
204
+ expect(destroy).toHaveBeenCalledTimes(1);
205
+ expect((transport as unknown as { blePlxManager?: unknown }).blePlxManager).toBeUndefined();
206
+ });
207
+
208
+ test('native connect timeouts contribute to the same manager reset budget', async () => {
209
+ const { transport, bleManager } = createHarness(() => Promise.resolve());
210
+ const destroy = jest.fn();
211
+ (bleManager as unknown as { destroy: jest.Mock }).destroy = destroy;
212
+ const nativeTimeout = Object.assign(new Error('Operation timed out'), {
213
+ errorCode: BleErrorCode.OperationTimedOut,
214
+ });
215
+
216
+ for (let attempt = 0; attempt < BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD; attempt += 1) {
217
+ // eslint-disable-next-line no-await-in-loop
218
+ await expect(
219
+ (transport as any).connectWithTimeout(UUID, () => Promise.reject(nativeTimeout))
220
+ ).rejects.toBe(nativeTimeout);
221
+ }
222
+
223
+ expect(destroy).toHaveBeenCalledTimes(1);
224
+ });
225
+
226
+ test('GATT discovery is bounded and abandons the native connection', async () => {
227
+ const { transport, device, bleManager } = createHarness(() => Promise.resolve());
228
+ device.discoverAllServicesAndCharacteristics.mockImplementation(
229
+ () =>
230
+ new Promise(() => {
231
+ // never settles
232
+ })
233
+ );
234
+
235
+ const errors: Array<{ errorCode?: unknown }> = [];
236
+ let settled = false;
237
+ (transport as any).resolveCharacteristicsWithTimeout(UUID, device).catch((error: unknown) => {
238
+ errors.push(error as { errorCode?: unknown });
239
+ settled = true;
240
+ });
241
+ await flush();
242
+ await advanceUntil(() => settled, BLE_GATT_SETUP_TIMEOUT_MS + 5000);
243
+
244
+ expect(errors).toHaveLength(1);
245
+ expect(errors[0]?.errorCode).toBe(HardwareErrorCode.BleConnectedError);
246
+ expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(UUID);
247
+ });
248
+
249
+ test('a successful GATT retry clears the timeout budget before an abandoned call settles', async () => {
250
+ const { transport, device, bleManager } = createHarness(() => Promise.resolve());
251
+ let resolveAbandonedDiscovery: (() => void) | undefined;
252
+ device.discoverAllServicesAndCharacteristics
253
+ .mockImplementationOnce(
254
+ () =>
255
+ new Promise<void>(resolve => {
256
+ resolveAbandonedDiscovery = resolve;
257
+ })
258
+ )
259
+ .mockResolvedValueOnce(undefined);
260
+
261
+ let firstSettled = false;
262
+ (transport as any).resolveCharacteristicsWithTimeout(UUID, device).catch(() => {
263
+ firstSettled = true;
264
+ });
265
+ await flush();
266
+ await advanceUntil(() => firstSettled, BLE_GATT_SETUP_TIMEOUT_MS + 5000);
267
+
268
+ await expect(
269
+ (transport as any).resolveCharacteristicsWithTimeout(UUID, device)
270
+ ).resolves.toMatchObject({
271
+ writeCharacteristic: expect.any(Object),
272
+ notifyCharacteristic: expect.any(Object),
273
+ });
274
+
275
+ resolveAbandonedDiscovery?.();
276
+ await flush();
277
+
278
+ expect(bleManager.cancelDeviceConnection).toHaveBeenCalledTimes(1);
279
+ expect((transport as any).connectionSetupTimeoutCounts.has(UUID)).toBe(false);
280
+ });
281
+ });
@@ -0,0 +1,132 @@
1
+ import ReactNativeBleTransport, { PROTOCOL_REPROBE_FALLBACK_ATTEMPTS } from '../index';
2
+ import protocolV1Schema from './protocolV1SchemaFixture';
3
+
4
+ jest.mock(
5
+ 'react-native',
6
+ () => ({
7
+ Platform: { OS: 'ios', select: (spec: Record<string, unknown>) => spec.ios },
8
+ PermissionsAndroid: {
9
+ PERMISSIONS: {},
10
+ RESULTS: {},
11
+ request: jest.fn(),
12
+ requestMultiple: jest.fn(),
13
+ },
14
+ }),
15
+ { virtual: true }
16
+ );
17
+
18
+ jest.mock('react-native-ble-plx', () => ({
19
+ BleATTErrorCode: { InvalidHandle: 1, UnlikelyError: 14 },
20
+ BleError: Error,
21
+ BleErrorCode: { DeviceDisconnected: 201, OperationStartFailed: 601 },
22
+ BleManager: jest.fn(),
23
+ ScanMode: { LowLatency: 2 },
24
+ }));
25
+
26
+ jest.mock('@onekeyfe/react-native-ble-utils', () => ({
27
+ __esModule: true,
28
+ default: {
29
+ getConnectedPeripherals: jest.fn(() => Promise.resolve([])),
30
+ getBondedPeripherals: jest.fn(() => Promise.resolve([])),
31
+ pairDevice: jest.fn(() => Promise.resolve()),
32
+ },
33
+ }));
34
+
35
+ const UUID = 'reprobe-device';
36
+
37
+ /** Drive detectProtocol with stubbed probes so we can observe which are attempted. */
38
+ function createHarness({ v1, v2 }: { v1: boolean; v2: boolean }) {
39
+ const transport = new ReactNativeBleTransport({});
40
+ transport.configure(protocolV1Schema);
41
+ const probeV1 = jest.fn(() => {
42
+ if (v1) {
43
+ (transport as any).deviceProtocol.set(UUID, 'V1');
44
+ }
45
+ return Promise.resolve(v1);
46
+ });
47
+ const probeV2 = jest.fn(() => {
48
+ if (v2) {
49
+ (transport as any).deviceProtocol.set(UUID, 'V2');
50
+ }
51
+ return Promise.resolve(v2);
52
+ });
53
+ (transport as any).probeProtocolV1 = probeV1;
54
+ (transport as any).probeProtocolV2 = probeV2;
55
+ (transport as any).resetProbeStateAfterProtocolProbe = jest.fn(() => Promise.resolve());
56
+ return { transport, probeV1, probeV2 };
57
+ }
58
+
59
+ const detect = (transport: ReactNativeBleTransport) =>
60
+ (transport as any).detectProtocol(UUID, undefined, undefined, () =>
61
+ Promise.resolve()
62
+ ) as Promise<string>;
63
+
64
+ describe('protocol re-probe after a known device goes away', () => {
65
+ test('an unknown device still probes both protocols', async () => {
66
+ const { transport, probeV1, probeV2 } = createHarness({ v1: false, v2: false });
67
+
68
+ await expect(detect(transport)).rejects.toBeDefined();
69
+
70
+ expect(probeV1).toHaveBeenCalledTimes(1);
71
+ expect(probeV2).toHaveBeenCalledTimes(1);
72
+ });
73
+
74
+ test('a device already known to speak V1 does not pay the V2 probe while it is away', async () => {
75
+ const known = createHarness({ v1: true, v2: false });
76
+ // First detection confirms V1 (this is the session that just talked to the device).
77
+ await expect(detect(known.transport)).resolves.toBe('V1');
78
+
79
+ // The device now reboots after a firmware install: V1 stops answering.
80
+ (known.transport as any).deviceProtocol.delete(UUID);
81
+ const probeV1 = jest.fn(() => Promise.resolve(false));
82
+ const probeV2 = jest.fn(() => Promise.resolve(false));
83
+ (known.transport as any).probeProtocolV1 = probeV1;
84
+ (known.transport as any).probeProtocolV2 = probeV2;
85
+
86
+ await expect(detect(known.transport)).rejects.toBeDefined();
87
+
88
+ expect(probeV1).toHaveBeenCalledTimes(1);
89
+ // The 10s Protocol V2 ping is pure waste for a device we just spoke V1 to.
90
+ expect(probeV2).not.toHaveBeenCalled();
91
+ });
92
+
93
+ test('after repeated failures it re-probes every protocol again', async () => {
94
+ const known = createHarness({ v1: true, v2: false });
95
+ await expect(detect(known.transport)).resolves.toBe('V1');
96
+
97
+ (known.transport as any).deviceProtocol.delete(UUID);
98
+ const probeV1 = jest.fn(() => Promise.resolve(false));
99
+ const probeV2 = jest.fn(() => Promise.resolve(false));
100
+ (known.transport as any).probeProtocolV1 = probeV1;
101
+ (known.transport as any).probeProtocolV2 = probeV2;
102
+
103
+ for (let attempt = 0; attempt < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS; attempt += 1) {
104
+ // eslint-disable-next-line no-await-in-loop
105
+ await expect(detect(known.transport)).rejects.toBeDefined();
106
+ }
107
+ expect(probeV2).not.toHaveBeenCalled();
108
+
109
+ // The device may genuinely have changed protocol, so the shortcut must expire.
110
+ await expect(detect(known.transport)).rejects.toBeDefined();
111
+ expect(probeV2).toHaveBeenCalled();
112
+ });
113
+
114
+ test('a successful detection clears the failure streak', async () => {
115
+ const known = createHarness({ v1: true, v2: false });
116
+ await expect(detect(known.transport)).resolves.toBe('V1');
117
+ (known.transport as any).deviceProtocol.delete(UUID);
118
+
119
+ const failingV1 = jest.fn(() => Promise.resolve(false));
120
+ (known.transport as any).probeProtocolV1 = failingV1;
121
+ await expect(detect(known.transport)).rejects.toBeDefined();
122
+
123
+ // Device comes back.
124
+ (known.transport as any).probeProtocolV1 = jest.fn(() => {
125
+ (known.transport as any).deviceProtocol.set(UUID, 'V1');
126
+ return Promise.resolve(true);
127
+ });
128
+ await expect(detect(known.transport)).resolves.toBe('V1');
129
+
130
+ expect((known.transport as any).protocolReprobeFailures.get(UUID)).toBeUndefined();
131
+ });
132
+ });
@@ -0,0 +1,39 @@
1
+ const protocolV1Schema = {
2
+ nested: {
3
+ Initialize: {
4
+ fields: {
5
+ session_id: { type: 'bytes', id: 1 },
6
+ derive_cardano: { type: 'bool', id: 3 },
7
+ },
8
+ },
9
+ GetFeatures: { fields: {} },
10
+ Success: {
11
+ fields: {
12
+ message: { type: 'string', id: 1 },
13
+ },
14
+ },
15
+ Ping: {
16
+ fields: {
17
+ message: { type: 'string', id: 1 },
18
+ button_protection: { type: 'bool', id: 2 },
19
+ },
20
+ },
21
+ FirmwareUpload: {
22
+ fields: {
23
+ payload: { rule: 'required', type: 'bytes', id: 1 },
24
+ hash: { type: 'bytes', id: 2 },
25
+ },
26
+ },
27
+ MessageType: {
28
+ values: {
29
+ MessageType_Initialize: 0,
30
+ MessageType_Ping: 1,
31
+ MessageType_Success: 2,
32
+ MessageType_FirmwareUpload: 7,
33
+ MessageType_GetFeatures: 55,
34
+ },
35
+ },
36
+ },
37
+ };
38
+
39
+ export default protocolV1Schema;