@onekeyfe/hd-core 1.2.2-alpha.113 → 1.2.2-alpha.114
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__/core-error-output.test.ts +8 -2
- package/__tests__/search-devices.test.ts +96 -9
- package/dist/api/SearchDevices.d.ts +2 -15
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/TransportManager.d.ts +2 -0
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/index.js +109 -49
- package/package.json +4 -4
- package/src/api/SearchDevices.ts +85 -37
- package/src/core/index.ts +38 -24
- package/src/data-manager/TransportManager.ts +14 -3
|
@@ -62,7 +62,13 @@ describe('Core 错误输出边界', () => {
|
|
|
62
62
|
const request = core.handleMessage({
|
|
63
63
|
id: 11,
|
|
64
64
|
type: IFRAME.CALL,
|
|
65
|
-
payload: {
|
|
65
|
+
payload: {
|
|
66
|
+
method: 'getDeviceState',
|
|
67
|
+
connectId: 'serial-V2',
|
|
68
|
+
retryCount: 1,
|
|
69
|
+
pollIntervalTime: 1,
|
|
70
|
+
timeout: 1000,
|
|
71
|
+
},
|
|
66
72
|
} as never);
|
|
67
73
|
await new Promise(resolve => {
|
|
68
74
|
setTimeout(resolve, 0);
|
|
@@ -87,7 +93,7 @@ describe('Core 错误输出边界', () => {
|
|
|
87
93
|
setTimeout(resolve, 0);
|
|
88
94
|
});
|
|
89
95
|
expect(search).toHaveBeenCalledTimes(1);
|
|
90
|
-
expect(initialize).toHaveBeenCalledTimes(shouldCancel ? 0 :
|
|
96
|
+
expect(initialize).toHaveBeenCalledTimes(shouldCancel ? 0 : 2);
|
|
91
97
|
} finally {
|
|
92
98
|
finishSearch?.();
|
|
93
99
|
await core.dispose();
|
|
@@ -4,6 +4,7 @@ jest.mock('../src/data-manager/TransportManager', () => ({
|
|
|
4
4
|
__esModule: true,
|
|
5
5
|
default: {
|
|
6
6
|
configure: jest.fn(),
|
|
7
|
+
ensureInitialized: jest.fn(),
|
|
7
8
|
},
|
|
8
9
|
}));
|
|
9
10
|
|
|
@@ -21,9 +22,9 @@ jest.mock('../src/device/DevicePool', () => ({
|
|
|
21
22
|
},
|
|
22
23
|
}));
|
|
23
24
|
|
|
24
|
-
const transportManagerMock: {
|
|
25
|
-
|
|
26
|
-
);
|
|
25
|
+
const transportManagerMock: {
|
|
26
|
+
default: { configure: jest.Mock; ensureInitialized: jest.Mock };
|
|
27
|
+
} = jest.requireMock('../src/data-manager/TransportManager');
|
|
27
28
|
const devicePoolMock: {
|
|
28
29
|
DevicePool: { getDevices: jest.Mock; getDeviceByPath: jest.Mock };
|
|
29
30
|
} = jest.requireMock('../src/device/DevicePool');
|
|
@@ -33,8 +34,10 @@ const dataManagerMock: {
|
|
|
33
34
|
isBleConnect: jest.Mock;
|
|
34
35
|
};
|
|
35
36
|
} = jest.requireMock('../src/data-manager');
|
|
36
|
-
const { configure: mockConfigureTransport } =
|
|
37
|
-
|
|
37
|
+
const { configure: mockConfigureTransport, ensureInitialized: mockEnsureInitialized } =
|
|
38
|
+
transportManagerMock.default;
|
|
39
|
+
const { getDevices: mockGetDevices, getDeviceByPath: mockGetDeviceByPath } =
|
|
40
|
+
devicePoolMock.DevicePool;
|
|
38
41
|
const { isBleConnect: mockIsBleConnect } = dataManagerMock.DataManager;
|
|
39
42
|
|
|
40
43
|
describe('SearchDevices', () => {
|
|
@@ -52,32 +55,116 @@ describe('SearchDevices', () => {
|
|
|
52
55
|
features: { protocol },
|
|
53
56
|
toMessageObject: () => ({ connectId: `serial-${protocol}` }),
|
|
54
57
|
}));
|
|
55
|
-
|
|
58
|
+
mockGetDeviceByPath.mockImplementation(
|
|
56
59
|
(path: string) => devices[['usb-V1', 'usb-V2'].indexOf(path)]
|
|
57
60
|
);
|
|
61
|
+
const extraDevice = {
|
|
62
|
+
toMessageObject: () => ({ connectId: 'not-initialized' }),
|
|
63
|
+
};
|
|
64
|
+
mockGetDevices.mockResolvedValue({
|
|
65
|
+
devices: { 'not-initialized': extraDevice },
|
|
66
|
+
deviceList: [extraDevice],
|
|
67
|
+
});
|
|
58
68
|
const method = new SearchDevices({
|
|
59
69
|
id: 1,
|
|
60
70
|
payload: { method: 'searchDevices' },
|
|
61
71
|
} as never);
|
|
62
72
|
method.init();
|
|
63
73
|
method.context = {
|
|
64
|
-
requestQueue: {
|
|
74
|
+
requestQueue: {
|
|
75
|
+
getRequestTasksId: () => [2],
|
|
76
|
+
getRequestTasksIdByConnectId: (connectId: string) =>
|
|
77
|
+
connectId === 'usb-V1' || connectId === 'usb-V2' ? [2] : [],
|
|
78
|
+
},
|
|
65
79
|
} as never;
|
|
66
80
|
method.connector = {
|
|
67
81
|
enumerate: jest.fn().mockResolvedValue({
|
|
68
|
-
descriptors: [
|
|
82
|
+
descriptors: [
|
|
83
|
+
{ path: 'usb-V1', commType: 'webusb' },
|
|
84
|
+
{ path: 'usb-V2', commType: 'webusb' },
|
|
85
|
+
{ path: 'not-initialized', commType: 'webusb' },
|
|
86
|
+
],
|
|
69
87
|
}),
|
|
70
88
|
} as never;
|
|
71
89
|
|
|
72
90
|
await expect(method.run()).resolves.toEqual([
|
|
73
91
|
{ connectId: 'serial-V1' },
|
|
74
92
|
{ connectId: 'serial-V2' },
|
|
93
|
+
{ connectId: 'not-initialized' },
|
|
75
94
|
]);
|
|
76
|
-
expect(mockGetDevices).
|
|
95
|
+
expect(mockGetDevices).toHaveBeenCalledTimes(1);
|
|
96
|
+
expect(mockGetDevices).toHaveBeenCalledWith(
|
|
97
|
+
[{ path: 'not-initialized', commType: 'webusb' }],
|
|
98
|
+
'not-initialized',
|
|
99
|
+
{
|
|
100
|
+
connectProtocol: undefined,
|
|
101
|
+
forceProtocolDetection: true,
|
|
102
|
+
refreshRuntimeState: true,
|
|
103
|
+
}
|
|
104
|
+
);
|
|
77
105
|
expect(mockConfigureTransport).not.toHaveBeenCalled();
|
|
78
106
|
}
|
|
79
107
|
);
|
|
80
108
|
|
|
109
|
+
test('owned WebUSB cache miss still reports a SearchDevice without probing', async () => {
|
|
110
|
+
mockGetDeviceByPath.mockReturnValue(undefined);
|
|
111
|
+
const usbDevice = { vendorId: 0x1209, productId: 0x4f4c };
|
|
112
|
+
const method = new SearchDevices({
|
|
113
|
+
id: 1,
|
|
114
|
+
payload: { method: 'searchDevices' },
|
|
115
|
+
} as never);
|
|
116
|
+
method.init();
|
|
117
|
+
method.context = {
|
|
118
|
+
requestQueue: {
|
|
119
|
+
getRequestTasksId: () => [2],
|
|
120
|
+
getRequestTasksIdByConnectId: (connectId: string) => (connectId === 'serial-V2' ? [2] : []),
|
|
121
|
+
},
|
|
122
|
+
} as never;
|
|
123
|
+
method.connector = {
|
|
124
|
+
enumerate: jest.fn().mockResolvedValue({
|
|
125
|
+
descriptors: [
|
|
126
|
+
{
|
|
127
|
+
path: 'serial-V2',
|
|
128
|
+
device: usbDevice,
|
|
129
|
+
commType: 'webusb',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
}),
|
|
133
|
+
} as never;
|
|
134
|
+
|
|
135
|
+
await expect(method.run()).resolves.toEqual([
|
|
136
|
+
{
|
|
137
|
+
connectId: 'serial-V2',
|
|
138
|
+
uuid: 'serial-V2',
|
|
139
|
+
serialNo: 'serial-V2',
|
|
140
|
+
deviceId: null,
|
|
141
|
+
deviceType: 'unknown',
|
|
142
|
+
name: 'serial-V2',
|
|
143
|
+
commType: 'webusb',
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
expect(mockGetDevices).not.toHaveBeenCalled();
|
|
147
|
+
expect(mockConfigureTransport).not.toHaveBeenCalled();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('searchDevices resolves empty when WebUSB bring-up is unavailable', async () => {
|
|
151
|
+
mockEnsureInitialized.mockRejectedValueOnce(
|
|
152
|
+
new Error('WebUSB is not supported by current browsers')
|
|
153
|
+
);
|
|
154
|
+
const method = new SearchDevices({
|
|
155
|
+
id: 1,
|
|
156
|
+
payload: { method: 'searchDevices' },
|
|
157
|
+
} as never);
|
|
158
|
+
method.init();
|
|
159
|
+
method.connector = {
|
|
160
|
+
enumerate: jest.fn().mockResolvedValue({ descriptors: [] }),
|
|
161
|
+
} as never;
|
|
162
|
+
|
|
163
|
+
await expect(method.run()).resolves.toEqual([]);
|
|
164
|
+
expect(mockEnsureInitialized).toHaveBeenCalled();
|
|
165
|
+
expect(mockConfigureTransport).toHaveBeenCalledTimes(1);
|
|
166
|
+
});
|
|
167
|
+
|
|
81
168
|
test('搜索忽略调用方协议并主动探测,单个无响应设备不阻断后续结果', async () => {
|
|
82
169
|
const unresponsiveDescriptor = {
|
|
83
170
|
path: 'stale-usb-device',
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
import { BaseMethod } from './BaseMethod';
|
|
2
|
+
import type { SearchDevice } from '../types/device';
|
|
2
3
|
import type DeviceConnector from '../device/DeviceConnector';
|
|
3
4
|
export default class SearchDevices extends BaseMethod {
|
|
4
5
|
connector?: DeviceConnector;
|
|
5
6
|
init(): void;
|
|
6
|
-
run(): Promise<
|
|
7
|
-
connectId: string;
|
|
8
|
-
serialNo: null;
|
|
9
|
-
uuid: string;
|
|
10
|
-
deviceId: null;
|
|
11
|
-
name: string | null;
|
|
12
|
-
deviceType: import("..").IDeviceType;
|
|
13
|
-
commType: import("packages/hd-transport/dist").OneKeyDeviceCommType;
|
|
14
|
-
path: string;
|
|
15
|
-
session?: string | null | undefined;
|
|
16
|
-
debugSession?: string | null | undefined;
|
|
17
|
-
debug: boolean;
|
|
18
|
-
id: string;
|
|
19
|
-
protocolType?: import("packages/hd-transport/dist").ProtocolType | undefined;
|
|
20
|
-
}[]>;
|
|
7
|
+
run(): Promise<SearchDevice[]>;
|
|
21
8
|
}
|
|
22
9
|
//# sourceMappingURL=SearchDevices.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchDevices.d.ts","sourceRoot":"","sources":["../../src/api/SearchDevices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,OAAO,KAAK,eAAe,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"SearchDevices.d.ts","sourceRoot":"","sources":["../../src/api/SearchDevices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,eAAe,MAAM,2BAA2B,CAAC;AAuC7D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,IAAI;IAME,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAsFrC"}
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAEL,KAAK,6BAA6B,EAElC,KAAK,YAAY,EAIlB,MAAM,wBAAwB,CAAC;AAiChC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB1C,OAAO,eAAe,MAAM,2BAA2B,CAAC;AAYxD,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,UAAU,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAmD,MAAM,WAAW,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAYpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AA0E7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBA0FvE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAEL,KAAK,6BAA6B,EAElC,KAAK,YAAY,EAIlB,MAAM,wBAAwB,CAAC;AAiChC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB1C,OAAO,eAAe,MAAM,2BAA2B,CAAC;AAYxD,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,UAAU,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAmD,MAAM,WAAW,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAYpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AA0E7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBA0FvE,CAAC;AA2FF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3F;AAunBD,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAWpF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAoB/E;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAQlF;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,WAEzD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,WAI3D;AAiKD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAMtF;AA2MD,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SA4G9D,CAAC;AA8GF,eAAO,MAAM,qBAAqB,gFAejC,CAAC;AAiLF,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,YAAY;IAC5C,OAAO,CAAC,cAAc,CAAoB;IAE1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IAEtC,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO,CAAC,cAAc,CAAC,CAAgB;IAGvC,OAAO,CAAC,sBAAsB,CAAoC;IAElE,OAAO,CAAC,iBAAiB,CAAoB;;IAS7C,OAAO,CAAC,cAAc;IAuChB,aAAa,CAAC,OAAO,EAAE,WAAW;IAuExC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAOV,gBAAgB;CAiC/B;AAED,eAAO,MAAM,QAAQ,YAIpB,CAAC;AAEF,eAAO,MAAM,aAAa,uBAYzB,CAAC;AAMF,eAAO,MAAM,IAAI,aACL,eAAe,aACd,GAAG,WACL,6BAA6B,8BAiBvC,CAAC;AAEF,eAAO,MAAM,eAAe;SAKrB,eAAe,CAAC,KAAK,CAAC;eAChB,GAAG;;UASf,CAAC"}
|
|
@@ -6,9 +6,11 @@ export default class TransportManager {
|
|
|
6
6
|
static defaultMessages: JSON | Record<string, any>;
|
|
7
7
|
static currentMessages: JSON | Record<string, any>;
|
|
8
8
|
static reactNativeInit: boolean;
|
|
9
|
+
static webUsbInit: boolean;
|
|
9
10
|
static protocolV1MessageSchema: ProtocolV1MessageSchema;
|
|
10
11
|
static plugin: LowlevelTransportSharedPlugin | null;
|
|
11
12
|
static load(): void;
|
|
13
|
+
static ensureInitialized(): Promise<void>;
|
|
12
14
|
static configure(): Promise<void>;
|
|
13
15
|
static reconfigure(features?: Features): Promise<void>;
|
|
14
16
|
static setTransport(TransportConstructor: any, plugin?: LowlevelTransportSharedPlugin): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransportManager.d.ts","sourceRoot":"","sources":["../../src/data-manager/TransportManager.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,6BAA6B,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAgBzC,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;IAE5B,MAAM,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEnD,MAAM,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEnD,MAAM,CAAC,eAAe,UAAS;IAE/B,MAAM,CAAC,uBAAuB,EAAE,uBAAuB,CAAqB;IAE5E,MAAM,CAAC,MAAM,EAAE,6BAA6B,GAAG,IAAI,CAAQ;IAE3D,MAAM,CAAC,IAAI;
|
|
1
|
+
{"version":3,"file":"TransportManager.d.ts","sourceRoot":"","sources":["../../src/data-manager/TransportManager.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,6BAA6B,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAgBzC,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;IAE5B,MAAM,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEnD,MAAM,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEnD,MAAM,CAAC,eAAe,UAAS;IAE/B,MAAM,CAAC,UAAU,UAAS;IAE1B,MAAM,CAAC,uBAAuB,EAAE,uBAAuB,CAAqB;IAE5E,MAAM,CAAC,MAAM,EAAE,6BAA6B,GAAG,IAAI,CAAQ;IAE3D,MAAM,CAAC,IAAI;WAQE,iBAAiB;WAUjB,SAAS;WAmDT,WAAW,CAAC,QAAQ,CAAC,EAAE,QAAQ;IAyB5C,MAAM,CAAC,YAAY,CAAC,oBAAoB,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,6BAA6B;IA6BrF,MAAM,CAAC,YAAY;mBAIE,2BAA2B;IAShD,MAAM,CAAC,kBAAkB;IAIzB,MAAM,CAAC,kBAAkB;IAIzB,MAAM,CAAC,0BAA0B;CAGlC"}
|
package/dist/index.js
CHANGED
|
@@ -43705,6 +43705,18 @@ class TransportManager {
|
|
|
43705
43705
|
this.defaultMessages = DataManager.getProtobufMessages();
|
|
43706
43706
|
this.currentMessages = this.defaultMessages;
|
|
43707
43707
|
this.protocolV1MessageSchema = 'v1CurrentSchema';
|
|
43708
|
+
this.webUsbInit = false;
|
|
43709
|
+
}
|
|
43710
|
+
static ensureInitialized() {
|
|
43711
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
43712
|
+
const env = DataManager.getSettings('env');
|
|
43713
|
+
if (env !== 'webusb' && env !== 'desktop-webusb')
|
|
43714
|
+
return;
|
|
43715
|
+
if (this.webUsbInit)
|
|
43716
|
+
return;
|
|
43717
|
+
yield this.transport.init(WebUsbLogger, DevicePool.emitter);
|
|
43718
|
+
this.webUsbInit = true;
|
|
43719
|
+
});
|
|
43708
43720
|
}
|
|
43709
43721
|
static configure() {
|
|
43710
43722
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -43733,7 +43745,7 @@ class TransportManager {
|
|
|
43733
43745
|
yield this.transport.init(WebBleLogger, DevicePool.emitter);
|
|
43734
43746
|
}
|
|
43735
43747
|
else if (env === 'webusb' || env === 'desktop-webusb') {
|
|
43736
|
-
yield this.
|
|
43748
|
+
yield this.ensureInitialized();
|
|
43737
43749
|
}
|
|
43738
43750
|
else {
|
|
43739
43751
|
yield this.transport.init(HttpLogger);
|
|
@@ -43816,6 +43828,7 @@ class TransportManager {
|
|
|
43816
43828
|
}
|
|
43817
43829
|
}
|
|
43818
43830
|
TransportManager.reactNativeInit = false;
|
|
43831
|
+
TransportManager.webUsbInit = false;
|
|
43819
43832
|
TransportManager.protocolV1MessageSchema = 'v1CurrentSchema';
|
|
43820
43833
|
TransportManager.plugin = null;
|
|
43821
43834
|
|
|
@@ -47263,6 +47276,28 @@ class PreInitialize extends BaseMethod {
|
|
|
47263
47276
|
}
|
|
47264
47277
|
|
|
47265
47278
|
const Log$e = getLogger(exports.LoggerNames.DevicePool);
|
|
47279
|
+
const isOwnedByActiveWebUsbRequest = (descriptor, requestQueue) => {
|
|
47280
|
+
if (!requestQueue)
|
|
47281
|
+
return false;
|
|
47282
|
+
const keys = [descriptor.path, descriptor.id].filter((key) => typeof key === 'string' && key.length > 0);
|
|
47283
|
+
return keys.some(key => requestQueue.getRequestTasksIdByConnectId(key).length > 0);
|
|
47284
|
+
};
|
|
47285
|
+
const toSearchDeviceFromDescriptor = (descriptor) => {
|
|
47286
|
+
const connectId = descriptor.path || descriptor.id || null;
|
|
47287
|
+
return {
|
|
47288
|
+
connectId,
|
|
47289
|
+
uuid: connectId !== null && connectId !== void 0 ? connectId : '',
|
|
47290
|
+
serialNo: connectId,
|
|
47291
|
+
deviceId: null,
|
|
47292
|
+
deviceType: hdShared.EDeviceType.Unknown,
|
|
47293
|
+
name: descriptor.name || connectId || '',
|
|
47294
|
+
commType: descriptor.commType,
|
|
47295
|
+
};
|
|
47296
|
+
};
|
|
47297
|
+
const toSearchDevice = (device) => {
|
|
47298
|
+
const message = device.toMessageObject();
|
|
47299
|
+
return message ? message : null;
|
|
47300
|
+
};
|
|
47266
47301
|
class SearchDevices extends BaseMethod {
|
|
47267
47302
|
init() {
|
|
47268
47303
|
this.useDevice = false;
|
|
@@ -47273,9 +47308,18 @@ class SearchDevices extends BaseMethod {
|
|
|
47273
47308
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
47274
47309
|
return __awaiter(this, void 0, void 0, function* () {
|
|
47275
47310
|
const env = DataManager.getSettings('env');
|
|
47276
|
-
const
|
|
47277
|
-
|
|
47278
|
-
|
|
47311
|
+
const isWebUsb = env === 'webusb' || env === 'desktop-webusb';
|
|
47312
|
+
const requestQueue = (_a = this.context) === null || _a === void 0 ? void 0 : _a.requestQueue;
|
|
47313
|
+
const hasActiveWebUsbRequest = isWebUsb && ((_b = requestQueue === null || requestQueue === void 0 ? void 0 : requestQueue.getRequestTasksId().length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
47314
|
+
if (isWebUsb) {
|
|
47315
|
+
try {
|
|
47316
|
+
yield TransportManager.ensureInitialized();
|
|
47317
|
+
}
|
|
47318
|
+
catch (error) {
|
|
47319
|
+
Log$e.debug('WebUSB bring-up unavailable', error);
|
|
47320
|
+
}
|
|
47321
|
+
}
|
|
47322
|
+
if (!hasActiveWebUsbRequest)
|
|
47279
47323
|
yield TransportManager.configure();
|
|
47280
47324
|
const deviceDiff = yield ((_c = this.connector) === null || _c === void 0 ? void 0 : _c.enumerate());
|
|
47281
47325
|
const devicesDescriptor = (_d = deviceDiff === null || deviceDiff === void 0 ? void 0 : deviceDiff.descriptors) !== null && _d !== void 0 ? _d : [];
|
|
@@ -47293,30 +47337,35 @@ class SearchDevices extends BaseMethod {
|
|
|
47293
47337
|
}
|
|
47294
47338
|
return devices;
|
|
47295
47339
|
}
|
|
47296
|
-
if (useCachedWebUsbDevices) {
|
|
47297
|
-
return devicesDescriptor.flatMap(descriptor => {
|
|
47298
|
-
const device = DevicePool.getDeviceByPath(descriptor.path);
|
|
47299
|
-
return (device === null || device === void 0 ? void 0 : device.features) ? [device.toMessageObject()] : [];
|
|
47300
|
-
});
|
|
47301
|
-
}
|
|
47302
47340
|
const deviceList = [];
|
|
47303
47341
|
for (const descriptor of devicesDescriptor) {
|
|
47304
|
-
|
|
47305
|
-
const
|
|
47306
|
-
|
|
47307
|
-
|
|
47308
|
-
refreshRuntimeState: true,
|
|
47309
|
-
});
|
|
47310
|
-
deviceList.push(...result.deviceList);
|
|
47342
|
+
if (hasActiveWebUsbRequest && isOwnedByActiveWebUsbRequest(descriptor, requestQueue)) {
|
|
47343
|
+
const cached = DevicePool.getDeviceByPath(descriptor.path);
|
|
47344
|
+
const message = (cached === null || cached === void 0 ? void 0 : cached.features) ? toSearchDevice(cached) : null;
|
|
47345
|
+
deviceList.push(message !== null && message !== void 0 ? message : toSearchDeviceFromDescriptor(descriptor));
|
|
47311
47346
|
}
|
|
47312
|
-
|
|
47313
|
-
|
|
47314
|
-
|
|
47315
|
-
|
|
47316
|
-
|
|
47347
|
+
else {
|
|
47348
|
+
try {
|
|
47349
|
+
const result = yield DevicePool.getDevices([descriptor], descriptor.path, {
|
|
47350
|
+
connectProtocol: undefined,
|
|
47351
|
+
forceProtocolDetection: true,
|
|
47352
|
+
refreshRuntimeState: true,
|
|
47353
|
+
});
|
|
47354
|
+
for (const device of result.deviceList) {
|
|
47355
|
+
const message = toSearchDevice(device);
|
|
47356
|
+
if (message)
|
|
47357
|
+
deviceList.push(message);
|
|
47358
|
+
}
|
|
47359
|
+
}
|
|
47360
|
+
catch (error) {
|
|
47361
|
+
const errorCode = error && typeof error === 'object' && 'errorCode' in error
|
|
47362
|
+
? error.errorCode
|
|
47363
|
+
: undefined;
|
|
47364
|
+
Log$e.debug('Skip unavailable device during search', Object.assign({ path: descriptor.path }, (errorCode !== undefined ? { errorCode } : {})));
|
|
47365
|
+
}
|
|
47317
47366
|
}
|
|
47318
47367
|
}
|
|
47319
|
-
return deviceList
|
|
47368
|
+
return deviceList;
|
|
47320
47369
|
});
|
|
47321
47370
|
}
|
|
47322
47371
|
}
|
|
@@ -66033,6 +66082,7 @@ const waitForPendingPromise = (connectId, getPrePendingCallPromise, removePrePen
|
|
|
66033
66082
|
Log.debug('pre pending call promise before call method, wait for it');
|
|
66034
66083
|
let timer;
|
|
66035
66084
|
let timedOut = false;
|
|
66085
|
+
let completed = false;
|
|
66036
66086
|
try {
|
|
66037
66087
|
yield Promise.race([
|
|
66038
66088
|
pendingPromise,
|
|
@@ -66043,11 +66093,13 @@ const waitForPendingPromise = (connectId, getPrePendingCallPromise, removePrePen
|
|
|
66043
66093
|
}, PRE_PENDING_CALL_TIMEOUT_MS);
|
|
66044
66094
|
}),
|
|
66045
66095
|
]);
|
|
66096
|
+
completed = !timedOut;
|
|
66046
66097
|
}
|
|
66047
66098
|
finally {
|
|
66048
66099
|
if (timer)
|
|
66049
66100
|
clearTimeout(timer);
|
|
66050
|
-
|
|
66101
|
+
if (timedOut || completed)
|
|
66102
|
+
removePrePendingCallPromise === null || removePrePendingCallPromise === void 0 ? void 0 : removePrePendingCallPromise(connectId, pendingPromise);
|
|
66051
66103
|
}
|
|
66052
66104
|
if (timedOut) {
|
|
66053
66105
|
Log.warn('pre pending call promise timed out before call method', { connectId });
|
|
@@ -66100,10 +66152,20 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
|
|
|
66100
66152
|
const pollingId = pollingManager.start(connectId);
|
|
66101
66153
|
const env = DataManager.getSettings('env');
|
|
66102
66154
|
const connect = () => ensureConnected(context, method, connectId, pollingId, method.abortSignal);
|
|
66103
|
-
|
|
66104
|
-
|
|
66105
|
-
|
|
66106
|
-
:
|
|
66155
|
+
if (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)) {
|
|
66156
|
+
const connectPromise = context.methodSynchronize(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
66157
|
+
var _k;
|
|
66158
|
+
if ((_k = method.abortSignal) === null || _k === void 0 ? void 0 : _k.aborted) {
|
|
66159
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
|
|
66160
|
+
}
|
|
66161
|
+
return connect();
|
|
66162
|
+
}), 'webusb-discovery');
|
|
66163
|
+
connectPromise.catch(() => undefined);
|
|
66164
|
+
device = yield requestQueue.waitForTask(task, () => connectPromise);
|
|
66165
|
+
}
|
|
66166
|
+
else {
|
|
66167
|
+
device = yield connect();
|
|
66168
|
+
}
|
|
66107
66169
|
if ((_e = method.abortSignal) === null || _e === void 0 ? void 0 : _e.aborted) {
|
|
66108
66170
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
|
|
66109
66171
|
}
|
|
@@ -66168,12 +66230,12 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
|
|
|
66168
66230
|
return waitForPendingPromise((_a = method.connectId) !== null && _a !== void 0 ? _a : '', getPrePendingCallPromise, removePrePendingCallPromise);
|
|
66169
66231
|
});
|
|
66170
66232
|
const inner = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
66171
|
-
var
|
|
66233
|
+
var _l, _m;
|
|
66172
66234
|
method.assertProtocolSupported(device.getProtocol(), device.getCurrentFirmwareType());
|
|
66173
66235
|
protocolV2Operation = device.isProtocolV2();
|
|
66174
66236
|
const versionRange = device.getCurrentMethodVersionRange(type => method.getVersionRange()[type]);
|
|
66175
|
-
const currentFirmwareVersion = (
|
|
66176
|
-
const currentBleVersion = (
|
|
66237
|
+
const currentFirmwareVersion = (_l = device.getCurrentFirmwareVersionString()) !== null && _l !== void 0 ? _l : '0.0.0';
|
|
66238
|
+
const currentBleVersion = (_m = device.getCurrentBLEFirmwareVersionString()) !== null && _m !== void 0 ? _m : '0.0.0';
|
|
66177
66239
|
const deviceFirmwareType = device.getCurrentFirmwareType();
|
|
66178
66240
|
let newVersionStatus;
|
|
66179
66241
|
if (device.features && !device.isProtocolV2()) {
|
|
@@ -66251,7 +66313,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
|
|
|
66251
66313
|
}
|
|
66252
66314
|
},
|
|
66253
66315
|
prepare: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
66254
|
-
var
|
|
66316
|
+
var _o, _p, _q, _r, _s, _t;
|
|
66255
66317
|
if (method.deviceId && method.checkDeviceId && !deviceIdCheckedDuringUnlockPreflight) {
|
|
66256
66318
|
const isSameDeviceID = yield checkLiveDeviceId(device, method.deviceId);
|
|
66257
66319
|
if (!isSameDeviceID) {
|
|
@@ -66271,7 +66333,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
|
|
|
66271
66333
|
require: support.require,
|
|
66272
66334
|
});
|
|
66273
66335
|
}
|
|
66274
|
-
const passphraseStateSafety = yield device.checkPassphraseStateSafety((
|
|
66336
|
+
const passphraseStateSafety = yield device.checkPassphraseStateSafety((_o = method.payload) === null || _o === void 0 ? void 0 : _o.passphraseState, (_p = method.payload) === null || _p === void 0 ? void 0 : _p.useEmptyPassphrase, (_q = method.payload) === null || _q === void 0 ? void 0 : _q.skipPassphraseCheck, resolveDeriveCardano(method), (_r = method.protocolV2UnlockContext) === null || _r === void 0 ? void 0 : _r.preflightMainPinSelected);
|
|
66275
66337
|
checkPassphraseEnableState(method, device.features);
|
|
66276
66338
|
if (!passphraseStateSafety) {
|
|
66277
66339
|
DevicePool.clearDeviceCache(method.payload.connectId);
|
|
@@ -66289,7 +66351,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
|
|
|
66289
66351
|
? e
|
|
66290
66352
|
: hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'open safety check failed.');
|
|
66291
66353
|
}
|
|
66292
|
-
(
|
|
66354
|
+
(_t = (_s = method.device) === null || _s === void 0 ? void 0 : _s.commands) === null || _t === void 0 ? void 0 : _t.checkDisposed();
|
|
66293
66355
|
}),
|
|
66294
66356
|
});
|
|
66295
66357
|
messageResponse = createResponseMessage(method.responseID, true, response);
|
|
@@ -66644,10 +66706,11 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
|
|
|
66644
66706
|
const POLL_INTERVAL_TIME = (method.payload && method.payload.pollIntervalTime) || 1000;
|
|
66645
66707
|
const TIME_OUT = (method.payload && method.payload.timeout) || 10000;
|
|
66646
66708
|
let timer = null;
|
|
66709
|
+
let lastInitializeError;
|
|
66647
66710
|
Log.debug(`EnsureConnected function start, MAX_RETRY_COUNT=${MAX_RETRY_COUNT}, POLL_INTERVAL_TIME=${POLL_INTERVAL_TIME} `);
|
|
66648
66711
|
const poll = (time = POLL_INTERVAL_TIME) => __awaiter(void 0, void 0, void 0, function* () {
|
|
66649
66712
|
return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
|
|
66650
|
-
var
|
|
66713
|
+
var _u;
|
|
66651
66714
|
const abort = () => {
|
|
66652
66715
|
if (abortSignal && abortSignal.aborted) {
|
|
66653
66716
|
if (timer) {
|
|
@@ -66676,8 +66739,10 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
|
|
|
66676
66739
|
Log.debug('EnsureConnected function try count: ', tryCount, ' poll interval time: ', time);
|
|
66677
66740
|
try {
|
|
66678
66741
|
yield initDeviceList(method);
|
|
66742
|
+
lastInitializeError = undefined;
|
|
66679
66743
|
}
|
|
66680
66744
|
catch (error) {
|
|
66745
|
+
lastInitializeError = error;
|
|
66681
66746
|
Log.debug('device list error: ', error);
|
|
66682
66747
|
if ([
|
|
66683
66748
|
hdShared.HardwareErrorCode.BridgeNotInstalled,
|
|
@@ -66690,15 +66755,7 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
|
|
|
66690
66755
|
}
|
|
66691
66756
|
if (error.errorCode === hdShared.HardwareErrorCode.TransportNotConfigured) {
|
|
66692
66757
|
yield TransportManager.configure();
|
|
66693
|
-
|
|
66694
|
-
else {
|
|
66695
|
-
const env = DataManager.getSettings('env');
|
|
66696
|
-
if (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)) {
|
|
66697
|
-
if (timer)
|
|
66698
|
-
clearTimeout(timer);
|
|
66699
|
-
reject(error);
|
|
66700
|
-
return;
|
|
66701
|
-
}
|
|
66758
|
+
lastInitializeError = undefined;
|
|
66702
66759
|
}
|
|
66703
66760
|
}
|
|
66704
66761
|
if (abort()) {
|
|
@@ -66781,13 +66838,16 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
|
|
|
66781
66838
|
clearTimeout(timer);
|
|
66782
66839
|
}
|
|
66783
66840
|
Log.debug('EnsureConnected get to max try count, will return: ', tryCount);
|
|
66784
|
-
|
|
66841
|
+
const preserveWebUsbInitError = DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env);
|
|
66842
|
+
const needsPermissionPrompt = DataManager.isBrowserWebUsb(env) && !((_u = method.payload) === null || _u === void 0 ? void 0 : _u.skipWebDevicePrompt);
|
|
66843
|
+
const fallbackError = needsPermissionPrompt
|
|
66844
|
+
? hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission)
|
|
66845
|
+
: hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
|
|
66846
|
+
const errorToReject = preserveWebUsbInitError && lastInitializeError ? lastInitializeError : fallbackError;
|
|
66847
|
+
if (needsPermissionPrompt && errorToReject === fallbackError) {
|
|
66785
66848
|
postMessage(createUiMessage(UI_REQUEST.WEB_DEVICE_PROMPT_ACCESS_PERMISSION));
|
|
66786
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission));
|
|
66787
|
-
}
|
|
66788
|
-
else {
|
|
66789
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound));
|
|
66790
66849
|
}
|
|
66850
|
+
reject(errorToReject);
|
|
66791
66851
|
return;
|
|
66792
66852
|
}
|
|
66793
66853
|
if (abort()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.2-alpha.
|
|
3
|
+
"version": "1.2.2-alpha.114",
|
|
4
4
|
"description": "Core processes and APIs for communicating with OneKey hardware devices.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-shared": "1.2.2-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.2-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.2-alpha.114",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.2-alpha.114",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"buffer": "^6.0.3",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"@types/w3c-web-usb": "^1.0.10",
|
|
47
47
|
"@types/web-bluetooth": "^0.0.21"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "6490382eeeb6bda264b71c7e9bf96143840ee1cb"
|
|
50
50
|
}
|
package/src/api/SearchDevices.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalizePro2BleAdvertisementName } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, canonicalizePro2BleAdvertisementName } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from './BaseMethod';
|
|
4
4
|
import TransportManager from '../data-manager/TransportManager';
|
|
@@ -6,10 +6,46 @@ import { DataManager } from '../data-manager';
|
|
|
6
6
|
import { LoggerNames, getDeviceTypeByBleName, getLogger } from '../utils';
|
|
7
7
|
import { DevicePool } from '../device/DevicePool';
|
|
8
8
|
|
|
9
|
+
import type { SearchDevice } from '../types/device';
|
|
9
10
|
import type DeviceConnector from '../device/DeviceConnector';
|
|
11
|
+
import type { OneKeyDeviceInfo as DeviceDescriptor } from '@onekeyfe/hd-transport';
|
|
12
|
+
import type { Device } from '../device/Device';
|
|
10
13
|
|
|
11
14
|
const Log = getLogger(LoggerNames.DevicePool);
|
|
12
15
|
|
|
16
|
+
type RequestQueueLookup = {
|
|
17
|
+
getRequestTasksIdByConnectId: (connectId: string) => number[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const isOwnedByActiveWebUsbRequest = (
|
|
21
|
+
descriptor: DeviceDescriptor,
|
|
22
|
+
requestQueue?: RequestQueueLookup
|
|
23
|
+
) => {
|
|
24
|
+
if (!requestQueue) return false;
|
|
25
|
+
const keys = [descriptor.path, descriptor.id].filter(
|
|
26
|
+
(key): key is string => typeof key === 'string' && key.length > 0
|
|
27
|
+
);
|
|
28
|
+
return keys.some(key => requestQueue.getRequestTasksIdByConnectId(key).length > 0);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const toSearchDeviceFromDescriptor = (descriptor: DeviceDescriptor): SearchDevice => {
|
|
32
|
+
const connectId = descriptor.path || descriptor.id || null;
|
|
33
|
+
return {
|
|
34
|
+
connectId,
|
|
35
|
+
uuid: connectId ?? '',
|
|
36
|
+
serialNo: connectId,
|
|
37
|
+
deviceId: null,
|
|
38
|
+
deviceType: EDeviceType.Unknown,
|
|
39
|
+
name: descriptor.name || connectId || '',
|
|
40
|
+
commType: descriptor.commType,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const toSearchDevice = (device: Device): SearchDevice | null => {
|
|
45
|
+
const message = device.toMessageObject();
|
|
46
|
+
return message ? (message as SearchDevice) : null;
|
|
47
|
+
};
|
|
48
|
+
|
|
13
49
|
export default class SearchDevices extends BaseMethod {
|
|
14
50
|
connector?: DeviceConnector;
|
|
15
51
|
|
|
@@ -19,14 +55,22 @@ export default class SearchDevices extends BaseMethod {
|
|
|
19
55
|
this.skipForceUpdateCheck = true;
|
|
20
56
|
}
|
|
21
57
|
|
|
22
|
-
async run() {
|
|
58
|
+
async run(): Promise<SearchDevice[]> {
|
|
23
59
|
const env = DataManager.getSettings('env');
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
// business request owns the
|
|
29
|
-
|
|
60
|
+
const isWebUsb = env === 'webusb' || env === 'desktop-webusb';
|
|
61
|
+
const requestQueue = this.context?.requestQueue;
|
|
62
|
+
const hasActiveWebUsbRequest = isWebUsb && (requestQueue?.getRequestTasksId().length ?? 0) > 0;
|
|
63
|
+
// Bring up WebUSB even when schema configuration is deferred while a
|
|
64
|
+
// business request owns the discovery lock. Failures here used to be
|
|
65
|
+
// swallowed by configure(); keep searchDevices resolving empty.
|
|
66
|
+
if (isWebUsb) {
|
|
67
|
+
try {
|
|
68
|
+
await TransportManager.ensureInitialized();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
Log.debug('WebUSB bring-up unavailable', error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!hasActiveWebUsbRequest) await TransportManager.configure();
|
|
30
74
|
const deviceDiff = await this.connector?.enumerate();
|
|
31
75
|
const devicesDescriptor = deviceDiff?.descriptors ?? [];
|
|
32
76
|
|
|
@@ -35,7 +79,7 @@ export default class SearchDevices extends BaseMethod {
|
|
|
35
79
|
* to avoid device pairing
|
|
36
80
|
*/
|
|
37
81
|
if (DataManager.isBleConnect(env)) {
|
|
38
|
-
const devices = [];
|
|
82
|
+
const devices: SearchDevice[] = [];
|
|
39
83
|
const seenIds = new Set<string>();
|
|
40
84
|
|
|
41
85
|
for (const device of devicesDescriptor) {
|
|
@@ -60,37 +104,41 @@ export default class SearchDevices extends BaseMethod {
|
|
|
60
104
|
return devices;
|
|
61
105
|
}
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
return devicesDescriptor.flatMap(descriptor => {
|
|
65
|
-
const device = DevicePool.getDeviceByPath(descriptor.path);
|
|
66
|
-
return device?.features ? [device.toMessageObject()] : [];
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const deviceList = [];
|
|
107
|
+
const deviceList: SearchDevice[] = [];
|
|
71
108
|
for (const descriptor of devicesDescriptor) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
if (hasActiveWebUsbRequest && isOwnedByActiveWebUsbRequest(descriptor, requestQueue)) {
|
|
110
|
+
const cached = DevicePool.getDeviceByPath(descriptor.path);
|
|
111
|
+
const message = cached?.features ? toSearchDevice(cached) : null;
|
|
112
|
+
// Do not probe a path an in-flight request already owns. A cache miss
|
|
113
|
+
// (passphrase switch / initSession) is still a connected device.
|
|
114
|
+
deviceList.push(message ?? toSearchDeviceFromDescriptor(descriptor));
|
|
115
|
+
} else {
|
|
116
|
+
try {
|
|
117
|
+
// Discovery is best effort. Browsers may retain WebUSB grants for devices that
|
|
118
|
+
// are offline, busy, or not ready, so one descriptor must not abort the scan.
|
|
119
|
+
const result = await DevicePool.getDevices([descriptor], descriptor.path, {
|
|
120
|
+
// Discovery must actively identify the protocol instead of trusting a caller hint.
|
|
121
|
+
connectProtocol: undefined,
|
|
122
|
+
forceProtocolDetection: true,
|
|
123
|
+
refreshRuntimeState: true,
|
|
124
|
+
});
|
|
125
|
+
for (const device of result.deviceList) {
|
|
126
|
+
const message = toSearchDevice(device);
|
|
127
|
+
if (message) deviceList.push(message);
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const errorCode =
|
|
131
|
+
error && typeof error === 'object' && 'errorCode' in error
|
|
132
|
+
? (error as { errorCode?: unknown }).errorCode
|
|
133
|
+
: undefined;
|
|
134
|
+
Log.debug('Skip unavailable device during search', {
|
|
135
|
+
path: descriptor.path,
|
|
136
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
91
139
|
}
|
|
92
140
|
}
|
|
93
141
|
|
|
94
|
-
return deviceList
|
|
142
|
+
return deviceList;
|
|
95
143
|
}
|
|
96
144
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -322,6 +322,7 @@ const waitForPendingPromise = async (
|
|
|
322
322
|
Log.debug('pre pending call promise before call method, wait for it');
|
|
323
323
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
324
324
|
let timedOut = false;
|
|
325
|
+
let completed = false;
|
|
325
326
|
try {
|
|
326
327
|
await Promise.race([
|
|
327
328
|
pendingPromise,
|
|
@@ -332,11 +333,12 @@ const waitForPendingPromise = async (
|
|
|
332
333
|
}, PRE_PENDING_CALL_TIMEOUT_MS);
|
|
333
334
|
}),
|
|
334
335
|
]);
|
|
336
|
+
completed = !timedOut;
|
|
335
337
|
} finally {
|
|
336
338
|
if (timer) clearTimeout(timer);
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
removePrePendingCallPromise?.(connectId, pendingPromise);
|
|
339
|
+
// Keep a rejected cleanup barrier for the next caller's safety check;
|
|
340
|
+
// only a completed cleanup or a timeout may clear it.
|
|
341
|
+
if (timedOut || completed) removePrePendingCallPromise?.(connectId, pendingPromise);
|
|
340
342
|
}
|
|
341
343
|
if (timedOut) {
|
|
342
344
|
Log.warn('pre pending call promise timed out before call method', { connectId });
|
|
@@ -422,12 +424,21 @@ const onCallDevice = async (
|
|
|
422
424
|
ensureConnected(context, method, connectId, pollingId, method.abortSignal);
|
|
423
425
|
// Discovery may acquire USB endpoints. Finish it before initializing a public
|
|
424
426
|
// request; once registered, that request makes discovery use cached state only.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
427
|
+
if (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)) {
|
|
428
|
+
// Synchronization can keep the connect action queued after the caller is
|
|
429
|
+
// cancelled. Observe that promise so a late device-not-found error does
|
|
430
|
+
// not become an unhandled rejection.
|
|
431
|
+
const connectPromise = context.methodSynchronize(async () => {
|
|
432
|
+
if (method.abortSignal?.aborted) {
|
|
433
|
+
throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
|
|
434
|
+
}
|
|
435
|
+
return connect();
|
|
436
|
+
}, 'webusb-discovery');
|
|
437
|
+
connectPromise.catch(() => undefined);
|
|
438
|
+
device = await requestQueue.waitForTask(task, () => connectPromise);
|
|
439
|
+
} else {
|
|
440
|
+
device = await connect();
|
|
441
|
+
}
|
|
431
442
|
if (method.abortSignal?.aborted) {
|
|
432
443
|
throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
|
|
433
444
|
}
|
|
@@ -1216,6 +1227,7 @@ const ensureConnected = async (
|
|
|
1216
1227
|
const POLL_INTERVAL_TIME = (method.payload && method.payload.pollIntervalTime) || 1000;
|
|
1217
1228
|
const TIME_OUT = (method.payload && method.payload.timeout) || 10000;
|
|
1218
1229
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
1230
|
+
let lastInitializeError: any;
|
|
1219
1231
|
Log.debug(
|
|
1220
1232
|
`EnsureConnected function start, MAX_RETRY_COUNT=${MAX_RETRY_COUNT}, POLL_INTERVAL_TIME=${POLL_INTERVAL_TIME} `
|
|
1221
1233
|
);
|
|
@@ -1256,7 +1268,9 @@ const ensureConnected = async (
|
|
|
1256
1268
|
Log.debug('EnsureConnected function try count: ', tryCount, ' poll interval time: ', time);
|
|
1257
1269
|
try {
|
|
1258
1270
|
await initDeviceList(method);
|
|
1271
|
+
lastInitializeError = undefined;
|
|
1259
1272
|
} catch (error) {
|
|
1273
|
+
lastInitializeError = error;
|
|
1260
1274
|
Log.debug('device list error: ', error);
|
|
1261
1275
|
if (
|
|
1262
1276
|
[
|
|
@@ -1271,15 +1285,7 @@ const ensureConnected = async (
|
|
|
1271
1285
|
}
|
|
1272
1286
|
if (error.errorCode === HardwareErrorCode.TransportNotConfigured) {
|
|
1273
1287
|
await TransportManager.configure();
|
|
1274
|
-
|
|
1275
|
-
const env = DataManager.getSettings('env');
|
|
1276
|
-
if (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)) {
|
|
1277
|
-
// Enumeration succeeded far enough to attempt initialization. Do not
|
|
1278
|
-
// replace its failure with a lookup against the now-empty DeviceList.
|
|
1279
|
-
if (timer) clearTimeout(timer);
|
|
1280
|
-
reject(error);
|
|
1281
|
-
return;
|
|
1282
|
-
}
|
|
1288
|
+
lastInitializeError = undefined;
|
|
1283
1289
|
}
|
|
1284
1290
|
}
|
|
1285
1291
|
|
|
@@ -1377,14 +1383,22 @@ const ensureConnected = async (
|
|
|
1377
1383
|
clearTimeout(timer);
|
|
1378
1384
|
}
|
|
1379
1385
|
Log.debug('EnsureConnected get to max try count, will return: ', tryCount);
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1386
|
+
const preserveWebUsbInitError =
|
|
1387
|
+
DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env);
|
|
1388
|
+
const needsPermissionPrompt =
|
|
1389
|
+
DataManager.isBrowserWebUsb(env) && !method.payload?.skipWebDevicePrompt;
|
|
1390
|
+
const fallbackError = needsPermissionPrompt
|
|
1391
|
+
? ERRORS.TypedError(HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission)
|
|
1392
|
+
: ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
1393
|
+
const errorToReject =
|
|
1394
|
+
preserveWebUsbInitError && lastInitializeError ? lastInitializeError : fallbackError;
|
|
1395
|
+
// Only ask the host for a WebUSB grant when the failure is actually
|
|
1396
|
+
// "not found / needs permission". A preserved initialize error must
|
|
1397
|
+
// not fire that prompt with a different public code.
|
|
1398
|
+
if (needsPermissionPrompt && errorToReject === fallbackError) {
|
|
1383
1399
|
postMessage(createUiMessage(UI_REQUEST.WEB_DEVICE_PROMPT_ACCESS_PERMISSION));
|
|
1384
|
-
reject(ERRORS.TypedError(HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission));
|
|
1385
|
-
} else {
|
|
1386
|
-
reject(ERRORS.TypedError(HardwareErrorCode.DeviceNotFound));
|
|
1387
1400
|
}
|
|
1401
|
+
reject(errorToReject);
|
|
1388
1402
|
return;
|
|
1389
1403
|
}
|
|
1390
1404
|
|
|
@@ -33,6 +33,8 @@ export default class TransportManager {
|
|
|
33
33
|
|
|
34
34
|
static reactNativeInit = false;
|
|
35
35
|
|
|
36
|
+
static webUsbInit = false;
|
|
37
|
+
|
|
36
38
|
static protocolV1MessageSchema: ProtocolV1MessageSchema = 'v1CurrentSchema';
|
|
37
39
|
|
|
38
40
|
static plugin: LowlevelTransportSharedPlugin | null = null;
|
|
@@ -42,6 +44,17 @@ export default class TransportManager {
|
|
|
42
44
|
this.defaultMessages = DataManager.getProtobufMessages();
|
|
43
45
|
this.currentMessages = this.defaultMessages;
|
|
44
46
|
this.protocolV1MessageSchema = 'v1CurrentSchema';
|
|
47
|
+
this.webUsbInit = false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static async ensureInitialized() {
|
|
51
|
+
const env = DataManager.getSettings('env');
|
|
52
|
+
if (env !== 'webusb' && env !== 'desktop-webusb') return;
|
|
53
|
+
if (this.webUsbInit) return;
|
|
54
|
+
// The emitter registers USB disconnect events; this must happen even when
|
|
55
|
+
// schema configuration is intentionally deferred during discovery.
|
|
56
|
+
await this.transport.init(WebUsbLogger, DevicePool.emitter);
|
|
57
|
+
this.webUsbInit = true;
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
static async configure() {
|
|
@@ -69,9 +82,7 @@ export default class TransportManager {
|
|
|
69
82
|
} else if (env === 'desktop-web-ble') {
|
|
70
83
|
await this.transport.init(WebBleLogger, DevicePool.emitter);
|
|
71
84
|
} else if (env === 'webusb' || env === 'desktop-webusb') {
|
|
72
|
-
|
|
73
|
-
// DEVICE.DISCONNECT; without it WebUSB never reports device removal.
|
|
74
|
-
await this.transport.init(WebUsbLogger, DevicePool.emitter);
|
|
85
|
+
await this.ensureInitialized();
|
|
75
86
|
} else {
|
|
76
87
|
await this.transport.init(HttpLogger);
|
|
77
88
|
}
|